Project

General

Profile

Actions

Bug #6994

closed

yield plus splat unwraps too much

Added by headius (Charles Nutter) over 11 years ago. Updated over 11 years ago.

Status:
Rejected
Assignee:
-
Target version:
-
ruby -v:
2.0.0dev and 1.9.3p253
Backport:
[ruby-core:47453]

Description

I don't see how anyone could argue that 1.9.3 and 2.0.0 are correct here:

system ~/projects/jruby $ ruby-1.8.7-p358 -e "def foo; yield *[1]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]

system ~/projects/jruby $ ruby-1.8.7-p358 -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[[1], nil, []]

system ~/projects/jruby $ ruby-1.8.7-p358 -e "def foo; yield *[[[1]]]; end; foo {|a, b, *c| p [a,b,c]}"
[[[1]], nil, []]

system ~/projects/jruby $ ruby-1.9.3 -e "def foo; yield *[1]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]

system ~/projects/jruby $ ruby-1.9.3 -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]

system ~/projects/jruby $ ruby-1.9.3 -e "def foo; yield *[[[1]]]; end; foo {|a, b, *c| p [a,b,c]}"
[[1], nil, []]

system ~/projects/jruby $ ruby-2.0.0 -e "def foo; yield *[1]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]

system ~/projects/jruby $ ruby-2.0.0 -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]

system ~/projects/jruby $ ruby-2.0.0 -e "def foo; yield *[[[1]]]; end; foo {|a, b, *c| p [a,b,c]}"
[[1], nil, []]

system ~/projects/jruby $ ruby-1.9.3 -v
ruby 1.9.3p253 (2012-07-04 revision 36307) [x86_64-darwin11.4.0]

system ~/projects/jruby $ ruby-2.0.0 -v
ruby 2.0.0dev (2012-08-27 trunk 36833) [x86_64-darwin11.4.0]

JRuby behaves like 1.8 in both 1.8 and 1.9 modes. I would feel pretty dirty fixing it, since I think the 1.9.3/2.0.0 behavior is wrong.

Updated by akr (Akira Tanaka) over 11 years ago

2012/9/8 headius (Charles Nutter) :

Bug #6994: yield plus splat unwraps too much
https://bugs.ruby-lang.org/issues/6994

system ~/projects/jruby $ ruby-1.8.7-p358 -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[[1], nil, []]

This behavior causes "yield *[[1]]" and "yield [1]" is different:

% ruby-1.8 -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[[1], nil, []]
% ruby-1.8 -e "def foo; yield [1]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]
% ruby-1.8 -v
ruby 1.8.8dev (2012-05-21 revision 26840) [x86_64-linux]

So, information passed by yield to a block is not only
a list of values but also something differentiate the above behaviors.

It makes us impossible to pass block arguments as follows:

def test()
m() {|*vals| yield(*vals) }
end

This passes only a list of values from test() to m().
So the information for "something differentiate the above behaviors"
is discarded.

system ~/projects/jruby $ ruby-1.9.3 -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]

Since Ruby 1.9, information passed by yield to a block is only
a list of values.

So "yield *[[1]]" and "yield [1]" behaves same:

% ruby -e "def foo; yield *[[1]]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]
% ruby -e "def foo; yield [1]; end; foo {|a, b, *c| p [a,b,c]}"
[1, nil, []]
% ruby -v
ruby 2.0.0dev (2012-08-15 trunk 36702) [x86_64-linux]

Splat works in callee side. It expands single array.
It cannot distinguish "yield *[[1]]" and "yield [1]".

JRuby behaves like 1.8 in both 1.8 and 1.9 modes. I would feel pretty dirty fixing it, since I think the 1.9.3/2.0.0 behavior is wrong.

I don't think Ruby 1.9 behavior is wrong.

Tanaka Akira

Updated by headius (Charles Nutter) over 11 years ago

What you're saying makes some sense if we treat all multiple-argument blocks as having an implicit () around them, since that makes method argument behavior match:

system ~ $ ruby-1.9.3 -e "def foo; yield *[[1]]; end; foo {|a,b| p a}"
1

system ~ $ ruby-1.9.3 -e "def foo((a, b)); p a; end; foo *[[1]]"
1

But this isn't consistent either, since the single-argument form does not spread arrays:

system ~/projects/jruby $ ruby-1.9.3 -e "def foo; yield *[[1]]; end; foo {|a| p a}"
[1]

system ~/projects/jruby $ ruby-1.9.3 -e "def foo((a)); p a; end; foo *[[1]]"
1

So it spreads an incoming array out when there's more than one receiving argument, but does not spread it out when there's only one receiving argument?

Updated by akr (Akira Tanaka) over 11 years ago

2012/9/9 headius (Charles Nutter) :

But this isn't consistent either, since the single-argument form does not spread arrays:

system ~/projects/jruby $ ruby-1.9.3 -e "def foo; yield *[[1]]; end; foo {|a| p a}"
[1]

system ~/projects/jruby $ ruby-1.9.3 -e "def foo((a)); p a; end; foo *[[1]]"
1

So it spreads an incoming array out when there's more than one receiving argument, but does not spread it out when there's only one receiving argument?

Single array expansion is not always performed.
It depends on the form of the block arguments.

As you said, {|a| } doesn't expand.
If {|a| } expands an array, we cannot get an element of a nested array in
[ary, ary, ...].each {|a| ... }.

Also, {|*a| } doesn't expand.
If {|*a| } expands an array, we cannot pass block arguments as-is
using {|*a| yield *a }.

I wrote about this design in [ruby-dev:38795]. It is written in
Japanese, though.

Tanaka Akira

Actions #5

Updated by headius (Charles Nutter) over 11 years ago

Ok, I accept the explanation. We will support the MRI behavior in JRuby.

Updated by headius (Charles Nutter) over 11 years ago

This can be closed.

Updated by kosaki (Motohiro KOSAKI) over 11 years ago

  • Status changed from Open to Rejected
Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0