Ruby's or operator precedence
Operators have precedence and sometimes they can give us hard time to figure out why our code works as it does.
Ruby has three or operators, |
, ||
, and the readable or
. All of them
have different precedences. That is, the precedence, that makes some trouble if
we do not care about which one we use.
The evaluation of logical operators are left-associative, that means the far left
operator is evaluated at first, than its result will be the left operand of the
next operator and so on. |
operator evaluates both operands, while ||
or or
are short-circuit operators.
There are two falsy values in the language, false
and nil
, anything else belong
to the truthy set. |
operator works as expected and returns logical values.
puts false | nil # false
puts true | nil # true
puts 2 == 2 | 2 # true
puts 2 == 2 | 3 # false, here 3 is consider as an int value, not a logical value
In case of ||
and or
operators returned value is not logical, as it would be in case of |
.
Nothing has suprised us!
puts 2 == 2 || 3 # 3
puts 3 || 2 == 2 # true
puts 2 == 2 or 3 # 3
puts 3 or 2 == 2 # true
Let us see a bit more complex example and examine the results. Since both ||
and or
are lazy operators, if left-hand side operand is true, the right-hand
side operand won’t be evaluated, hence we understand why empty string is
emitted instead of 3
in place of b
.
a = 2 || b = 3
puts "|| '#{ a }' '#{ b }'" # || '2' ''
a = 2 or b = 3
puts "or '#{ a }' '#{ b }'" # or '2' ''
What if the right-hand side operator is also evaluated, since the result of the
expression is false
on the left.
a = false || b = 3
puts "|| '#{ a }' '#{ b }'" # || '3' '3'
a = false or b = 3
puts "or '#{ a }' '#{ b }'" # or 'false' '3'
Uhh, quite wierd results. Not just they are different, but the value of a
is
the same as the value of b
. Where is false
?
The trick is that the precedence of ||
is higher than the precedence of =
(assign),
but the precedence of =
is higher than the precedence of or
.
According to the abovementioned lines, the evaluation of ||
looks as follows:
a = (false || b = 3)
a = ( b = 3 )
a = 3
And operators, like &
, &&
, and and
, work similar.
As we see the precedence of operators can cause a big mess if we are not circumspect enough.
Though, if it is possible, use ||
operator and be careful.
Sample can be found: https://gist.github.com/torokmark/8347c58f0cda918e99cb43fba84aea81