Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does `puts(nil or 4)` fail in Ruby?

Tags:

ruby

When I do:

puts(nil or 4)

Ruby complains:

SyntaxError: syntax error, unexpected keyword_or, expecting ')'

Why is that? puts(nil || 4) does work, but I wonder why or doesn't. I thought the difference between the two was only in their operator precedence.

(I know the expression nil or 4 doesn't seem useful, as it always returns 4. It's just an example, for simplicity's sake. My actual expression is Integer(ENV['WD'] or 4).)

like image 757
Niccolo M. Avatar asked Jun 03 '16 11:06

Niccolo M.


Video Answer


2 Answers

Short answer

Because that's how ruby syntax is.

Longer answer

and/or keywords were designed to be used in control flow constructs. Consider this example:

def die(msg)
  puts "Exited with: #{msg}"
end

def do_something_with(arg)
  puts arg
end

do_something_with 'foo' or die 'unknown error'
# >> foo
# >> Exited with: unknown error

Here or works nicely with ruby's optional parentheses, because of ruby parsing rules (pseudo-BNF).

In short, an argument list (CALL_ARGS) is a list of ARG, separated by comma. Now, most anything is an ARG (class definitions, for example, through being a PRIMARY), but not an unadorned EXPR. If you surround an expression with parentheses, then it'll match a rule for "compound statement" and, therefore, will be a PRIMARY, which is an ARG. What this means is that

puts( (nil or 4) ) # will work, compound statement as first argument
puts (nil or 4)  # same as above, omitted optional method call parentheses
puts(nil or 4) # will not work, EXPR can't be an argument
puts nil or 4 # will work as `puts(nil) or 4`

You can read the grammar referenced above to understand exactly how it works.

BONUS: Example of class definition being a valid ARG

puts class Foo
       def bar
         puts "hello"
       end
     end, 'second argument'

# >> bar # this is the "value" of the class definition
# >> second argument
like image 77
Sergio Tulentsev Avatar answered Oct 01 '22 19:10

Sergio Tulentsev


It is because or and and have lower precedence than method call. Your expression is interpreted as:

{puts(nil} or {4)}

where {} stands for grouping. The syntax error comes from the expression

puts(nil

(and the following will also raise a syntax error):

4)

If you force grouping by putting a pair of parentheses around the expression, then it will work the way you intended:

puts((nil or 4))

Notice that the outer pair of parentheses is used for method call, not grouping, hence just having one pair of parentheses has no effect of changing the grouping.

Alternatively, if you disambiguate a single pair of parentheses to be used for grouping by putting a space, then that will work too:

puts (nil or 4)
like image 35
sawa Avatar answered Oct 01 '22 19:10

sawa