Ruby: Blocks and Procs
Message from 2022
This post is pretty old! Opinions and technical information in it are almost certainly oudated. Commands and configurations will probably not work. Consider the age of the content before putting any of it into practice.
Ruby has two kinds of anonymous function: blocks, and procs.
- Block
-
A block is a syntactic element. The only things you can do with these are:
- pass them to methods as the “default block” argument
- in a method that’s been called with a default block,
yield
zero or more arguments to it, getting a result - in a method that’s been called with a default block, capture it into a
Proc
with an ampersand in the arguments list
- Proc
-
A
Proc
is an object. You can do just about anything with it, including turn it back and forth from a block.</dd> </dl><h1>Developing with Blocks</h1>Using blocks with somebody else’s API is intuitive:
(1..5).map{|n| n * 3} #=> [3, 6, 9, 12, 15]
Making an API that uses blocks can be easy too:
module Object def with yield self end end # calling "hello".with{|h| puts h} # prints "hello"
If you
yield
more than once, you run the block multiple times.Developing with Procs
How do you even make a
Proc
?doubler = proc{|n| n * 2} #=> #<Proc:0x007f8e938d8618@(irb):1>
There’s a few methods, like
proc
, that turn a block into aProc
.lambda
is very similar. They’re easy to write:def my_proc(&block) block end tripler = my_proc{|n| n * 3} #=> #<Proc:0x007f8e9506d170@(irb):5>
The ampersand in the argument list to
my_proc
is what transforms the block into aProc
, and then we simply return it.You can use the ampersand to turn a
Proc
into a block too:(1..5).map doubler # ArgumentError: wrong number of arguments(1 for 0) (1..5).map &doubler #=> [2, 4, 6, 8, 10]
And you can call a
Proc
directly without having to turn it into a block too:doubler.call 4 #=> 8 doubler[5] #=> 10 doubler.(6) # the dot before the parentheses is significant #=> 12
Blocks and
Proc
s can be confusing. I hope this helps!