Project

General

Profile

Bug #2422

splat operator fails on array of 1 element

Added by raulparolari (Raul Parolari) over 9 years ago. Updated almost 8 years ago.

Status:
Rejected
Priority:
Normal
ruby -v:
1.9.1
[ruby-core:27003]

Description

=begin
In Ruby 1.9.1:

a, b = *[ 137, 271 ] # a = 137; b = 271 # (as expected)

a = *[ 137 ] # a = [137 ] # (should be 137 !)

Definition of splat (Matz & Flanagan book, p.98): "the array elements replace the array in the original rvalue".
Works as defined in Ruby 1.8; fails in 1.9.1
=end

History

#1

Updated by ujihisa (Tatsuhiro Ujihisa) over 9 years ago

  • Status changed from Open to Assigned
  • Assignee set to matz (Yukihiro Matsumoto)

=begin

=end

#2

Updated by daz (Dave B) over 9 years ago

=begin
Tanaka Akira (on ruby-core) wrote:

On Thu, Dec 3, 2009 at 2:35 AM, Raul Parolari redmine@ruby-lang.org wrote:

Bug #2422: splat operator fails on array of 1 element
http://redmine.ruby-lang.org/issues/show/2422

In Ruby 1.9.1:

a, b = *[ 137, 271 ] # a = 137; b = 271 # (as expected)

a = *[ 137 ] # a = [137 ] # (should be 137 !)

The Ruby 1.9 behavior is simplified and more consistent.

% ruby -ve '
a = *[1,2,3]; p a
a = *[1,2]; p a
a = *[1]; p a
a = *[]; p a'
ruby 1.9.2dev (2009-09-24 trunk 25067) [i686-linux]
[1, 2, 3]
[1, 2]
[1]
[]

% ruby-1.8 -ve '
a = *[1,2,3]; p a
a = *[1,2]; p a
a = *[1]; p a
a = *[]; p a'
ruby 1.8.8dev (2009-10-15 revision 25343) [i686-linux]
[1, 2, 3]
[1, 2]
1
nil

Ruby 1.9 doesn't handle zero or one element array specially.

But Raul's example reveals inconsistency from 1.9 for a balanced LHS / RHS ...

puts 'ruby %s (%s) [%s]' % [RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PLATFORM]

a,b,c,d = *[1,2,3,4]
p [a,b,c,d]

a,b,c = *[1,2,3]
p [a,b,c]

a,b = *[1,2]
p [a,b]

a = *[1]
p [a]

=begin (output)

ruby 1.8.6 (2008-08-11) [i386-mswin32]
[1, 2, 3, 4]
[1, 2, 3]
[1, 2]
[1]

ruby 1.9.2 (2009-05-19) [i386-mswin32_90]
[1, 2, 3, 4]
[1, 2, 3]
[1, 2]
[[1]]

=end

a = *1 results in a = [1]

--
daz

=end

#3

Updated by katz (katz bo) over 9 years ago

=begin
I personally think *[1, 2, 3] should be always unpacked first.

a = *[1, 2, 3] #(unpack Array due to *) should be equal to:
a = 1, 2, 3 #(as Ruby is very smart) is equal to: (this is really the tricky part)
*a = 1, 2, 3 #which packs 1, 2, 3 into Array a. Hence is equal to
a = [1, 2, 3]

a = *[1] #(unpack Array due to *) should be equal to:
a = 1 #(again, Ruby is smart it won't read this as *a = 1)

a, b = *[1, 2] #the Array gets unpacked first, so we get:
a, b = 1, 2 #this behaves identical to the next line:
a, b = [1, 2] #Hence a = 1; b = 2

If my understanding is correct, then the 1.9 behavior is a bug(TM).

I'm a noob so don't expect me to know what I'm talking about :D.
=end

#4

Updated by daz (Dave B) over 9 years ago

=begin

Tanaka Akira wrote:

2009/12/21 katz bo redmine@ruby-lang.org:

I personally think *[1, 2, 3] should be always unpacked first.

a = *[1, 2, 3] #(unpack Array due to *) should be equal to:
a = 1, 2, 3 #(as Ruby is very smart) is equal to: (this is really the tricky part)
*a = 1, 2, 3 #which packs 1, 2, 3 into Array a. Hence is equal to
a = [1, 2, 3]

a = *[1] #(unpack Array due to *) should be equal to:
a = 1 #(again, Ruby is smart it won't read this as *a = 1)

Your way cannot interpret a = *[].

a = *[]
a =

It is not a valid Ruby statement.

I would expect to see the value of the first element in the empty array
as 1.8.6 provides ...

puts 'ruby %s (%s) [%s]' % [RUBY_VERSION, RUBY_RELEASE_DATE, RUBY_PLATFORM]

a = *[]
p a

a = [][0]
p a

# ruby 1.8.6 (2008-08-11) [i386-mswin32]
# nil
# nil

# ruby 1.9.2 (2009-05-19) [i386-mswin32_90]
# []
# nil

I understood your last reply to me about "a, =" in multiple assignment
with a single RHS but, to me, "a = 1" is multiple assignment with
balanced (single) LHS/RHS.

Producing an empty array from a splatted empty array seems unproductive. ;)

daz

=end

#5

Updated by katz (katz bo) over 9 years ago

=begin
Dave B wrote:

Producing an empty array from a splatted empty array seems unproductive.
Agreed.

Tanaka Akira wrote:

2009/12/21 katz bo redmine@ruby-lang.org:

I personally think *[1, 2, 3] should be always unpacked first.

Your way cannot interpret a = *[].

a = *[]
a =

It is not a valid Ruby statement.

Why not think of this as a special case?
There's always something special about zeros and nils in my shallow opinion. :P

In fact, a = *[] should exactly be a = IMHO. And strictly speaking, a = *[] should not be a valid syntax at all.
But, this is Ruby!

What does Ruby do with a = *[] ? Let me illustrate an IMO proper way:
Ruby looks at *[] and goes, "Fine, let me unpack it."
Ruby unpacks *[].
Then Ruby goes, "Nothing? But I can't carry ! No.. not in an assignment! Wait.. Let's do it!"
Ruby marks it as nil.

One of the reasons why I think this special case should be handled this way, is the way Ruby handles parallel assignments:
a, b = [1] # => [1]
p a # => 1
p b # => nil
This is not the case, for example, in python. (It spits "ValueError: need more than 1 value to unpack" when you do a, b = [1]).
Since Ruby auto-pads the out-of-range elements as nil, we have a good reason to make Ruby automatically handle a = *[] in this way.

I also have a vague observation that the splatted array is actually dealt differently by the internal Ruby in some cases. Assignment is such a case.

# COMMAND # OUTPUT # RETURN # NOTES
p # # => nil
p [] # # => nil # Fits very well in my "unpack first" theory
p nil # nil # => nil
a = *[]; p a # [] # => [] # ruby 1.9; should both be nil IMO
a =
[]; p a # [] # => [] # ruby 1.9; should both be nil IMO; the space between * and [ is allowed!

Additionally, I find `return' fails my expectations in a similar way assignment does:

def foo; return []; end; foo # => [] # ruby 1.9; I have doubt: nil or []?
def bar; return *[1]; end; bar # => [1] # ruby 1.9; I was expecting 1
def baz; return *[],
[1]; end; baz # => [1]
def ban; return [1],[2]; end; ban # => [1, 2]
def boo; return *[1,2]; end; boo # => [1, 2]

We know that return packs multiple parameters into an array. This, combined with my "unpack first" theory, makes me think the first two results are not correct. But I don't know. Can't test it with ruby 1.8 since I don't have it installed.

Can somebody post the result to the code above for ruby 1.8?

=end

#6

Updated by Eregon (Benoit Daloze) over 9 years ago

=begin
2009/12/21 katz bo redmine@ruby-lang.org

Issue #2422 has been updated by katz bo.

Dave B wrote:

Producing an empty array from a splatted empty array seems unproductive.
Agreed.

Tanaka Akira wrote:

2009/12/21 katz bo redmine@ruby-lang.org:

I personally think *[1, 2, 3] should be always unpacked first.

Your way cannot interpret a = *[].

a = *[]
a =

It is not a valid Ruby statement.

Why not think of this as a special case?
There's always something special about zeros and nils in my shallow
opinion. :P

In fact, a = *[] should exactly be a = IMHO. And strictly speaking, a =
*[] should not be a valid syntax at all.
But, this is Ruby!

What does Ruby do with a = *[] ? Let me illustrate an IMO proper way:
Ruby looks at *[] and goes, "Fine, let me unpack it."
Ruby unpacks *[].
Then Ruby goes, "Nothing? But I can't carry ! No.. not in an
assignment! Wait.. Let's do it!"
Ruby marks it as nil.

One of the reasons why I think this special case should be handled this
way, is the way Ruby handles parallel assignments:
a, b = [1] # => [1]
p a # => 1
p b # => nil
This is not the case, for example, in python. (It spits "ValueError: need
more than 1 value to unpack" when you do a, b = [1]).
Since Ruby auto-pads the out-of-range elements as nil, we have a good
reason to make Ruby automatically handle a = *[] in this way.

I also have a vague observation that the splatted array is actually dealt
differently by the internal Ruby in some cases. Assignment is such a case.

COMMAND # OUTPUT # RETURN # NOTES

p # # => nil
p [] # # => nil # Fits very well in my "unpack first" theory
p nil # nil # => nil
a = *[]; p a # [] # => [] # ruby 1.9; should both be nil IMO
a =
[]; p a # [] # => [] # ruby 1.9; should both be nil IMO; the
space between * and [ is allowed!

Additionally, I find `return' fails my expectations in a similar way
assignment does:

def foo; return []; end; foo # => [] # ruby 1.9; I have doubt: nil
or []?
def bar; return *[1]; end; bar # => [1] # ruby 1.9; I was expecting 1
def baz; return *[],
[1]; end; baz # => [1]
def ban; return [1],[2]; end; ban # => [1, 2]
def boo; return *[1,2]; end; boo # => [1, 2]

We know that return packs multiple parameters into an array. This, combined
with my "unpack first" theory, makes me think the first two results are not
correct. But I don't know. Can't test it with ruby 1.8 since I don't have it
installed.

Can somebody post the result to the code above for ruby 1.8?

Here it is:
COMMAND # 1.9 vs 1.8

p # => nil
p [] # => nil
p nil # => nil
a = *[]; p a # => [] vs nil
a =
[]; p a # => [] vs nil

These 2 last look very weird.


def foo; return *[]; end; foo # => [] vs nil
def bar; return *[1]; end; bar # => [1] vs 1

def baz; return [],[1]; end; baz # => [1] vs SyntaxError: compile error
(
(irb):4: syntax error, unexpected tSTAR, expecting tAMPER
def baz; return [],[1]; end; baz
^
(irb):4: syntax error, unexpected ';', expecting tCOLON2 or '[' or '.'
def baz; return [],[1]; end; baz
^
)

def ban; return [1],[2]; end; ban # => [1, 2] vs SyntaxError: compile
error
(
(irb):4: syntax error, unexpected tSTAR, expecting tAMPER
def ban; return [1],[2]; end; ban # => [1, 2]
^
(irb):4: syntax error, unexpected ';', expecting tCOLON2 or '[' or '.'
def ban; return [1],[2]; end; ban # => [1, 2]
^
)

def boo; return *[1,2]; end; boo # => [1, 2] vs [1, 2]

So here we clearly see how 1.9 is more 'safe' ...

2009/12/21 katz bo <redmine@ruby-lang.org>
Issue #2422 has been updated by katz bo.


Dave B wrote:
>Producing an empty array from a splatted empty array seems unproductive.
Agreed.

Tanaka Akira wrote:
> 2009/12/21 katz bo <redmine@ruby-lang.org>:
>> I personally think [1, 2, 3] should be always unpacked first.
>
> Your way cannot interpret a = *[].
>
> a = *[]
> a =
>
> It is not a valid Ruby statement.

Why not think of this as a special case?
There's always something special about zeros and nils in my shallow opinion. :P

In fact, a = *[] should exactly be a = IMHO. And strictly speaking, a = *[] should not be a valid syntax at all.
But, this is Ruby!

What does Ruby do with a = *[] ? Let me illustrate an IMO proper way:
Ruby looks at *[] and goes, "Fine, let me unpack it."
Ruby unpacks *[].
Then Ruby goes, "Nothing? But I can't carry <nothing>! No.. not in an assignment! Wait.. Let's do it!"
Ruby marks it as nil.

One of the reasons why I think this special case should be handled this way, is the way Ruby handles parallel assignments:
a, b = [1] # => [1]
p a # => 1
p b # => nil
This is not the case, for example, in python. (It spits "ValueError: need more than 1 value to unpack" when you do a, b = [1]).
Since Ruby auto-pads the out-of-range elements as nil, we have a good reason to make Ruby automatically handle a = *[] in this way.

I also have a vague observation that the splatted array is actually dealt differently by the internal Ruby in some cases. Assignment is such a case.

# COMMAND    # OUTPUT # RETURN # NOTES
p            #        # => nil
p *[]        #        # => nil # Fits very well in my "unpack first" theory
p nil        # nil    # => nil
a = *[]; p a # []     # => []  # ruby 1.9; should both be nil IMO
a =
[]; p a # []     # => []  # ruby 1.9; should both be nil IMO; the space between * and [ is allowed!

Additionally, I find `return' fails my expectations in a similar way assignment does:

def foo; return []; end;       foo # => []  # ruby 1.9; I have doubt: nil or []?
def bar; return *[1]; end;      bar # => [1] # ruby 1.9; I was expecting 1
def baz; return *[],
[1]; end;  baz # => [1]
def ban; return [1],[2]; end; ban # => [1, 2]
def boo; return *[1,2]; end;    boo # => [1, 2]

We know that return packs multiple parameters into an array. This, combined with my "unpack first" theory, makes me think the first two results are not correct. But I don't know. Can't test it with ruby 1.8 since I don't have it installed.

Can somebody post the result to the code above for ruby 1.8?
Here it is:COMMAND # 1.9 vs 1.8

p                # => nil
p []            # => nil
p nil            # => nil
a = *[]; p a # => [] vs nil
a =
[]; p a # => []  vs nilThese 2 last look very weird.---------def foo; return []; end; foo # => [] vs nil
def bar; return *[1]; end; bar # => [1] vs 1def baz; return *[],
[1]; end;  baz # => [1] vs SyntaxError: compile error
(       (irb):4: syntax error, unexpected tSTAR, expecting tAMPER    def baz; return [],[1]; end;  baz
                         <br style="font-family: courier new,monospace;">    (irb):4: syntax error, unexpected ';', expecting tCOLON2 or '[' or '.'
    def baz; return [],[1]; end;  baz                             <br style="font-family: courier new,monospace;">
)def ban; return [1],[2]; end; ban # => [1, 2] vs SyntaxError: compile error
(   (irb):4: syntax error, unexpected tSTAR, expecting tAMPER
   def ban; return [1],[2]; end; ban # => [1, 2]                         <br style="font-family: courier new,monospace;">
   (irb):4: syntax error, unexpected ';', expecting tCOLON2 or '[' or '.'   def ban; return [1],[2]; end; ban # => [1, 2]
                             <br style="font-family: courier new,monospace;">)
def boo; return *[1,2]; end; boo # => [1, 2] vs [1, 2]
So here we clearly see how 1.9 is more 'safe' ...

=end

#7

Updated by katz (katz bo) over 9 years ago

=begin
Thank you, Benoit Daloze.

I wrote:

What does Ruby do with a = *[] ? Let me illustrate an IMO proper way:
Ruby looks at *[] and goes, "Fine, let me unpack it."
Ruby unpacks *[].
Then Ruby goes, "Nothing? But I can't carry ! No.. not in an
assignment! Wait.. Let's do it!"
Ruby marks it as nil.

As now I think I understand assignment better, I'd like to correct myself a bit:

foo = *[]
When processing the above line, the Ruby interpreter does these things:
1) First Ruby sees the left side of the assignment, i.e. foo =, and he points foo to nil.
2) Then Ruby tries to evaluate the rvalue. Looking at the *, Ruby unpacks the array [].
3) Ruby gets a little surprised when he finds it's empty in there.
4) So Ruby (happily) does nothing, because there is nothing to re-point foo to.

That's the story. And this story tells me foo ends up nil.
What's the new story about ruby 1.9 if it's not a bug?
=end

#8

Updated by mame (Yusuke Endoh) about 9 years ago

  • Status changed from Assigned to Rejected

=begin
Hi,

What's the new story about ruby 1.9 if it's not a bug?

If rvalue has a single expression, it returns the value itself.
If rvalue has multiple expressions (including splat), it returns
an array whose elements are each result.

If lvalue has a single variable, it assigns rvalue itself.
If lvalue has multiple variables, corresponding variable and
rvalue element are assigned respectively (if rvalue is not an
array, it is interpreted as singleton array).

Anyway, this is never a bug, It is contemplated, intended spec
change. Please accept reality.

You can register the same change request to Feature tracker,
but I strongly advise you to provide more concrete reason than
intuition.

--
Yusuke Endoh mame@tsg.ne.jp
=end

Also available in: Atom PDF