Erik Trautman logo crest shield seal

Erik Trautman

Technical entrepreneurship, business strategy and product development

Ruby Explained: Blocks, Procs, and Lambdas, aka "Closures"

This post will explain blocks and Procs, with which you're probably at least somewhat familiar, but also some lesser known Ruby closures like Lambdas and Methods

One of the most confusing parts of learning basic Ruby (until your AHA! moment) is understanding what blocks are and how they work, mostly because it's something you probably haven't ever seen before trying out Ruby. It shouldn't be, because they're actually pretty simple. You HAVE already seen them before... they are commonly used as inputs to some of the iterators you've no doubt worked with like each or map.

Here, you'll learn more about blocks and also about their lessor known cousins, Procs, lambdas and Methods. By the end, you should be comfortable working with blocks and writing your own methods that take them. You should understand when you may need to use a Proc instead and the basics of the other two options -- lambdas and Methods.

Blocks are just chunks of code that you can pick up and drop into another method as an input. They're often called "anonymous functions" because they have no name but behave much like functions. They're like little helper functions... you don't find blocks just hanging around without some method (like #each) using them.

You declare a block using squiggly braces {} if it's on one line or do ... end if it's on multiple lines (by convention... you can use either one if you really want):

> [1,2,3].each { |num| print "#{num}! " }
1! 2! 3! =>[1,2,3]
> [1,2,3].each do |num|
>    print "#{num}!"
> end
1! 2! 3! =>[1,2,3]         # Identical to the first case.

Just like methods, some blocks take inputs, others do not. Some return important information, others do not. Blocks let you use the implicit return (whatever's on the last line) but NOT return, since that will return you from whatever method actually called the block.

Blocks are used as arguments to other methods (like #each), just like the normal arguments that you see between the parentheses... they just happen to always be listed last and on their own because they tend to take up multiple lines. Don't think of them as anything too special. The #each method isn't special either, it's just built to accept a block as an argument.

How does #each take a block then? Through the magic of the yield statement, which basically says "run the block right here". When you write your own methods, you don't even need to specially declare that you'd like to accept a block. It will just be there waiting for you when you call yield inside your method.

yield can pass parameters to your block as well. See this made-up version of the #each method to get an idea of what's happening under the hood. We'll put this method into the Array class so you can call it directly on an array (like [1,2,3].my_each) instead of having to take the array as an argument like my_each([1,2,3]):

class Array 
  def my_each
    i = 0
    while i < self.size
        yield(self[i])  
        i+=1      
    end
    self
  end
end

As you can see, we iterate over the array that our #my_each method was called on (which can be grabbed using self). Then we call the block that got passed to #my_each and pipe in whatever member of the original array we are currently on. Last, we just return the original array because that's what #each does. We would run it just the same way as #each:

> [1,2,3].my_each { |num| print "#{num}!" }
1! 2! 3! => [1,2,3]

Which operates in that case just like all these lines:

class Array
  def my_each
    i = 0
    while i < self.size
        print "#{self[i]}!"   # Our block got "subbed in" here
        i+=1
    end
    self
  end
end

So one reason blocks are great is because you can write a sort of generic method like #each which wraps your block in code that says what to do with it. Another use case is when creating methods where you want to optionally be able to override how they "work" internally by supplying your own block -- #sort lets you supply your own block to determine how to actually order the items of the array if you want to!

If you want to ask whether a block was passed at all (to only yield in that case), use #block_given?, or rather: yield if block_given?

A lot of beginners just blindly take it on faith that #each and #map and #select etc. all work the way they do. You're more skeptical than that, which is good. They're really quite simple and you'll get a chance to build your own soon enough.

What if you want to pass TWO blocks to your function? What if you want to save your block to a variable so you can use it again later? That's a job for Procs, aka Procedures! Actually, a block is a Proc (which is the class name for a block) and they rhyme just to confuse you. The block is sort of like a stripped-down and temporary version of a Proc that Ruby included just to make it really easy to use things like those #each iterators.

A Proc is just a block that you save to a variable, thereby giving it a bit more permanence:

> my_proc = Proc.new { |arg1| print "#{arg1}! " }

Use that block of code (now called a Proc) as an input to a function by prepending it with an apersand &:

> [1,2,3].each(&my_proc)
1! 2! 3! =>[1,2,3]

It's the same as passing the block like you did before!

When you create your own function to accept procs, the guts need to change a little bit because you'll need to use #call instead of yield inside (because which proc would yield run if you had more than one?). #call literally just runs the Proc that is called on. You can give it arguments as well to pass on to the Proc:

> my_proc.call("howdy ")  # edit: note that this is the same `my_proc` as above
howdy => nil

Most of the time, using a block is more than sufficient, especially in your early projects. Once you start seeing the need for using a Proc (like passing multiple arguments or saving it for later as a callback), you'll have Procs there waiting for you.

Blocks and Procs are both a type of "closure". A closure is basically a formal, computer-science-y way of saying "a chunk of code that you can pass around but which hangs onto the variables that you gave it when you first called it". It's the blanket term used to refer to blocks and Procs and...

There are two other similar closures to be aware of but about which you certainly don't need to be an expert because they're used in less typical applications. The first of these is a lambda. If Procs are sort of a more-fleshed-out version of blocks, then lambdas are sort of a more-fleshed-out version of Procs. They are one step closer to being actual methods themselves, but still technically count as anonymous functions. If you're coming from Javascript, anonymous functions shouldn't be anything new to you.

Just to focus on the differences between lambdas and Procs, a lambda acts more like a real method. What does that mean?

  • A lambda gives you more flexibility with what it returns (like if you want to return multiple values at once) because you can safely use the explicit return statement inside of one. With lambdas, return will only return from the lambda itself and not the enclosing method, which is what happens if you use return inside a block or Proc.
  • Lambdas are also much stricter than Procs about you passing them the correct number of arguments (you'll get an error if you pass the wrong number).

Here's a simple example to show you the syntax of a lambda (btw, there's nothing special to lambdas about placing the #call after the end, if you hadn't seen that done before, it's just like method chaining):

> lambda do |word| 
>   puts word
>   return word            # you can do this in lambdas not Procs
> end.call("howdy ")
howdy => "howdy "        # not nil because we gave it a return

The second additional closure is called a Method because, well, it's the closest of the four (blocks, Procs, lambdas, and Methods) to an actual method. Which it is. "Method"'s (capitalized because they're actually a class of their own) are really just a convenient way to pass a normal method to another normal method by wrapping the symbol of its name in the word method() So what? To use the same example as we have been so far:

Edit: Note that #my_each has been modified for this example to now take an argument, which the standard #each does not. We're using #my_eachbelow.

class Array
  def my_each(some_method)
    i = 0
    while i < self.size
      some_method.call(self[i])
      i+=1
    end
  end
  self
end

def print_stuff(word)
  print "#{word}! "
end

> [1,2,3].my_each(method(:print_stuff))    # symbolize the name!
1! 2! 3! => nil

TADA! We've now gotten our worthless array (and a "howdy") printed out 4 different ways, each seemingly less useful than the last! Fear not. Learn your blocks cold and have a good handle on Procs (which should be easy since they're basically the same thing) but just keep lambdas and Methods in the back of your head for much later.

So...

  • Blocks are unnamed little code chunks you can drop into other methods. Used all the time.
  • Procs are identical to blocks but you can store them in variables, which lets you pass them into functions as explicit arguments and save them for later. Used explicitly sometimes.
  • Lambdas are really full methods that just haven't been named. Used rarely.
  • Methods are a way of taking actual named methods and passing them around as arguments to or returns from other methods in your code. Used rarely.
  • Closure is just the umbrella term for all four of those things, which all somehow involve passing around chunks of code.


The "Ruby Explained" posts are designed to be a sort of "In-Plain-English" version of key Ruby concepts which are usually covered in other introductory texts but rarely for free and often incompletely. When I'm learning a new thing, I usually want someone to explain it to me like I'm a five year old because that's the best way to make sure nothing gets missed. This is my attempt to pass that same sentiment on to you. Let me know if there's anything I can improve.

If you're just getting interested in this stuff, check out The Odin Project for a free curriculum to learn web development.

Tags:   Ruby Explained