Blk.call vs. yield (possible bug?)

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
[1, 2]

1
[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
[1, 2]

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

[1, 2]

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

Dominik

Nobody having an opinion on that?

···

On Mon, 02 Jan 2006 19:30:54 +0100, Dominik Bathon <dbatml@gmx.de> wrote:

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
[1, 2]

1
[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
[1, 2]

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

[1, 2]

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

Dominik

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
[1, 2]

1
[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

···

On Tue, 3 Jan 2006, Dominik Bathon wrote:

--
David A. Black
dblack@wobblini.net

"Ruby for Rails", from Manning Publications, coming April 2006!

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.

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

···

On Wed, 04 Jan 2006 19:57:08 +0100, <dblack@wobblini.net> wrote: