zabolekar
zabolekar

Reputation: 1644

Ruby: evaluation order depends on whitespace and seemingly redundant parens, why?

I'm trying to understand the behaviour of arithmetic methods in Ruby. 1 .+ 2 .* 3 and 1 .+ (2) .* 3 and 1. + 2. * 3 and 1. + (2). * 3 and 1.+2.*3 all evaluate to 7, which means :+ is called before :*, but 1.+(2).*3 evaluates to 9, which means :* is called before :+. Redefining both methods confirms that that's what's happening:

class Integer
    alias_method :add, :+
    alias_method :mul, :*
    def + other
        print :+
        self.add other
    end
    def * other
        print :*
        self.mul other
    end
end

puts 1 .+ 2 .* 3
puts 1 .+ (2) .* 3
puts 1. + 2. * 3
puts 1. + (2). * 3
puts 1.+2.*3
puts 1.+(2).*3

Output:

*+7
*+7
*+7
*+7
*+7
+*9

Why does it happen? Please point me where to find the relevant documentation. Thanks in advance.

Upvotes: 0

Views: 54

Answers (1)

Siim Liiser
Siim Liiser

Reputation: 4348

Let's give an example that might be easier to understand

puts (2) + 3

vs

puts(2) + 3

In the first case, puts is followed by a space. Ruby takes it as omitted parentheses and evaluates the rest as the argument. It's equvalent to puts((2) + 3). 5 is printed. In the 2nd case, puts is immediately followed by a left parenthesis and ruby takes that as the beginning of method arguments. It's equivalent to (puts(2)) + 3. It will print a 2 and then fail when trying to evaluate nil + 3.

Now lets look at your examples

puts 1 .+ 2 .* 3
puts 1 .+ (2) .* 3
puts 1. + 2. * 3
puts 1. + (2). * 3
puts 1.+2.*3

In the first 6 cases, no method call is immediately followed by (, all the parentheses are redundant. They are evaluated right to left. Note that not * before +, but right to left.

puts 1 .* 2 .+ 3 #=> 5

In the last example puts 1.+(2).*3, the ( immediately follows .+, so 2 is an argument to .+. The result is then multiplied by 3.

Upvotes: 2

Related Questions