Forum: Ruby blk.call vs. yield (possible bug?)

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Dominik B. (Guest)
on 2006-01-02 20:31
(Received via mailing list)
Hello,

I always thought that the following 4 methods should be equivalent if
they
are called with one argument:

def t1(*a)
	yield *a
end

def t2(*a, &blk)
	blk.call(*a)
end

def t3(a)
	yield a
end

def t4(a, &blk)
	blk.call(a)
end


Well they don't seem to be:

$ cat tst.rb
def t1(*a)
         yield *a
end

def t2(*a, &blk)
         blk.call(*a)
end

def t3(a)
         yield a
end

def t4(a, &blk)
         blk.call(a)
end

[:t1, :t2, :t3, :t4].each { |meth|
         puts "================================"
         p send(meth, [1, 2]) { |a,| a }
         p send(meth, [1, 2]) { |a,b| [a, b] }
         p send(meth, [1, 2]) { |a| a }
}
$ ruby -v
ruby 1.8.3 (2005-09-21) [i686-linux]
$ ruby tst.rb
================================
[1, 2]
[[1, 2], nil]
[1, 2]
================================
1
[1, 2]
[1, 2]
================================
1
[1, 2]
[1, 2]
================================
1
[1, 2]
[1, 2]
$ ./ruby-1.9 -v
ruby 1.9.0 (2005-12-28) [i686-linux]
$ ./ruby-1.9 tst.rb
================================
[1, 2]
[[1, 2], nil]
[1, 2]
================================
1
[1, 2]
[1, 2]
================================
1
[1, 2]
[1, 2]
================================
1
[1, 2]
[1, 2]

So there is a difference between using *a and a in the method parameter
list and a difference between using yield and block.call(). I would have
expected

[1, 2]
[[1, 2], nil]
[1, 2]

for all 4 cases. (I checked that send() is not the source of the
problem)

Is this intended this way? Should it be changed?

Interestingly yarv behaves different:

$ ./yarv -v
ruby 1.9.0 (2005-11-18) [i686-linux]
YARVCore 0.3.3 (rev: 319) [opts: ]
$ ./yarv tst.rb
================================
[1, 2]
[1, 2]
[1, 2]
================================
[1, 2]
[[1, 2], nil]
[1, 2]
================================
[1, 2]
[1, 2]
[1, 2]
================================
[1, 2]
[[1, 2], nil]
[1, 2]

Dominik
Dominik B. (Guest)
on 2006-01-04 20:42
(Received via mailing list)
Nobody having an opinion on that?
unknown (Guest)
on 2006-01-04 20:57
(Received via mailing list)
Hi --

On Tue, 3 Jan 2006, Dominik B. wrote:

> 	blk.call(*a)
>
> $ ruby tst.rb
> [1, 2]
> [1, 2]
> ================================
> 1
> [1, 2]
> [1, 2]

I think it has something to do with the Proc/lambda difference.  When
you grab the block into a variable, it becomes a Proc object.
Otherwise it behaves like a lambda:

irb(main):001:0> pr = Proc.new {|a,| a }
=> #<Proc:0x001b92b0@(irb):1>
irb(main):002:0> la = lambda {|a,| a }
=> #<Proc:0x0035b734@(irb):2>
irb(main):003:0> pr.call([1,2])
=> 1
irb(main):004:0> la.call([1,2])
=> [1, 2]

I haven't tried all of them, but I strongly suspect you'll find that
all the behavior you're finding goes back to this.

I find it very hard to remember that there is a difference, to
understand *why* there's a difference, and to remember what the
difference is.  I have to test them each time -- which may mean that
I'm stupid, or it may mean that they're hard to understand and
remember, or something in between.

I'm still hoping for some kind of simplification or unification.
(Maybe the YARV behavior is a sign of things to come?)


David

--
David A. Black
removed_email_address@domain.invalid

"Ruby for Rails", from Manning Publications, coming April 2006!
http://www.manning.com/books/black
Dominik B. (Guest)
on 2006-01-05 00:08
(Received via mailing list)
On Wed, 04 Jan 2006 19:57:08 +0100, <removed_email_address@domain.invalid> 
wrote:

> irb(main):004:0> la.call([1,2])
> => [1, 2]
>
> I haven't tried all of them, but I strongly suspect you'll find that
> all the behavior you're finding goes back to this.

Yes, this is one cause, but I think the real "problem" is the
one-array-on-the-right-side-special-case of multiple assignment:

>> a,b,c = [1,2,3]
=> [1, 2, 3]
>> [a,b,c]
=> [1, 2, 3]

while

>> a,b,c = [1,2,3], 1
=> [[1, 2, 3], 1]
>> [a,b,c]
=> [[1, 2, 3], 1, nil]

I have investigate a bit in the source and I think what happens is that
for "Proc.new {|a,| a }" the multiple assignment is done like:

>> a, = [1,2]; a
=> 1

while for "lambda {|a,| a }" the multiple assignment is done like:

>> a, = *[[1,2]]; a
=> [1, 2]

They would be equivalent, if there wouldn't be the above mentioned
special
case...

Of course this special case is useful, for example for multiple return
values from a method, if it was not there one would have to write "a, b
=
*meth()" instead of "a, b = meth()"...

Well, let's see what the future will bring, I think Matz said something
about simplifying multiple assignment on RubyConf 05.

Dominik
This topic is locked and can not be replied to.