Why does Ruby have blocks?

Ruby’s blocks are easily the biggest hurdle most newbies to the language have to overcome. Even for people with years of experience in other languages, the the concept of blocks is often an elusive one at first.

In my opinion, some of the difficulty in grokking blocks can be chalked up to how they are usually explained. In this article, which started out as a post on Parley, I want to explain how I think of blocks: as just a pragmatic shortcut for a pattern you’ve probably already used in other languages.

Ruby, like many languages, lets us create a little anonymous hunk of executable code that can be passed around. There are various ways to do this; for the purpose of this post, let’s pretend there’s only one: proc.

code = proc do |name|
  puts "hello, #{name}"
end

code.call("Avdi")
# >> hello, Avdi

proc is handy, because it is often useful to pass behavior instead of data into methods.

For instance (to use a familiar example), when opening a file, we might pass a proc of code to be executed while the file is open. Then the file will be automatically closed when the proc is finished.

code = proc do
# ...
end
open("myfile", "w", :while_open_do => code)

The behavior that we pass, in the form of procs, into these methods is almost always one-off code. We don’t really have any reason to assign it to a local variable. So more often, we wind up passing it like this:

open("myfile", "w", :while_open_do => proc do
# ...
end)

If you’re more familiar with JavaScript, compare this common JS idiom, where an anonymous function is passed as an argument to a method (thanks to commenter Prasanna):

$( "#target" ).click(function() {
  alert( “Handler for .click() called.” );
});

The more we use methods like this that take behavior arguments as well as data arguments, we realize something: 90% of the time, the method only takes a single behavior argument. There’s something about small methods with single responsibilities that causes them to only have one piece of behavior that needs to be specified from outside.

So we start using a convention: when a method needs to be parameterized with some behavior, we always use the last as the one that takes the “block of code”, or “block” for short.

def open(filename, mode, block)
# ...
end

open("myfile", "w", proc do
# ...
end)

This makes our code a lot more concise for this very common case. However, as we keep doing this, we still have some objections:

  1. We start to see that proc keyword everywhere. It starts to feel a little like noise.
  2. We realize that by passing behavior as an argument, we can almost make things that look like brand new language constructs. But not quite, because there’s always the proc , and there’s always that closing parenthesis after the end .

In fact, that closing parenthesis is a particularly strong irritant in a language that normally doesn’t require us to use parentheses at all.

What if we modified the language a bit to give us some extra support for the “one argument is a block of code” convention? We could just use a special symbol to indicate which one. And then the language could let us start the block of code right after the argument list, so we don’t have to worry about that closing parenthesis. And since we’re making this part of the language syntax, let’s get rid of the requirement for the proc keyword in this special case.

def open(filename, mode, &block)
# ...
end

open "myfile", "w" do
# ...
end

Heck, while we’re at it, let’s make a way to invoke the block of code even if it hasn’t been explicitly specified as a parameter. We’ll use yield to mean “yield control to the block of code that we assume has been passed”.

def open(filename, mode)
# ...
yield
# ...
end

And now we have Ruby blocks. The important thing to remember is that fundamentally, they are just syntactical sugar for a very common idiom: a method, one of whose parameters is a block of code used to pass in behavior.

(If you’re an experienced Ruby programmer, you probably know that blocks have some other semantic peculiarities having to do with control flow. I don’t believe this alters the core premise, however, that blocks are essentially a shortcut.)

One last word: this is totally a plug, but if you dug this article, you might like RubyTapas, where you can level up your Ruby knowledge in two coffee-break-sized nibbles per week.

21 comments

  1. Interesting post, I enjoyed it!

    “If you’re an experienced Ruby programmer, you probably know that blocks have some other semantic peculiarities having to do with control flow.”

    Can you expand on this a little or link to someplace that does?

  2. I explain ruby blocks to newbies in 2 steps:

    1) Ruby blocks are just nameless functions/methods

    2) It’s the same thing you do in javascript where you pass (anonymous) functions as parameters to other function calls. Eg: a button click event:

    $( “#target” ).click(function() {
    alert( “Handler for .click() called.” );
    });

    This nameless fn can be passed directly as is (as in the above case), or stored in a variable and passed, like so:
    handler = function(){alert(“…”);};
    $(“#target”).click(handler);

    In Ruby, a smilar thing would be:
    handler = proc { puts ‘hi’ }
    some_object.some_method(handler)

  3. Very nice explanation, Avdi, clearest I’ve seen since starting to flirt with this fine lady Ruby! I like to hear you on Ruby Rogues as well. This could be a new Koan!
    @Prasanna, nice breakdown of the JS implementations mirroring Ruby’s constructs, which may be more familiar to front-ender’s who are new to Ruby and Rails. FWIW, I’ve always felt the latter JS syntax more readable, if a bit more verbose. Concise isn’t always best.
    Rock on!

    1. Well, technically it’s available to anyone who is willing to spring for 10 bucks a year, which turns out to create a fantastic signal to noise ratio.

      But yeah, that’s why I turned it into an article, so more people would see it.

    2. In addition to your fine explanation, I’d recommend this tutorial by the good old folks at the Pragmatic Programmers: Your Path to Mastering Ruby Blocks | http://pragmaticstudio.com/blog/2014/12/3/mastering-ruby-blocks-step-1
      Thanks again, for your blog, Avdi. All this has helped me tremendously in my changing careers from healthcare to web development. I’ve found my passion in web dev, and the Way lies in my pilgimage along the path to Ruby Enlightenment. “Matz is nice, so we are nice.”

Leave a Reply to Avdi Grimm Cancel reply

Your email address will not be published. Required fields are marked *