Bug in lambda?

K

kevin cline

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
 
W

Wilson Bilkovich

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

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

Logan Capaldo

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

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
 
K

Konrad Meyer

--nextPart1899898.QzmCiarhaQ
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline

This looks like a pretty serious bug. It seems that lambda-
expressions are not properly localizing their formal arguments:
=20
bash-3.2$ ruby --version
ruby 1.8.6 (2007-03-13 patchlevel 0) [i386-mswin32]
=20
bash-3.2$ cat lambda-bug.rb
a =3D 0
f =3D lambda { |a| a }
puts "a =3D #{a}"
puts "f.call(1) =3D> #{f.call(1)}"
puts "now a =3D #{a}"
=20
bash-3.2$ ruby lambda-bug.rb
a =3D 0
f.call(1) =3D> 1
now a =3D 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,
=2D-=20
Konrad Meyer <[email protected]> http://konrad.sobertillnoon.com/

--nextPart1899898.QzmCiarhaQ
Content-Type: application/pgp-signature; name=signature.asc
Content-Description: This is a digitally signed message part.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (GNU/Linux)

iD8DBQBG42spCHB0oCiR2cwRAh39AKCa05rnHzYjy3vN3jxL1EmcJQgZwACfflUt
j9FAxqouw3riaCDpxasqcKQ=
=U/zC
-----END PGP SIGNATURE-----

--nextPart1899898.QzmCiarhaQ--
 
D

dblack

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 :)


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)
 
L

Logan Capaldo

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
 
7

7stud --

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 :)

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

7stud --

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

Whoops. Methods, methods.....
 
7

7stud --

7stud said:
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
 
L

Logan Capaldo

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? ;) 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| }
 
W

Wilson Bilkovich

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}
 
D

dblack

Hi --

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}

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 :)


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)
 
W

Wilson Bilkovich

Hi --

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 :)

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}

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 :)

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. :)

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

dblack

Hi --

Hi --

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 :)


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}

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 :)

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. :)

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 :) 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

--
* 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)
 
W

Wilson Bilkovich

Hi --

Hi --

On Mon, 10 Sep 2007, Wilson Bilkovich 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 :)


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}

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 :)

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. :)

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 :) 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.
 
D

dblack

Hi --

Hi --

Hi --

On Mon, 10 Sep 2007, Wilson Bilkovich 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 :)


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}

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 :)



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. :)

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 :) 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.

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

--
* 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)
 
W

Wilson Bilkovich

Hi --

Hi --

On Mon, 10 Sep 2007, Wilson Bilkovich wrote:

Hi --

On Mon, 10 Sep 2007, Wilson Bilkovich 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 :)


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}

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 :)



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. :)

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 :) 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.

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

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

William James

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

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

Robert Klemme

2007/9/9 said:
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 :)

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
 
D

dblack

Hi --

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

--
* 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)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top