proctice makes perfect

Ruby Blocks, Procs, and Lambdas
Tuesday, April 21st, 2015

back to blog index

Proc. Oh how I loathe you… until I finally understood you. I have approached my study of Ruby from many angles over the past few months: Learning to Program, The Ruby Programming Language, Team Treehouse, Codecademy, Ruby Monk, and many other internet resources. I made solid progress. Then I finally began The Well-Grounded Rubyist. It was the best guide I’d encountered, so I finally decided to learn with some structure: I would read the entire book, and not move past a topic until I completely understood it. In the past I’d seen procs and had decided to wait. They were described in countless different ways, none of them perfectly clear to me. Finally, it was time to tackle them, and I did. Here’s a summary.

As Rubyists, we’ve all seen this familiar form:

[1, 2, 3].each { |num| puts num }

The part between the curly brackets? That’s a code block. Simple. Array#each passes each item to the code block, and the block does its work. And a block is everything that Ruby needs to make a Proc, so that’s simple too:

p = Proc.new { puts “Hello World!” }

But after that, confusion comes easily . They say everything in Ruby is an object. So the code block in both examples is an object, right? Wrong. The array [1, 2, 3] is the object in the first, and the proc itself is the object in the second. In the first example, the code block is simply part of the syntax of Array#each.

In The Well-Grounded Rubyist, Mr. Black states that a proc is “an object whose job is to provide execution access to a previously-defined code block.” Thus, a proc allows us to encapsulate some code into an object and pass it around. So the proc is an object, but the code block alone is not.

To use the proc (from the second example), we simply call it, and it runs.

p.call # => Hello World!

None of this is too difficult, really. Procs contain previously defined code. The blocks they contain can have arguments, like the first example. These are simple procs, but more complex structures can give the user a lot of power.

But why put the code in an object? Because it can then be used as an argument in another method. The structure for that use confused me at first, so I’ll take it slow.

def example(q)
  puts “Calling the proc”
  q.call # => Hello World!
end

example(p)

If we were to write a method where a proc is expected, then it's just like anything we’ve seen before. We call the method, the argument that comes in can be called, and “Hello World!” is displayed again. But what if we don’t already have a proc object? Here is a syntax that may be seen more commonly, and it can be confusing.

def example(&block)
  block.class # => Proc
  puts “Calling the block”
  block.call
end

example { puts “Hello World!” }

Let’s pretend we’d never made the proc earlier. Here we present the method with a code block. The &block in the parameter list says that if a block is received, turn it into a proc, instead of binding arguments to it. Easy as that. But here’s where most resources make the topic confusing. They call "&block" an argument. It is not… at least in the traditional sense. If you use Method#arity or force an argument error by calling the method with any argument you’re used to using, you’ll see that #example has no parameters. And that’s necessary.

We want Ruby to run predictably across the board, and the same goes here: If we don’t present the method with a block, we of course don’t want an argument error. Blocks should be optional. Once I understood this, everything was much clearer. "&block" is not a parameter like we normally see them. It just allows us to use the block like a variable, and do block.call instead of yield. (“block” can be anything, it’s important to note. We could use “&cookies” and the world would keep spinning.)

If we wanted to use our previously defined “p” in this example, we also have to use the ampersand. So example(&p) would work too - - Again, because this is 0 for 0 for arguments (in error lingo) and the previous was 1 for 1.

So there you have it. Procs allow us to pass around code. Proc.new allows us to create a proc, or the “&” allows us to create one from a code block during a method call. Not too bad. Hopefully my slow journey toward understanding will help someone else, and we can all be better Rubyists together.

...Oh, you were wondering about lambdas too? Fine. Yes you often seem them together, and many resources can make them just as confusing. Deep breath. This could take awhile. Here we go: a lambda is a specific form of a proc. They are more strict about receiving the expected number of arguments, and they handle certain control flow words differently. Exhale. Oh, that wasn’t so bad. Sure, there’s a bit more to it, but if you’re looking for that info, I promise you need a much more detailed guide than this page. That was enough to help us sleep at night.

back to blog index