oddity with block argument passing

G

Gustav Munkby

In my latest project, I was a bit surprised by the way arguments to
Proc.new were handled. Somehow an array which i passed in was
mysteriously lost and presented as a nil within the block I had
created.

If this is to be expected, I would have liked to see a mention of it
in the documentation. After reading the documentation more carefully,
I realized that if i would use Kernel.proc instead of Proc.new it
should do the same "except the resulting Proc objects check the number
of parameters passed", which evidentially in my case also means that
the parameter actually goes through.

Wondering whether this was a bug or not, I tried out the latest CVS
version to see whether the behavior had changed or not. Indeed the
behavior was changed in the CVS version, but in a direction quite
opposite to what I had hoped for, since now both Proc.new and
Kernel.proc performs in the same way. (Eating up my array argument)

Kernel.lambda seems to work the way I want for all cases though and
thus using this will solve my problems, but I was curious whether this
difference between lambda and proc is intended, and also whether the
change of semantics from current to CVS was intended.

Below is an example which isn't especially meaningful, but serves it's
purpose to highlight the differences between the semantics of the
different approaches in different versions of ruby.

Constructs = [
lambda {|x, *y| [x, y]},
proc {|x, *y| [x, y]},
Proc.new {|x, *y| [x, y]}
]

puts "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
Constructs.each{ |x| puts x[[]].inspect }

Below is the output of this script from the two versions of ruby where
I've tried it.

$ ruby proc_test.rb
ruby 1.8.3 (2005-09-21) [i686-linux]
[[], []]
[[], []]
[nil, []]

$ ruby-cvs proc_test.rb
ruby 1.9.0 (2005-12-20) [i686-linux]
[[], []]
[nil, []]
[nil, []]

Is this to be expected?

!g
 
A

angus

2005-12-20 21.40 CET] said:
Constructs = [
lambda {|x, *y| [x, y]},
proc {|x, *y| [x, y]},
Proc.new {|x, *y| [x, y]}
]

puts "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
Constructs.each{ |x| puts x[[]].inspect }

Below is the output of this script from the two versions of ruby where
I've tried it.

$ ruby proc_test.rb
ruby 1.8.3 (2005-09-21) [i686-linux]
[[], []]
[[], []]
[nil, []]

$ ruby-cvs proc_test.rb
ruby 1.9.0 (2005-12-20) [i686-linux]
[[], []]
[nil, []]
[nil, []]

Compare:

def a x, *y
[x, y]
end

a [] # => [[], []]

x, *y = []

[x, y] # => [nil, []]

In one case, values are assigned as in normal assignment; in others, as in
method parameter assignment. The "normal variable assignment" is to be
expected in normal blocks; the ones created with lambda use parameter-like
assignment.
Is this to be expected?

Many people expect different things here, and you can find a lot of
discussions about this topic in the list archives.

HTH.
 
G

Gustav Munkby

angus said:
2005-12-20 21.40 CET] said:
Constructs = [
lambda {|x, *y| [x, y]},
proc {|x, *y| [x, y]},
Proc.new {|x, *y| [x, y]}
]

puts "ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
Constructs.each{ |x| puts x[[]].inspect }

Below is the output of this script from the two versions of ruby where
I've tried it.

$ ruby proc_test.rb
ruby 1.8.3 (2005-09-21) [i686-linux]
[[], []]
[[], []]
[nil, []]

$ ruby-cvs proc_test.rb
ruby 1.9.0 (2005-12-20) [i686-linux]
[[], []]
[nil, []]
[nil, []]


Compare:

def a x, *y
[x, y]
end

a [] # => [[], []]

x, *y = []

[x, y] # => [nil, []]

In one case, values are assigned as in normal assignment; in others, as in
method parameter assignment. The "normal variable assignment" is to be
expected in normal blocks; the ones created with lambda use parameter-like
assignment.

thanks for this explanation, now i really understand why there's a
difference between the parameter-like assignment and normal variable
assignment.

they way you say it, it sounds as if the difference should be between
lambda and proc, but in 1.8.3 the difference is between Proc.new and
proc, does this mean that there is an issue with Kernel.proc in 1.8.3?

and i would also suggest changing the api-documation, allthough i don't
know exactly i shall proceed to propose such a change. for Kernel.lambda
it currently states:

Equivalent to Proc.new, except the resulting Proc objects check the
number of parameters passed when called.

a better phrasing i think would be:

Equivalent to Proc.new, except the resulting Proc objects have
method-like-parameter-passing instead of the normal
expression-like-parameter-passing.

or something along those lines. personally i would think it'd be nice
with something more explaining the differences of the two, but the
api-documentation might not be the proper place for making this clear.

!g
 
D

dblack

Hi --

thanks for this explanation, now i really understand why there's a difference
between the parameter-like assignment and normal variable assignment.

they way you say it, it sounds as if the difference should be between lambda
and proc, but in 1.8.3 the difference is between Proc.new and proc, does this
mean that there is an issue with Kernel.proc in 1.8.3?

proc and lambda are synonyms. Because it's confusing for proc and
Proc.new to mean different things, Matz has said that proc is (will
be?) deprecated, so it will just be Proc.new and lambda.


David

--
David A. Black
(e-mail address removed)

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
 

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

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,124
Latest member
JuniorPell
Top