Bug in lambda?

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

On 9/8/07, kevin cline [email protected] wrote:

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

This is the expected behavior of Ruby 1.8.
‘lambda’ and ‘proc’ are expected to diverge further in future versions.

On 9/8/07, kevin cline [email protected] wrote:

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

Not a bug, it’s a “feature”. I don’t like it either.

foo = lambda { |$this_works_too| }

foo.call(2)

p $this_works_too

class SoDoesThis
attr_reader :foo
def setter
lambda { |@foo| }
end
end

bar = SoDoesThis.new
bar.setter[“this is sparta”]
p bar.foo

On Saturday 08 September 2007 08:15:06 pm kevin cline wrote:

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

IIRC this is a ruby 1.8.x thing; it will be fixed in 1.9. Don’t depend
on
this behaviour / try to avoid repeating variable names :D.

Cheers,

In 1.9 it won’t be able to access local scope?

Hi –

On Sun, 9 Sep 2007, kevin cline wrote:

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 9/9/07, Bernardo Monteiro R. [email protected] wrote:

In 1.9 it won’t be able to access local scope?

No, just not the arguments. The following will still work:

a = 1
foo = lambda { a = 99 }
foo.call
p a # prints 99

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?

7stud – wrote:

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

Whoops. Methods, methods…

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

On 9/9/07, [email protected] [email protected] 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:

I think it shouldn’t be changed too…

On 9/9/07, Logan C. [email protected] wrote:

On 9/9/07, Bernardo Monteiro R. [email protected] wrote:

On 9/9/07, [email protected] [email protected] 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. =(

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

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

Hi –

On Mon, 10 Sep 2007, Wilson B. wrote:

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. =(

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

hydra>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

On 9/9/07, Bernardo Monteiro R. [email protected] wrote:

On 9/9/07, [email protected] [email protected] 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:

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, [email protected] [email protected] wrote:

changed :slight_smile:
p h
block parameters :slight_smile:

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.

Hi –

On Mon, 10 Sep 2007, Wilson B. wrote:

that this makes perfect sense, once you learn it, and should not be
f[7]
who think that there’s nothing wrong with assignment semantics for
block parameters :slight_smile:

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.

I’ve never used h[:x] as a block parameter. That is a bit exotic, and
a bit useless :slight_smile: But I think I’ve used an instance variable. Also
it’s handy if, for example, you want to capture the last value passed
during an iteration.

David

On 9/9/07, [email protected] [email protected] wrote:

On 9/9/07, Bernardo Monteiro R. [email protected] wrote:

feature, David. :slight_smile:

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

I’ve never used h[:x] as a block parameter. That is a bit exotic, and
a bit useless :slight_smile: But I think I’ve used an instance variable. Also
it’s handy if, for example, you want to capture the last value passed
during an iteration.

I would be OK-er with this if method arguments obeyed the same rules.
e.g.
def foo=(@foo);end

foo = 5
would then automatically set the instance variable for you.
I think I mostly don’t like it because it is unique to block
arguments, and even then is pretty significantly different in
‘regular’ blocks vs. lambdas.

On 9/9/07, [email protected] [email protected] wrote:

Hi –

it’s handy if, for example, you want to capture the last value passed
I think I mostly don’t like it because it is unique to block

I was referring (poorly) to the fact that lambdas enforce arity but
regular blocks do not.

Hi –

On Mon, 10 Sep 2007, Wilson B. wrote:

On 9/9/07, Logan C. [email protected] wrote:
like a wild animal through the streets of your city. =(
Hey, nothing wrong with that – it’s just like
while ‘yield’ calls are variadic. Please tell me you don’t use this

I would be OK-er with this if method arguments obeyed the same rules.
e.g.
def foo=(@foo);end

foo = 5
would then automatically set the instance variable for you.
I think I mostly don’t like it because it is unique to block
arguments, and even then is pretty significantly different in
‘regular’ blocks vs. lambdas.

It’s definitely different from method-param semantics. It’s never
bothered me, though – it’s just a different decision about how block
params will work.

I’m not sure what you mean when you say different between regular
blocks and lambdas – ?

David

On Sep 9, 3:28 am, [email protected] wrote:

a = 0
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

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