Ruby Kernel.raise method throws error when enclosi

2020-04-28 06:48发布

I like method parameters enclosed in parenthesis, this is some Pascal nostalgia. When cleaning up code, if I find a method parameters without it I enclose them immediately. Today it caused my working code throwing errors although my syntax looks okay according to the documentation.

Kernel.raise's documentation has this format:

(Object) raise(exception[, string [, array]])

These are all working:

> raise TypeError
TypeError: TypeError

> raise (TypeError)
TypeError: TypeError

> raise "Error message"
RuntimeError: Error message

> raise ("Error message")
RuntimeError: Error message

But the enclosed version of the next throws syntax error:

> raise TypeError, "Error message"
TypeError: Error message

> raise (TypeError, "Error message")
SyntaxError: unexpected ')', expecting $end

I can live without it, I just want to know why this ends with an error.

2条回答
冷血范
2楼-- · 2020-04-28 07:35

You probably already know that in idiomatic Ruby one would never insert a space between the end of a method and an argument list in parenthesis. Some style guides explicitly forbid it.

There's a pragmatic reason too.

1.9.2-p290 > def concat(a, b)
1.9.2-p290 >  a + b
1.9.2-p290 > end

1.9.2-p290 > concat 'foo', 'bar'
 => "foobar"
1.9.2-p290 > concat('foo', 'bar')
 => "foobar"
1.9.2-p290 > concat ('foo', 'bar')
SyntaxError: (irb):27: syntax error, unexpected ',', expecting ')'

You'll encounter errors calling any method this way, not just Kernel.raise.

I'm not familiar with Ruby internals, but I would imagine the reason for this is that when a space precedes the argument list, Ruby is expecting the "no-parens" style. So of course this works:

1.9.2-p290 :035 > concat ("bar"), ("foo")
 => "barfoo"

Presumably Ruby is trying to evaluate the contents of each parenthesized expression before passing the result to the method. I'd speculate that writing raise (TypeError, "Error message") is asking Ruby to evaluate just TypeError, "Error message", which of course fails.

查看更多
别忘想泡老子
3楼-- · 2020-04-28 07:51

Parentheses are used for expression grouping and precedence override in Ruby. So, when you say

foo (bar, baz)

You are sending the message :foo with a single argument which is the result of evaluating the expression bar, baz. And bar, baz is not a valid expression, therefore you get a SyntaxError.

foo (bar)

works, because bar is a valid expression.

foo (if bar then baz else quux end)

would work, too.

If you want Ruby to interpret parentheses not for expression grouping but for passing multiple arguments along with a message send, the opening parenthesis needs to directly follow the message selector:

foo(bar, baz)

This has nothing to do with Kernel#raise, BTW. You cannot change the syntax for message sends in Ruby (in fact, you cannot change any syntax in Ruby), therefore whatever is true for Kernel#raise must also be true for every other method.

查看更多
登录 后发表回答