Search code examples
ruby-on-railsruby

Error when no space between method and parenthesis


What causes the error below? I expect them both to return 2.

The Ruby Style Guide encourages no space between method name and parenthesis and accordingly Rubocop alerts with (safely auto-correctable) Lint/ParenthesesAsGroupedExpression when there is a space. But aligning with the Style Guide and having no space causes an actual error.

Integer.sqrt (4 if true) # 2

Integer.sqrt(4 if true) # syntax error, unexpected `if' modifier, expecting ')'

There is a related question 10 years ago which explores similar issues with a top-rated answer (2 votes) that was not accepted by the OP and I find unclear and hard to follow. The response to this question appears far clearer and more comprehensive. This question also references a nuance with Rubocop to be explored further.


Solution

  • The exhaustive explanation is quite complex, and requires digging into the Ruby BNF grammar, but the simple(r) explanation requires understanding two things:

    1. If you do NOT use the form function(args) (without space and with parentheses), you are making Ruby guess what you mean. If you always use function(args), then Ruby KNOWS it is a function call with arguments. If you do not, then Ruby will have to guess what you mean.

    Just as an example. Take function 4 if true. How would Ruby know what you mean here? Either it is function((4 if true)), or function(4) if true. Ruby has to "guess".

    This was just an example, you have to combine this rule with rule number 2 below to understand the full picture:

    1. You cannot put a control statement (if, while, unless, etc.) as a function argument, UNLESS YOU ENCLOSE IT IN PARENTHESES.

    If you accept these two rules, 1 and 2 together, you will understand that this:

    function (control_statement)
    

    Will be interpreted as this:

    function((control_statement))
    

    And therefore the only form which is allowed, if you plan to use a control statement in an argument list, is this one:

    Integer.sqrt((4 if true))
    

    All other forms will confuse the interpreter, and are not allowed by the Ruby grammar.