Bug #6994
closedyield plus splat unwraps too much
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 headius (Charles Nutter) over 11 years ago
Related JRuby bug: http://jira.codehaus.org/browse/JRUBY-6499
Updated by akr (Akira Tanaka) over 11 years ago
2012/9/8 headius (Charles Nutter) headius@headius.com:
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) headius@headius.com:
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]]"
1So 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
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