Why is the splat/unary operator changing the assigned value a when p is called before *a = ""?

ruby

Solution

Very interesting question! Ruby takes this expression:

 p *a = "a"

and translates it to something like this:

 temp = (a = "a")
 p *temp

So the first thing that happens is that `a` gets assigned to `"a"`, and then the result of the assignment expression which is `"a"` gets splatted and sent to `p`. Since `p`'s default behaviour when sent multiple arguments is just to iterate over and print each one, you only see `"a"` appear.

In short, it follows a "assign then splat" order of evaluation. So `a` gets assigned to `"a"` before the string gets splatted.

When you don't have a function call however, it is interpreted as something like this:

# *a = "a" gets interpreted as:
temp = "a"
a = *temp

This follows a "splat then assign" order of evaluation. So `a` gets assigned after the string gets splatted.

You can see what's being received by a function by going like this:

def foo *args
  puts args.inspect
end

foo *a = "a"    # outputs ["a"]
a               # outputs "a"

Hope this clears up what's going on!

In short (thanks to Mark Reed):

p *a = "a"    # interpreted as: p(*(a = "a"))
*a = "a"      # interpreted as: a = *("a")

Problem

To give a little context around how I understand the problem. Using splat collect on a string sends :to_a or :to_ary to the String ``` class String def method_missing method, *args, &block p method #=> :to_ary p args #=> [] p block #=> nil end end *b = "b" ``` So I was thinking that redefining the :to_ary method would be what I'm after. ``` class String def to_ary ["to_a"] end end p *a = "a" #=> "a" p a #=> "a" *b = "b" p b #=> ["to_a"] ``` Now this confuses me to no end. Printing the result from the *a = "a" changes the value assigned to a? To demonstrate further ``` class String def to_ary [self.upcase!] end end p *a = "a" #=> "a" p a #=> "a" *b = "b" p b #=> ["B"] ```

Original source