blk.call vs. yield (possible bug?)

D

Dominik Bathon

Hello,

I always thought that the following 4 methods should be equivalent if the=
y =20
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 "=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D"
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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
$ ./ruby-1.9 -v
ruby 1.9.0 (2005-12-28) [i686-linux]
$ ./ruby-1.9 tst.rb
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]

So there is a difference between using *a and a in the method parameter =20
list and a difference between using yield and block.call(). I would have =
=20
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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]

Dominik
 
D

Dominik Bathon

Nobody having an opinion on that?

Hello,

I always thought that the following 4 methods should be equivalent if =20
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 "=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D"
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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
$ ./ruby-1.9 -v
ruby 1.9.0 (2005-12-28) [i686-linux]
$ ./ruby-1.9 tst.rb
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
1
[1, 2]
[1, 2]

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

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

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

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
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[1, 2]
[1, 2]
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= =3D=3D=3D=3D=3D=3D=3D=3D
[1, 2]
[[1, 2], nil]
[1, 2]

Dominik
 
D

dblack

Hi --

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:

[: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]

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top