Bug #6994

yield plus splat unwraps too much

Added by Charles Nutter over 1 year ago. Updated over 1 year ago.

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

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.

History

#2 Updated by Akira Tanaka over 1 year 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

#3 Updated by Charles Nutter over 1 year 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?

#4 Updated by Akira Tanaka over 1 year 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"
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 . It is written in
Japanese, though.
--
Tanaka Akira

#5 Updated by Charles Nutter over 1 year ago

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

#6 Updated by Charles Nutter over 1 year ago

This can be closed.

#7 Updated by Motohiro KOSAKI over 1 year ago

  • Status changed from Open to Rejected

Also available in: Atom PDF