Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

ruby

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"]
like image 763
Patrik Björklund Avatar asked Jun 05 '12 16:06

Patrik Björklund


1 Answers

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")
like image 185
robbrit Avatar answered Oct 08 '22 05:10

robbrit