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?

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs