0

I'm having some trouble understanding why I can't call select with round brackets.

a = [1,2,3]
a.select {|v| v.is_a?(Integer)}  # works
a.select({|v| v.is_a?(Integer)}) # syntax error, unexpected '|', expecting '}

As far as I can tell select is a regular method.

Array.method_defined? :select # true
Array.method_defined? :is_a?  # true

I though round brackets are optional for methods in ruby.
In the case below round brackets make no difference.

a.reverse() == a.reverse #true

I'm using ruby 2.2.1.
Any ideas?

ConorSheehan1
  • 1,640
  • 1
  • 18
  • 30

3 Answers3

4

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
Kris
  • 19,188
  • 9
  • 91
  • 111
2

Round brackets are a way to pass arguments to a method while the squiggly brackets (or do/end) are a way to pass a block to a method. They are not interchangeable.

Squiggly brackets can also be used to create a hash in ruby, which can cause some confusion.

Some ruby methods can take arguments and a block in which case you can use round brackets before the squiggly brackets: E.G.

open("ChangeLog") { |f|
    f.slice_before(/\A\S/).each { |e| pp e }
}
Puhlze
  • 2,634
  • 18
  • 16
  • Ok that makes sense. I was thinking the block would be treated the same as other parameters. Forgot blocks are an exception in ruby and aren't actually objects. The round brackets work fine if you pass a proc or lambda to select, but not a block. Thanks for the help! – ConorSheehan1 May 23 '17 at 15:35
1

With or without parenthesis, Array#select doesn't accept any regular arguments. It, however accepts a block argument but the blocks associated with a method call are always placed after the parenthesis, not inside them.

Consequently, the call:

a.select {|v| v.is_a? Integer }

can also be written with parenthesis as:

a.select() {|v| v.is_a? Integer }
axiac
  • 68,258
  • 9
  • 99
  • 134