Who has the highest precedence here ?

In this article, we’re going to explore the following topics:

method 'arg' and return

method1 || method2 'hello'

variable = a or b

block {..} vs block do..end

Before to start

I’m thrilled to share with you our latest project: Fun Facts about Ruby — Volume 1

Please feel free to spread the word and share this post! 🙏

Thank you for your time!

Introduction

Operator precedence is a collection of rules that reflect conventions about which procedures to perform first in order to evaluate a given mathematical expression.

It’s a very important layer for any programming language. It can definitely change the way you use a language.

Indeed, depending on the priority given to a specific operator, keyword or method call your program can produce unattended side effects.

In this article, we’re going to detail some idioms that can ease the readability of you code but can also lead to undesirable side effects in Ruby.

method 'arg' and return

This idiom can ends up to an unattended side effect as the && operator has a higher precedence than and

produces

"method1"

In the above example, only the call to method1 calls the p method.

Why the call to method2 doesn’t output "method2" ?

Let’s slightly modify the above example to show you how Ruby executes the content of method1 and method2

I added parens to show you how Ruby processes operations in the first example.

We can see that for method1 , the and operator has a lower precedence than a method call — p 'method1' in our case.

So the method call is processed then return is called.

For method2 the && operator has a higher precedence than the p ‘method2' method call.

So because there is no parens around the parameter of the p method call then the && operator will apply a LOGICAL AND operation between ‘method2' and return first.

Then the result of this operation will be passed as parameter of the p method call.

But as return exits the method then the call to p will never be processed.

So here a solution is to use and instead of && .

method1 || method2 'hello'

This pretty common idiom can ends up to an error raising that can be confusing if we are not familiar with the precedence rules for || or and method calls .

Let’s have a look at the following example

The method1 or method2 ‘Jim’ idiom works as expected because method1 returns a string which is not falsy.

Note that here, the or operator has a lower precedence than the method2 'Jim' method call.

So, why the call to method1 || method2 'Jim' raises a SyntaxError ?

Let’s slightly modify the above example to show you how Ruby executes the above code

As the or operator has a lower precedence than the method2 ‘Jim’ method call then there is no syntax error in this idiom. method1 is processed and as it’s not falsy then method2 ‘Jim’ is not processed due to short-circuit evaluation in Ruby.

As the || operator has a higher precedence than the method2 ‘Jim’ method call then it tries to operate a OR between method1 and method2 .

The problem is that the 'Jim' string isn’t interpreted as a parameter of the method2 .

There is at least 3 solutions to overcome this issue:

surrounding the parameter with parens: method1 || method2('Jim')

using the or operator instead

variable = a or b

Due to the fact that the assignment operator = has a higher precedence than or then this idiom can result to an unattended side effect

Here, the variable a contains the value 42 when the variable b is nil .

Let’s slightly modify the above example to show you how Ruby executes the above code

For the variable a , the || operator has a higher precedence than = .

So nil || 42 is evaluated and the result is assigned to a .

For the variable b , the or operator has a lower precedence than the = operator. So nil is assigned to b first. Then 42 is evaluated.

So here, a solution is to use || instead of or — depending on your needs.

block {..} vs block do..end

Feel free to read Block as method argument in Ruby (2mn) if you’re not familiar with blocks in Ruby

Precedence also matters with Ruby blocks.

Indeed, blocks using brackets have higher precedence than blocks using do..end .

Let’s illustrate this assertion with the following example

produces

method2 received block? true

method1 received block? false method2 received block? false

method1 received block? true

Here, with the call to method2 using brackets syntax, the method2 receives the block and not the method1 .

In contrary, with the call to method2 using do..end syntax, the method1 receives the block and not the method2

Let’s detail what happens in the above example by adding few parens

Here, we can clearly see that for the first call to method1 the block is attached to the inner method call — method2 .

For the second call to method1 , we can see that the block is attached to the outer method call — method1 .

So when we say that {..} block has a higher precedence than do..end block this has an influence on which method call the block will be attached to.

Voilà!