Ruby Proc Syntax

Ruby Proc Syntax

When you prefix the last argument of a call with & you are making clear that you are sending a block and not a normal argument. Ok, in method(&:something), :something is a symbol, not a proc, so Ruby automatically calls the method to_proc to get a real block. And Rails guys (and now also vanilla Ruby) cleverly defined it as:

class Symbol
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
end

That's why you can do:

>> [1, 2, 3].map(&:to_s) # instead of [1, 2, 3].map { |n| n.to_s }
=> ["1", "2", "3"]

[edit] Note: when you realize that this construction is no syntatic sugar but generic infrastructure that Ruby provides, nothing stops you from implementing your own to_proc for other classes. Never felt limited because &:method allowed no arguments?

class Array
def to_proc
proc { |obj, *args| obj.send(*(self + args)) }
end
end

>> ["1", "F", "FF"].map(&[:to_i, 16])
=> [1, 15, 255]

Ruby - providing multiple lambda or proc arguments to a function?

Methods in Ruby may not take more than one block. However, you can bind any arbitrary number of blocks as procs (or lambdas) and pass them as regular arguments. You don't get the nice block sugar syntax (do...end), but it works just fine.

In Ruby, blocks (the anonymous functions passed to methods via {|...| } or do ... end syntax) are one of the very few exceptions to the "everything is an object" rule. Ruby makes special allowances for the fact that it is not uncommon to want to pass a callback or anonymous function to a method. You pass that with the block syntax, then can declare that you want access to the block as a Proc by adding the &block parameter as the last argument in your arg list, which takes your passed anonymous function and binds it to a Proc instance stored in the variable block. This lets you call it (by calling block.call) or pass it on as a normal object instance (as block) or as a block (as &block). You can also implicitly call it with yield without binding it to a Proc.

def debug(obj = nil, &block)
p [:object, obj]
p [:block, block]
end

def pass_block_as_proc(&foo)
puts "Passing as foo"
debug(foo)
puts "Passing as &foo"
debug(&foo)
end

pass_block_as_proc {}

Output:

Passing as foo
[:object, #<Proc:0x000055ee3a724a38>]
[:block, nil]
Passing as &foo
[:object, nil]
[:block, #<Proc:0x000055ee3a724a38>]

So, if we want to pass multiple callbacks to a method, we can't rely on the anonymous block sugar. Instead, we have to pass them as bound Proc instances:

def multiple_procs(value, proc1, proc2)
proc2.call(proc1.call(value))
end

puts multiple_procs "foo",
->(s) { s.upcase },
->(s) { s + s }

# => FOOFOO

This link is some additional useful reading to understand the difference between blocks and procs.

When to use lambda, when to use Proc.new?

Another important but subtle difference between procs created with lambda and procs created with Proc.new is how they handle the return statement:

  • In a lambda-created proc, the return statement returns only from the proc itself
  • In a Proc.new-created proc, the return statement is a little more surprising: it returns control not just from the proc, but also from the method enclosing the proc!

Here's lambda-created proc's return in action. It behaves in a way that you probably expect:

def whowouldwin

mylambda = lambda {return "Freddy"}
mylambda.call

# mylambda gets called and returns "Freddy", and execution
# continues on the next line

return "Jason"

end


whowouldwin
#=> "Jason"

Now here's a Proc.new-created proc's return doing the same thing. You're about to see one of those cases where Ruby breaks the much-vaunted Principle of Least Surprise:

def whowouldwin2

myproc = Proc.new {return "Freddy"}
myproc.call

# myproc gets called and returns "Freddy",
# but also returns control from whowhouldwin2!
# The line below *never* gets executed.

return "Jason"

end


whowouldwin2
#=> "Freddy"

Thanks to this surprising behavior (as well as less typing), I tend to favor using lambda over Proc.new when making procs.

Ruby Procs - ends up with syntax error

& is a shortcut for calling the to_proc method.
Because you have a symbol (:anything) the to_proc method is called on the symbol. This returns a proc which will call the method with the corresponding name (anything) on the object that is passed as argument.

If you do this with an enumerator like map then you actually call the method on each object in the collection.

Now this shortcut does not allow you to pass an argument to the methods. (+ is just syntactic sugar for calling a method on a number that takes another method as argument)

But you can use currying for such a use case:

add = -> (a, b) { a + b }
add_two = add.curry.call(2)

p [1,2,3,4].map(&add_two) # => [3, 4, 5, 6]

What does to_proc method mean in Ruby?

Some methods take a block, and this pattern frequently appears for a block:

{|x| x.foo}

and people would like to write that in a more concise way. In order to do that they use a combination of: a symbol, the method Symbol#to_proc, implicit class casting, and & operator. If you put & in front of a Proc instance in the argument position, that will be interpreted as a block. If you combine something other than a Proc instance with &, then implicit class casting will try to convert that to a Proc instance using to_proc method defined on that object if there is any. In case of a Symbol instance, to_proc works in this way:

:foo.to_proc # => ->x{x.foo}

For example, suppose you write:

bar(&:foo)

The & operator is combined with :foo, which is not a Proc instance, so implicit class cast applies Symbol#to_proc to it, which gives ->x{x.foo}. The & now applies to this and is interpreted as a block, which gives:

bar{|x| x.foo}


Related Topics



Leave a reply



Submit