Ruby Parenthesis syntax exception with i++ ++i
There's no ++
operator in Ruby. Ruby is taking your foo++ + ++foo
and taking the first of those plus signs as a binary addition operator, and the rest as unary positive operators on the second foo
.
So you are asking Ruby to add 5 and (plus plus plus plus) 5, which is 5, hence the result of 10.
When you add the parentheses, Ruby is looking for a second operand (for the binary addition) before the first closing parenthesis, and complaining because it doesn't find one.
Where did you get the idea that Ruby supported a C-style ++
operator to begin with? Throw that book away.
Why are parenthesis sometimes required in Ruby?
Because { ... }
has two meanings: hash literal and block.
Consider this:
%w(foo bar baz).select { |x| x[0] == "b" }
# => ["bar", "baz"]
Here, { ... }
is a block.
Now imagine that you are calling a method on the current object, so the explicit receiver is not needed:
select { |x| x[0]] == "b" }
Now imagine that you don't care about the parameter:
select { true }
Here, { true }
is still a block, not a hash. And so it is in your function call:
redirect_to { action: 'atom' }
is (mostly) equivalent to
redirect_to do
action: 'atom'
end
and it is nonsense. However,
redirect_to({ action: atom' })
has an argument list, consisting of a hash.
Ruby Kernel.raise method throws error when enclosing parameters in parenthesis
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.
Ruby - when I should use parenthesis or not when calling a function/method
There isn't an official standard for Ruby code best practices. However, a set of preferred styles has evolved in the Ruby community. It's a good idea to follow those preferred styles, just because it makes your code more easily readable by other Ruby programmers.
Nick Roz has given you a link to the style guide. I would also recommend that you consider installing and using rubocop. It will give you feedback on when and when not to parenthesize arguments, many other formatting matters such as proper indenting, and which of the often several different syntax options to choose in a particular situation.
To answer your specific question about whether or not to use parentheses for method arguments, the answer is yes, in general. Exceptions are, as the guide says, "methods that have 'keyword' status in Ruby." An example is puts
(actually the Kernel.puts
method). Most people don't use parentheses here, but the guide states that they are optional.
Another example, as Nick has said (although "methods with keyword arguments" isn't quite correct; in that case the arguments are symbols that represent methods rather than keywords), is attr_accessor
, which is actually Module.attr_accessor
.
So, as a general rule, if it looks like a "command" (a "keyword," if you will), but is actually a method, omit the parentheses. Otherwise, use them. And if you're not sure of the difference, get in the habit of using rubocop.
Idiomatic use of parentheses in Ruby
I'd suggest you take a look at the community-driven Ruby coding style guide, here particularly the section on Syntax.
Omit parentheses around parameters for methods that are part of an internal DSL (e.g. Rake, Rails, RSpec), methods that are with "keyword" status in Ruby (e.g. attr_reader, puts) and attribute access methods. Use parentheses around the arguments of all other method invocations. - excerpt from the guide
class Person
attr_reader :name, :age
# omitted
end
temperance = Person.new('Temperance', 30)
temperance.name
puts temperance.age
x = Math.sin(y)
array.delete(e)
Ruby method call using round brackets throwing syntax error
You cannot pass a block with such a synatx, you would have to do something like this:
a.select(&lambda { |v| v.is_a?(Integer) })
but normally you would just do
a.select { |v| v.is_a?(Integer) }
which is the same as
a.select() { |v| v.is_a?(Integer) }
i.e the block is outside the method parameters.
You could also use the 'stabby' lambda syntax:
is_a_integer = ->(v) { v.is_a?(Integer) }
a.select(&is_a_integer)
So if you want to pass a block as an argument you need to prefix with &
, but usually you would have the block outside the parameters for atheistic reasons.
Also notice the difference between these to method signatures and the way they are called:
def call(&block)
yield
end
call { 1 } # => 1
call(lambda { 1 }) # => ArgumentError
call(&lambda { 1 }) # => 1
and
def call(block)
block.call
end
call { 1 } # => ArgumentError
call(lambda { 1 }) # => 1
call(&lambda { 1 }) # => ArgumentError
This is because lambda (and Procs) are objects hence we can do #call
to evaluate them, but blocks are not and can be evaluated using the yield
keyword. There is more information in this blog post.
lambda { 1 }.class # => Proc
Exceptions: why does adding parenthesis change anything?
Your case 2 is the same as this:
baz = 'a'
(baz, bar = foo) rescue [1, 2]
Since foo
raises an error, assignment of values to baz
and bar
fails, so baz
remains to be "a"
, and bar
remains to be nil
, a value that was assigned during the parsing stage. Then, the assignment gets rescued, and the return value would be [1, 2]
.
Your case 4 is the same as this:
(baz = raise Error) rescue 1
Since the righthand side of the assignment raises an error, assignment to baz
fails, and baz
would remain to be nil
, that was assigned during the parsing stage. Then, the assignment is rescued, and the return value is 1
.
Related Topics
How to Find the Most Recently Modified Folder in a Directory Using Ruby
How to Check If a Resource Exists in an Aws S3Bucket
Why Aren't "Gem" and "Bundle" Using the Same Libxml2
Ruby Symbol#To_Proc Leaks References in 1.9.2-P180
How to Test Order-Conscious Equality of Hashes
How to Get the Real File from S3 Using Carrierwave
Enforce File Upload Size on Heroku
Rewrite Template.Js.Erb into Template.Js.Slim
Rails Syntax Error: Unexpected Keyword_Ensure, Expecting Keyword_End
Secure Erasing of Password from Memory in Ruby
Sorting Numeric Strings in Ruby
Why Do I Get "Undefined Method 'Paginate'" Error in Production
Error When Starting Sinatra: "Tried to Create Proc Object Without a Block"