Operator Precedence Anxiety


Early in my career, I used to add useless brackets to my logical expressions “just in case”, meaning that I wasn’t really sure about the precedence of operators, and it was easier to just add some parentheses than be bothered to learn what operator was evaluated first.

After a while I learnt to do better than that, and I wish other engineers did the same. Over and over, I keep seeing experienced engineers that do things “just in case”, instead of taking a minute or two to learn how things really work.

What’s interesting is that most modern languages share the same order for logical operators, inherited from C, so they broadly have the same operator precedence, with a few exceptions. However, if you happen to work with some relatively old languages, you may encounter a few discrepancies.

In the rest of this article, I’ll show a few practical differences worth knowing. These are the places where we can get surprised when we switch languages. If we learn these, we can delete a few unneeded brackets.

Logical Operators

The logical AND and OR operators work differently in Bash than in modern programming languages. For example, in the following code, Bash returns false, while TypeScript returns true.

true || false && false

In TypeScript, and most C-like languages, the AND operator takes precedence over the OR operator. However, in Bash they have the same precedence, so they are just evaluated from left to right.

This gets very interesting in languages like Ruby that have symbol logical operators “&&” and “||” and word logical operators “and” and “or“. They are equivalent for practical purposes, except for which is evaluated first. This is the order of precedence of operators in Ruby:

!   >   &&   >   ||   >   =   >   and / or

One thing to highlight is that “and” and “or” have the same precedence, so they are evaluated from left to right. Another interesting thing to note is that the assignment operator “=” has higher precedence than and/or. This can cause unexpected results if we’re unaware of this, for example:

x = true and false

The expression above evaluates to false, but sets x to true. That’s because by the time we evaluate the “and false” part, we’ve already done the assignment.

Binary Operators

In some old programming languages, like Smalltalk, binary operators have the same level of precedence. So, for code like this:

3 + 4 * 5

Smalltalk will return 35, because it first calculates 3 + 4 and only then it computes 7 * 5. On the contrary, languages like C, Java or Python would return 23, as this expression would be equivalent to do 3 + (4 * 5).

This only becomes a problem if you ever work with an old programming language, since the most popular programming languages nowadays behave just like C in this regard.

Ternary operator

Let’s now explore this other example where we have two nested ternary operators:

a ? 1 : c ? 3 : 4

When the order of precedence is the same, programming languages need to decide how to automatically group the operands.

TypeScript is right associative, which means that the expression will return 3, as it is parsed as a ? 1 : (c ? 3 : 4). However, because PHP (7.x and earlier) is left associative, the same code is parsed as (a ? 1 : c) ? 3 : 4, which returns 4 instead.

Since PHP 8 (2020), parentheses on chained ternary operators are compulsory to prevent unexpected results from assuming the wrong association (missing parentheses throw a parse error).

When to use parentheses

I have one big ask. Please, please, please do not use parentheses “just in case”. That is not a good reason. When we say that, we’re just saying that we don’t know how that programming language works.

Parentheses are still useful when we want to override the default grouping based on precedence, and also when we want to add clarity to what we’re doing. Note that this is a decision we make to have clear code, not an excuse to cover for what we don’t know.

On top of that, we should already be writing tests on our code, whether we know the order of precedence of our operators or not, and they will tell us if any of our assumptions are wrong.

By following this advice we can improve the readability of our code, one bit at a time.

Happy coding!
José Miguel

Share if you find this content useful, and Follow me on LinkedIn to be notified of new articles.


Leave a Reply

Your email address will not be published. Required fields are marked *