Bug in lambda?

Hi --

This looks like a pretty serious bug. It seems that lambda-
expressions are not properly localizing their formal arguments:

bash-3.2$ ruby --version
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

bash-3.2$ cat lambda-bug.rb
a = 0
f = lambda { |a| a }
puts "a = #{a}"
puts "f.call(1) => #{f.call(1)}"
puts "now a = #{a}"

bash-3.2$ ruby lambda-bug.rb
a = 0
f.call(1) => 1
now a = 1

Definitely not a bug. Block parameters use assignment semantics, with
regard to the scope in which the block appears. In your example,
you're assigning 1 to a. If you create a local variable inside the
block, however, it only exists for the duration of the block.

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

David

···

On Sun, 9 Sep 2007, kevin cline wrote:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

This looks like a pretty serious bug.

I guess you didn't read p. 51 of "Programming Ruby (2nd Ed)". You can
read the first edition online. See the section Implementing Iterators
here:

http://www.rubycentral.com/pickaxe/tut_containers.html

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

The behavior in question seems consistent with the way closures work in
Ruby:

def func(x)
    y = 10
    lambda {puts x+y; y += 1}
end

p = func(5)
p.call
p.call
p.call

--output:--
15
16
17

Is there something inconsistent with the way a block defined outside of
any functions behaves versus a closure?

···

--
Posted via http://www.ruby-forum.com/\.

I think it shouldn't be changed too...

···

On 9/9/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

--
Bernardo Rufino

Does this mean that you and Guy and Matz smoked
the same wacky tobaccy?

···

On Sep 9, 3:28 am, dbl...@wobblini.net wrote:

Hi --

On Sun, 9 Sep 2007, kevin cline wrote:
> This looks like a pretty serious bug. It seems that lambda-
> expressions are not properly localizing their formal arguments:

> bash-3.2$ ruby --version
> ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]

> bash-3.2$ cat lambda-bug.rb
> a = 0
> f = lambda { |a| a }
> puts "a = #{a}"
> puts "f.call(1) => #{f.call(1)}"
> puts "now a = #{a}"

> bash-3.2$ ruby lambda-bug.rb
> a = 0
> f.call(1) => 1
> now a = 1

Definitely not a bug. Block parameters use assignment semantics, with
regard to the scope in which the block appears. In your example,
you're assigning 1 to a. If you create a local variable inside the
block, however, it only exists for the duration of the block.

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense

Now I'm curious: how often do you have use for this feature? I mean,
if block parameters were locally scoped you just had to punch in one
more assignment to get the same effect like the current version:

def foo
  x=1
  lambda {|a| x=a}
end

Do I miss something?

Kind regards

robert

···

2007/9/9, dblack@wobblini.net <dblack@wobblini.net>:

Definitely not a bug. Block parameters use assignment semantics, with
regard to the scope in which the block appears. In your example,
you're assigning 1 to a. If you create a local variable inside the
block, however, it only exists for the duration of the block.

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

7stud -- wrote:

Is there something inconsistent with the way a block defined outside of
any functions behaves versus a closure?

Whoops. Methods, methods.....

···

--
Posted via http://www.ruby-forum.com/\.

7stud -- wrote:

The behavior in question seems consistent with the way closures work in
Ruby:

def func(x)
    y = 10
    lambda {puts x+y; y += 1}
end

p = func(5)
p.call
p.call
p.call

--output:--
15
16
17

Is there something inconsistent with the way a block defined outside of
any functions behaves versus a closure?

I guess this would be a more complete example:

$z = 100

def func(x)
    y = 10

    lambda {
        puts x+y+$z
        x+=1
        y += 1
        $z+=100
    }
end

p = func(5)
p.call
p.call
p.call

--output:--
115
217
319

···

--
Posted via http://www.ruby-forum.com/\.

> As far as remember, Guy Decoux and I are the only two people who think
> that this makes perfect sense, once you learn it, and should not be
> changed :slight_smile:
>

I think it shouldn't be changed too...

I just think it has icky semantics.
a.rb:

fib = lambda do |a|
  if a == 1 or a == 0
    1
  else
    fib[a - 1] + fib[a - 2]
  end
end

p fib.call(10)

C:\Documents and Settings\Logan>ruby a.rb
89

vs.

b.rb:
a = nil
fib = lambda do |a|
  if a == 1 or a == 0
    1
  else
    fib[a - 1] + fib[a - 2]
  end
end

p fib.call(10)

C:\Documents and Settings\Logan>ruby b.rb
b.rb:6: stack level too deep (SystemStackError)
        from b.rb:6
        from b.rb:6
        from b.rb:6
        from b.rb:6
        from b.rb:6
        from b.rb:6
        from b.rb:6
        from b.rb:6
         ... 633 levels...
        from b.rb:6
        from b.rb:6
        from b.rb:6
        from b.rb:10

Am I the only one who makes use of recursive lambdas? :wink: If we keep
assignment semantics for block arguments, I think we should disallow
recursive blocks. I don't want recursive blocks to go away personally.
I think it's reasonable to give block arguments argument semantics
(especially in light of the addition of &block arguments to blocks)
instead of assignment semantics. You don't lose any power, although
you do lose some "expressivity", eg:

a = nil
set_a = lambda { |b| a = b }

instead of
a = nil
set_a = lambda { |a| }

···

On 9/9/07, Bernardo Monteiro Rufino <bermonruf@gmail.com> wrote:

On 9/9/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

--
Bernardo Rufino

Hi --

Definitely not a bug. Block parameters use assignment semantics, with
regard to the scope in which the block appears. In your example,
you're assigning 1 to a. If you create a local variable inside the
block, however, it only exists for the duration of the block.

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

Now I'm curious: how often do you have use for this feature? I mean,
if block parameters were locally scoped you just had to punch in one
more assignment to get the same effect like the current version:

def foo
x=1
lambda {|a| x=a}
end

Do I miss something?

I don't think so, but part of the problem for me is that there's no
problem -- in other words, I learned that it works with assignment
syntax, and probably haven't made all that much use of it but never
thought there was any reason for it *not* to work that way. I suppose
it's possible to clobber variables, but then it's possible to do that
without blocks, too, if you're not careful about variable names.

David

···

On Mon, 10 Sep 2007, Robert Klemme wrote:

2007/9/9, dblack@wobblini.net <dblack@wobblini.net>:

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

>
> > As far as remember, Guy Decoux and I are the only two people who think
> > that this makes perfect sense, once you learn it, and should not be
> > changed :slight_smile:
> >

This one is my favorite.
P.S. Don't use this in 'real' code or I will be forced to hunt you
like a wild animal through the streets of your city. =(

cat eye_of_terror.rb

h = {}
f = lambda {|h[:x]| }
f[7]
p h

ruby eye_of_terror.rb

{:x=>7}

···

On 9/9/07, Logan Capaldo <logancapaldo@gmail.com> wrote:

On 9/9/07, Bernardo Monteiro Rufino <bermonruf@gmail.com> wrote:
> On 9/9/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

I think that the original poster was worried that this script would
print "7". If that's not the case, forgive me. :slight_smile:

def do_it x, f
  f.call 7
  p x
end

f = lambda {|x| x}
do_it 1, f

The existing behavior seems much scarier in IRB than in 'real' code.

···

On 9/10/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

Hi --

On Mon, 10 Sep 2007, Robert Klemme wrote:

> 2007/9/9, dblack@wobblini.net <dblack@wobblini.net>:
>> Definitely not a bug. Block parameters use assignment semantics, with
>> regard to the scope in which the block appears. In your example,
>> you're assigning 1 to a. If you create a local variable inside the
>> block, however, it only exists for the duration of the block.
>>
>> As far as remember, Guy Decoux and I are the only two people who think
>> that this makes perfect sense, once you learn it, and should not be
>> changed :slight_smile:
>
> Now I'm curious: how often do you have use for this feature? I mean,
> if block parameters were locally scoped you just had to punch in one
> more assignment to get the same effect like the current version:
>
> def foo
> x=1
> lambda {|a| x=a}
> end
>
> Do I miss something?

I don't think so, but part of the problem for me is that there's no
problem -- in other words, I learned that it works with assignment
syntax, and probably haven't made all that much use of it but never
thought there was any reason for it *not* to work that way. I suppose
it's possible to clobber variables, but then it's possible to do that
without blocks, too, if you're not careful about variable names.

Ah, ok. I suspected you had some cool usage for this which I overlooked and could insert into my repertoire. :slight_smile:

As you say, there are many ways to shoot yourself in the foot - block parameters are just one of them.

Kind regards

  robert

···

On 10.09.2007 16:52, dblack@wobblini.net wrote:

Hi --

On Mon, 10 Sep 2007, Robert Klemme wrote:

2007/9/9, dblack@wobblini.net <dblack@wobblini.net>:

Definitely not a bug. Block parameters use assignment semantics, with
regard to the scope in which the block appears. In your example,
you're assigning 1 to a. If you create a local variable inside the
block, however, it only exists for the duration of the block.

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

Now I'm curious: how often do you have use for this feature? I mean,
if block parameters were locally scoped you just had to punch in one
more assignment to get the same effect like the current version:

def foo
x=1
lambda {|a| x=a}
end

Do I miss something?

I don't think so, but part of the problem for me is that there's no
problem -- in other words, I learned that it works with assignment
syntax, and probably haven't made all that much use of it but never
thought there was any reason for it *not* to work that way. I suppose
it's possible to clobber variables, but then it's possible to do that
without blocks, too, if you're not careful about variable names.

Hi --

···

On Mon, 10 Sep 2007, Wilson Bilkovich wrote:

On 9/9/07, Logan Capaldo <logancapaldo@gmail.com> wrote:

On 9/9/07, Bernardo Monteiro Rufino <bermonruf@gmail.com> wrote:

On 9/9/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

This one is my favorite.
P.S. Don't use this in 'real' code or I will be forced to hunt you
like a wild animal through the streets of your city. =(

>cat eye_of_terror.rb
h = {}
f = lambda {|h[:x]| }
f[7]
p h

>ruby eye_of_terror.rb
{:x=>7}

Hey, nothing wrong with that -- it's just like

   h[:x] = 7

but in slightly different form. But like I said, I'm one of the few
who think that there's nothing wrong with assignment semantics for
block parameters :slight_smile:

David

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

What output would you prefer? I don't see how this can print "7" if that's what you were up to. In fact, I would be scared to see that. :slight_smile:

Actually the original issue was different IIRC, because the block was defined in a scope which had the block parameter as a local variable which is not the case in your example.

Kind regards

  robert

···

On 10.09.2007 19:05, Wilson Bilkovich wrote:

On 9/10/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

Hi --

On Mon, 10 Sep 2007, Robert Klemme wrote:

2007/9/9, dblack@wobblini.net <dblack@wobblini.net>:

Definitely not a bug. Block parameters use assignment semantics, with
regard to the scope in which the block appears. In your example,
you're assigning 1 to a. If you create a local variable inside the
block, however, it only exists for the duration of the block.

As far as remember, Guy Decoux and I are the only two people who think
that this makes perfect sense, once you learn it, and should not be
changed :slight_smile:

Now I'm curious: how often do you have use for this feature? I mean,
if block parameters were locally scoped you just had to punch in one
more assignment to get the same effect like the current version:

def foo
x=1
lambda {|a| x=a}
end

Do I miss something?

I don't think so, but part of the problem for me is that there's no
problem -- in other words, I learned that it works with assignment
syntax, and probably haven't made all that much use of it but never
thought there was any reason for it *not* to work that way. I suppose
it's possible to clobber variables, but then it's possible to do that
without blocks, too, if you're not careful about variable names.

I think that the original poster was worried that this script would
print "7". If that's not the case, forgive me. :slight_smile:

def do_it x, f
  f.call 7
  p x
end

f = lambda {|x| x}
do_it 1, f

I note no one has addressed my concern. I feel like there's an
elephant in the room that only I can see :slight_smile:

# > I don't think so, but part of the problem for me is that there's no
# > problem -- in other words, I learned that it works with assignment
# > syntax, and probably haven't made all that much use of it but never
# > thought there was any reason for it *not* to work that way.
# I suppose
# > it's possible to clobber variables, but then it's possible
# to do that
# > without blocks, too, if you're not careful about variable names.

···

From: Robert Klemme [mailto:shortcutter@googlemail.com]
# On 10.09.2007 16:52, dblack@wobblini.net wrote:
#
# Ah, ok. I suspected you had some cool usage for this which I
# overlooked and could insert into my repertoire. :slight_smile:

irb(main):001:0> require 'with_index'
=> true
irb(main):002:0> a=[1,2,3,4]
=> [1, 2, 3, 4]
irb(main):007:0> i=nil
=> nil
irb(main):008:0> a[i]+=1 until a.all_w_index?{|x,i| x>3}
=> nil
irb(main):009:0> a
=> [4, 4, 4, 4]

note, that i modified #all? so i will know where the trigger was
same also with #any?..

irb(main):010:0> a=[1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):011:0> a[i]+=1 while a.any_w_index?{|x,i| x<4}
=> nil
irb(main):012:0> a
=> [4, 4, 4, 4, 5]

but you can do same w scratch vars
though w an added assignment, i find it clearer..

irb(main):029:0> a=[1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):030:0> even=nil
=> nil
irb(main):031:0> a[even]+=1 while a.any_w_index?{|x,i| even=i; x%2==0}
=> nil
irb(main):032:0> a
=> [1, 3, 3, 5, 5]
irb(main):033:0> odd=nil
=> nil
irb(main):034:0> a[odd]+=1 while a.any_w_index?{|x,i| odd=i; x%2!=0}
=> nil
irb(main):035:0> a
=> [2, 4, 4, 6, 6]

the idioms would be likened to,

   part = nil
   machine.repair(part) while machine.any?{|part| part.broken?}

and

   broken = nil
   machine.repair(broken) while
         machine.any?{|part| (broken=part).broken?}

   so that is,

      "repair part while there is any part broken"
   vs
      "repair broken part while there is any part broken"

   your choice.

# As you say, there are many ways to shoot yourself in the foot - block
# parameters are just one of them.

Arggh, the scary part is you wont even know if you've been shot until you're dead --too late. I myself do not like block args (or any args for that matter) clobbering local, global, and instance vars. Surely, it is not the intent of the programmer to clobber them, nor is it an intuitive intent to peek on arg values from the outside (is that being obj-oriented?). I clobber what i clobber. When coding an arg var, surely i do not need to bother and question myself "will this var clobber other vars?". Ruby should help me on that, not put fear on it. I wonder what is the original intent of matz when he designed that feature. It is scary. I've been hit by it. And now i have to be careful about naming my vars. Is shadowing arg vars really used much in practice? It is not a fun part of ruby if you ask me. or maybe, it is just too much fun for me. forgive the non-ruby-hacker, pls :frowning:

kind regards -botp

I agree, except for the fact that assignments are relatively static,
while 'yield' calls are variadic. Please tell me you don't use this
feature, David. :slight_smile:

Hash#each do {|h[:x]| ... } is.. pretty strange.

···

On 9/9/07, dblack@wobblini.net <dblack@wobblini.net> wrote:

Hi --

On Mon, 10 Sep 2007, Wilson Bilkovich wrote:

> On 9/9/07, Logan Capaldo <logancapaldo@gmail.com> wrote:
>> On 9/9/07, Bernardo Monteiro Rufino <bermonruf@gmail.com> wrote:
>>> On 9/9/07, dblack@wobblini.net <dblack@wobblini.net> wrote:
>>>
>>>> As far as remember, Guy Decoux and I are the only two people who think
>>>> that this makes perfect sense, once you learn it, and should not be
>>>> changed :slight_smile:
>>>>
>
> This one is my favorite.
> P.S. Don't use this in 'real' code or I will be forced to hunt you
> like a wild animal through the streets of your city. =(
>
> >cat eye_of_terror.rb
> h = {}
> f = lambda {|h[:x]| }
> f[7]
> p h
>
> >ruby eye_of_terror.rb
> {:x=>7}

Hey, nothing wrong with that -- it's just like

   h[:x] = 7

but in slightly different form. But like I said, I'm one of the few
who think that there's nothing wrong with assignment semantics for
block parameters :slight_smile:

Hi --

···

On Tue, 11 Sep 2007, Logan Capaldo wrote:

I note no one has addressed my concern. I feel like there's an
elephant in the room that only I can see :slight_smile:

Do you mean the thing about recursive blocks?

David

--
* Books:
   RAILS ROUTING (new! http://www.awprofessional.com/title/0321509242\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

I only see an otter. :slight_smile: I can see how block parameter's scoping rules
creates the issue you demonstrated but IMHO the problem is not limited
to block parameters. As David said already, you will shoot yourself in
the foot if you do not take care when naming variables. I do agree
though that the current block parameter semantics make the situation
worse although I would not go as far as to forbid recursive block
invocations. Still, I think it is a good idea that this is being
worked on.

Kind regards

robert

···

2007/9/11, Logan Capaldo <logancapaldo@gmail.com>:

I note no one has addressed my concern. I feel like there's an
elephant in the room that only I can see :slight_smile:

# Actually the original issue was different IIRC, because the block was
# defined in a scope which had the block parameter as a local variable
# which is not the case in your example.

nonetheless, it is still dangerous on procs/lambda
these block arg var creeping scope just adds to the confusion and danger

simple example (got this fr avi's post),

botp@pc4all:~$ irb
irb(main):001:0>
irb(main):002:0* x="this is a test"
=> "this is a test"
irb(main):003:0> fac = lambda{|x| if x == 0 then 1 else fac.call(x - 1) * x end}
=> #<Proc:0xb7dcda90@(irb):3>
irb(main):004:0> puts fac.call(3)
0
=> nil
irb(main):005:0> quit
botp@pc4all:~$ irb
irb(main):001:0> fac = lambda{|x| if x == 0 then 1 else fac.call(x - 1) * x end}
=> #<Proc:0xb7d6542c@(irb):1>
irb(main):002:0> puts fac.call(3)
6
=> nil

kind regards -botp

···

From: Robert Klemme [mailto:shortcutter@googlemail.com]