Bug #10837
closedspatting a single element array produces an array instead of a single value for return and next
Added by bughit (bug hit) over 10 years ago. Updated over 9 years ago.
Description
irb(main):013:0> ->{return *[1]}.()
=> [1]
irb(main):014:0> ->{next *[1]}.()
=> [1]
*[x] should mean x as it already does for arguments
Updated by nobu (Nobuyoshi Nakada) over 10 years ago
Actions
#1
[ruby-core:68061]
- Status changed from Open to Rejected
It's similar to return *[1, 2].
Updated by bughit (bug hit) over 10 years ago
Actions
#2
[ruby-core:68062]
Nobuyoshi Nakada wrote:
It's similar to
return *[1, 2].
No it's not similar,
return *[1, 2] means return 1, 2
similar would be:
return *[1] means return 1
in general, splatting in a context that takes a coma separated list of items (method args, rescue, return, next, multiple assignment) is supposed to destructure the array. The array should be gone, only the elemnts remain, so returning the [1] is wrong.
Updated by bughit (bug hit) over 10 years ago
Actions
#3
[ruby-core:68063]
bug hit wrote:
Nobuyoshi Nakada wrote:
It's similar to
return *[1, 2].No it's not similar,
return *[1, 2]meansreturn 1, 2similar would be:
return *[1]meansreturn 1in general, splatting in a context that takes a coma separated list of items (method args, rescue, return, next, multiple assignment) is supposed to destructure the array. The array should be gone, only the elemnts remain, so returning the
[1]is wrong.
some examples
method_call *[a] is method_call a as expected
rescue *[a] is rescue a as expected
[*[a]] is [a] as expected
b = *[a] is b = [a] why?
(next|return) *[a] is (next|return) [a] why?
the intuitive, expected and logical behavior would be for *array to consistently destructure, when used in a coma separated list context
Updated by bughit (bug hit) over 10 years ago
Actions
#4
[ruby-core:68075]
bug hit wrote:
bug hit wrote:
Nobuyoshi Nakada wrote:
It's similar to
return *[1, 2].No it's not similar,
return *[1, 2]meansreturn 1, 2similar would be:
return *[1]meansreturn 1in general, splatting in a context that takes a coma separated list of items (method args, rescue, return, next, multiple assignment) is supposed to destructure the array. The array should be gone, only the elemnts remain, so returning the
[1]is wrong.some examples
method_call *[a]ismethod_call aas expected
rescue *[a]isrescue aas expected
[*[a]]is[a]as expected
b = *[a]isb = [a]why?
(next|return) *[a]is(next|return) [a]why?the intuitive, expected and logical behavior would be for
*arrayto consistently destructure, when used in a coma separated list context
please explain
Updated by marcandre (Marc-Andre Lafortune) over 10 years ago
Actions
#5
[ruby-core:68081]
bug hit wrote:
b = *[a]isb = [a]why?
(next|return) *[a]is(next|return) [a]why?
I agree, this can be surprising.
The reason for the behavior is that return 1, 2, strictly speaking, shouldn't be valid Ruby as you can only return one value. Instead of forbidding it, return 1, 2 is automatically converted to return [1, 2]. They are equivalent. So return *array is converted to return [*array], and that holds even in the cases where array contains one or no element (or isn't an array).
The same can be said for next and =.
HTH
Updated by bughit (bug hit) over 10 years ago
Actions
#6
[ruby-core:68082]
Marc-Andre Lafortune wrote:
bug hit wrote:
b = *[a]isb = [a]why?
(next|return) *[a]is(next|return) [a]why?I agree, this can be surprising.
The reason for the behavior is that
return 1, 2, strictly speaking, shouldn't be valid Ruby as you can only return one value. Instead of forbidding it,return 1, 2is automatically converted toreturn [1, 2]. They are equivalent. Soreturn *arrayis converted toreturn [*array], and that holds even in the cases wherearraycontains one or no element (or isn't an array).The same can be said for
nextand=.HTH
So it seems it's an implementation artifact. Would it not be better if semantics of splatting were consistent, i.e. rvalue splat would always destructure the array?
Updated by bughit (bug hit) over 10 years ago
Actions
#7
[ruby-core:68307]
Nobuyoshi Nakada wrote:
It's similar to
return *[1, 2].
Please clarify your position, is this about preserving compatibility, or do you really disagree that conceptually an rvalue splat should eliminate the array?
Updated by bughit (bug hit) over 10 years ago
Actions
#8
please explain
Updated by najamelan (Naja Melan) over 9 years ago
Actions
#9
[ruby-core:76394]
bug hit wrote:
Nobuyoshi Nakada wrote:
It's similar to
return *[1, 2].Please clarify your position, is this about preserving compatibility, or do you really disagree that conceptually an rvalue splat should eliminate the array?
Actually this is really useful. Whenever a method accepts a something or an array of somethings you can use the splat operator to make it an array if it's not already. Now you can operate on it as an array.
def initialize( something )
@something = *something
end
If it didn't do that, you would have to write:
@something = something.kind_of?( Array ) ? something : [something]
Updated by bughit (bug hit) over 9 years ago
Actions
#10
[ruby-core:76400]
Naja Melan wrote:
Actually this is really useful. Whenever a method accepts a
somethingor anarray of somethingsyou can use the splat operator to make it an array if it's not already.
This is a bad hack that produces obfuscated code and does not work properly, if you splat a hash you will end up with an array of arrays. What you want is something like Array.wrap from rails, which both communicates intention clearly and works universally.
Splatting an array should consistently destructure it.
Updated by najamelan (Naja Melan) over 9 years ago
Actions
#11
[ruby-core:76401]
@bug hit
My apologies, it seems I'm mistaken. The splat operator does not just seem to coerce into array for all types:
a = { a: 1 }
p [a] # [{:a=>1}]
p *a # [:a, 1]
p [*a] # [[:a, 1]]
It seems it calls #to_a on classes that implement this. I'm sorry if I create confusion in the ongoing discussion.
Updated by bughit (bug hit) over 9 years ago
Actions
#12
[ruby-core:76402]
Naja Melan wrote:
@bug hit
My apologies, it seems I'm mistaken. The splat operator does not just seem to coerce into array for all types:
a = { a: 1 } p [a] # [{:a=>1}] p *a # [:a, 1] p [*a] # [[:a, 1]]It seems it calls
#to_aon classes that implement this. I'm sorry if I create confusion in the ongoing discussion.
Also when it's already an array (a = *array), what you are asking ruby to do is first destructure the array, then coalesce it back into an array, essentially making a shallow copy, whereas what you actually want is to leave it alone. Array.wrap leaves it alone.