I understand there is a difference in the precedence as shown in another answer:
p foo = false || true
# => true
p foo = false or true
# => false
But it seems like there is something more that's different between or
and ||
.
For example:
p foo = 42 or raise "Something went wrong with foo"
# => 42
p foo = nil or raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
p foo = 42 || raise "Something went wrong with foo"
# => syntax error, unexpected tOP_ASGN, expecting end-of-input
I was expecting to get:
p foo = 42 or raise "Something went wrong with foo"
# => 42
p foo = nil or raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
p foo = 42 || raise "Something went wrong with foo"
# => Something went wrong with foo (RuntimeError)
But it's a syntax error. So what is happening?
Here's a precedence table for Ruby.
It's not clear from this table but Ruby method invocation without parentheses has a lower precedence than ||
and =
, but higher than or
. See this question.
So for your code, from highest to lowest precedence :
||
=
raise "something"
or
or
foo = 42 or raise "Something went wrong with foo"
First comes =
:
( foo = 42 ) or raise "Something went wrong with foo"
Then raise
:
( foo = 42 ) or ( raise "Something went wrong with foo" )
Then or
:
( ( foo = 42 ) or ( raise "Something went wrong with foo" ) )
||
foo = 42 || raise "Something went wrong with foo"
First comes ||
:
foo = ( 42 || raise ) "Something went wrong with foo"
Here's your syntax error!
You want :
foo = 42 || (raise "Something went wrong with foo") #=> 42
or
foo = 42 || raise("Something went wrong with foo") #=> 42
or just
foo = 42 || raise
When you have troubles with precedence, you should be careful about adding another puts
or p
without parentheses !
For example :
p [1,2,3].map do |i|
i*2
end
outputs :
#<Enumerator: [1, 2, 3]:map>
even though you might have expected :
[2, 4, 6]
||
and or
are not the same operation.
The first is equivalent to a method call, the latter is a control flow keyword. You probably always want to use ||
to avoid confusion with precedence. Most style guides for Ruby have a clause that bans the use of and
and or
for that reason.
So then,
A or B
# can be considered equivalent to
if A then A else B end
whereas
A || B
# can be considered equivalent to
A.or { B } # given a hypothetical "logical or" method
Now let's look into your or
example
p foo = false or true
is equivalent to
temp = p(foo = false) # => nil
if temp
temp
else
true
end
and thus when executed prints false
and returns true
[1] pry(main)> p foo = false or true
false
=> true
[2] pry(main)> foo
=> false
whereas
p foo = false || true
is equivalent to (glossing over the difference between boolean and logical OR for now since your example is dealing with booleans anyway)
p(foo = false.|(true))
and thus when executed prints true
and returns true
[1] pry(main)> p foo = false || true
true
=> true
[2] pry(main)> foo
=> true
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With