10. Blocks and Procs

This is definitely one of the coolest features of Ruby. Some other languages have this feature, though they may call it something else (like closures), but many of the more popular ones do not, which is a shame.

So what is this cool new thing? It's the ability to take a block of code (code between do and end), wrap it up in an object (called a proc), store it in a variable or pass it to a method, and run the code in the block whenever you want (more than once, if you want). So it's kind of like a method, except it isn't bound to an object (it is an object), and you can store it or pass it around like any other object. I think it's time for an example:

toast = Proc.new do
  puts 'Cheers!'
end

toast.call
toast.call
toast.call

I created a proc (I think it's short for "procedure", but the important thing is that it rhymes with "block") that holds a block of code, and then I called the proc three times. As you can see, it looks a lot like a method.

Actually, it's even more like a method than I've shown you, because blocks can take parameters:

doYouLike = Proc.new do |aGoodThing|
  puts 'I *really* like '+aGoodThing+'!'
end

doYouLike.call 'chocolate'
doYouLike.call 'ruby'

Okay, so we see what blocks and procs are, and how to use them, but what's the point? Why not just use methods? Well, it's because there are some things you just can't do with methods. In particular, you can't pass methods into other methods (but you can pass procs into methods), and methods can't return other methods (but they can return procs). This is simply because procs are objects; methods aren't.

(By the way, does any of this look familiar? Yeah, you've seen blocks before... when you learned about iterators. But let's talk more about that in a bit.)

Methods That Take Procs

When we pass a proc into a method, we can control how, if, or how many times we call the proc. For example, let's say there's something we want to do before and after some code is run:

def doImportantThing aProc
  puts 'Everybody just HOLD ON! I have something to do...'
  aProc.call
  puts 'Ok everyone, I\'m done. As you were.'
end

sayHello = Proc.new do
  puts 'hello'
end

sayGoodbye = Proc.new do
  puts 'goodbye'
end

doImportantThing sayHello
doImportantThing sayGoodbye

Maybe that doesn't sound so fabulous... but it is. :-) It is very common in programming to have strict requirements about things that must be done. If you want to save a file, for example, you have to open the file, write the information you want to it, and then close the file. If you forget to close the file, Bad Things(tm) can happen. But every time you want to save or load a file, you have to do the same thing: open the file, do what you really want to do, and then close it. It's tedious and easy to forget. In Ruby, saving (or loading) files works similarly to the code above, so you don't have to worry about anything but what you want to save (or load). (In the next chapter I'll show you how to do things like saving and loading files.)

You can also write methods that determine how many times, or even if, to call a proc. Here's a method that calls a proc about half of the time, and another that calls it twice:

def maybeDo aProc
  if rand(2) == 0
    aProc.call
  end
end

def doTwice aProc
  aProc.call
  aProc.call
end

wink = Proc.new do
  puts '<wink>'
end

glance = Proc.new do
  puts '<glance>'
end

maybeDo wink
maybeDo glance
doTwice wink
doTwice glance

(If you reload this page a few times, you'll see the output change.) These are some of the most common uses of procs, which enable us to do things we simply couldn't do using methods alone. Sure, you could write a method to wink twice, but you couldn't write one to just do anything twice!

Before we move on, let's look at one last example. So far the procs we have passed in have been fairly similar to each other. This time they will be quite different, so you can see how much a method depends on the procs passed into it. Our method will take some object and a proc, and will call the proc on that object. If the proc returns false, we quit; otherwise we call the proc with the returned object. We keep doing this until the proc returns false (which it had better do eventually, or the program will crash). The method will return the last non-false value returned by the proc.

def doUntilFalse firstInput, someProc
  input  = firstInput
  output = firstInput

  while output
    input  = output
    output = someProc.call input
  end

  input
end

buildArrayOfSquares = Proc.new do |array|
  lastNumber = array.last
  if lastNumber <= 0
    false
  else
    array.pop                         # Take off the last number...
    array.push lastNumber*lastNumber  # ...and replace it with its square...
    array.push lastNumber-1           # ...followed by the next smaller number.
  end
end

alwaysFalse = Proc.new do |justIgnoreMe|
  false
end

puts doUntilFalse([5], buildArrayOfSquares).inspect
puts doUntilFalse('I\'m writing this at 3:00 am; someone knock me out!', alwaysFalse)

Okay, that was a pretty weird example, I admit. But it shows how differently our method acts when given different procs.

The inspect method is a lot like to_s, except that the string it returns tries to show you the ruby code for building the object you passed it. Here it shows us the whole array returned by our first call to doUntilFalse. You might also notice that we never squared that 0 on the end of that array, but since 0 squared is still just 0, we didn't have to. And since alwaysFalse was, you know, always false, doUntilFalse didn't do anything at all the second time we called it; it just returned what was passed in.

Methods That Return Procs

One of the other cool things you can do with procs is to create them in methods and return them. This allows all sorts of crazy programming power (things with impressive names like lazy evaluation, infinite data structures, and currying), but the fact is that I almost never do this in practice, nor do I remember seeing anyone else do it in their code. I think it's the kind of thing you just don't end up doing in Ruby, or maybe Ruby just encourages you to find other solutions; I don't know. In any case, I will only touch on this briefly.

In this example, compose takes two procs and returns a new proc that, when called, calls the first proc and passes its result into the second proc.

def compose proc1, proc2
  Proc.new do |x|
    proc2.call(proc1.call(x))
  end
end

square = Proc.new do |x|
  x * x
end

double = Proc.new do |x|
  x + x
end

doubleThenSquare = compose double, square
squareThenDouble = compose square, double

puts doubleThenSquare.call(5)
puts squareThenDouble.call(5)

Notice that the call to proc1 had to be inside the parentheses for proc2 in order for it to be done first.

Passing Blocks (Not Procs) into Methods

Okay, so this has been sort of academically interesting, but also a bit of a hassle to use. A lot of the problem is that there are three steps you have to go through (define the method, make the proc, and call the method with the proc), when it feels like there should only be two (define the method, and pass the block right into the method, without using a proc at all), since most of the time you don't want to use the proc/block after you pass it into the method. Well, wouldn't you know it, Ruby has it all figured out for us! In fact, you've already been doing it every time you use iterators.

I'll show you a quick example, and then we'll talk about it.

class Array

  def eachEven(&wasABlock_nowAProc)
    isEven = true  # We start with "true" because arrays start with 0, which is even.

    self.each do |object|
      if isEven
        wasABlock_nowAProc.call object
      end

      isEven = !isEven  # Toggle from even to odd, or odd to even.
    end
  end

end

['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
  puts 'Yum!  I just love '+fruit+' pies, don\'t you?'
end

# Remember, we are getting the even-numbered elements
# of the array, all of which happen to be odd numbers,
# just because I like to cause problems like that.
[1, 2, 3, 4, 5].eachEven do |oddBall|
  puts oddBall.to_s+' is NOT an even number!'
end

To pass a block to eachEven, all we had to do was stick the block after the method. You can pass a block into any method this way, though many methods will just ignore the block. In order to make your method not ignore the block, but grab it and turn it into a proc, put the name of the proc at the end of your method's parameter list, preceded by an ampersand (&). That part is a little tricky, but not too bad, and you only have to do it once (when you define the method). Then you can use the method over and over again, just like the built-in methods that take blocks, like each and times. (Remember 5.times do...?)

If you get confused, just remember what eachEven is supposed to do: call the block passed in for every other element in the array. Once you've written it and it works, you don't need to think about what it's actually doing under the hood ("which block is called when??"); in fact, that's exactly why we write methods like this: so we never have to think about how they work again. We just use them.

I remember one time I wanted to time how long different sections of my code were taking (this is known as profiling the code). So I wrote a method that takes the time before running the code, runs it, takes the time again at the end, and returns the difference. I can't find the code right now, but I don't need it; it probably looked something like this:

def profile blockDescription, &block
  startTime = Time.now

  block.call

  duration = Time.now - startTime

  puts blockDescription+': '+duration.to_s+' seconds'
end

profile '25000 doublings' do
  number = 1

  25000.times do
    number = number + number
  end

  puts number.to_s.length.to_s+' digits'  # That is, the number of digits in this HUGE number.
end

profile 'count to a million' do
  number = 0

  1000000.times do
    number = number + 1
  end
end

How simple! How elegant! With that tiny method, I can now easily time any section of any program that I want to; I just throw the code into a block and send it to profile. What could be simpler? In most languages, I would have to explicitly add that timing code (the stuff inside profile) around every section I wanted to time. In Ruby, however, I get to keep it all in one place, and (more importantly) out of my way!

A Few Things to Try

  • Grandfather Clock. Write a method that takes a block and calls it once for each hour that has passed today. That way, if I were to pass in the block do puts 'DONG!' end, it would chime (sort of) like a grandfather clock. Test your method out with a few different blocks (including the one I just gave you). Hint: You can use Time.now.hour to get the current hour. However, this returns a number between 0 and 23, so you will have to alter those numbers in order to get ordinary clock-face numbers (1 to 12).
  • Program Logger. Write a method called log, which takes a string description of a block and, of course, a block. Similar to doImportantThing, it should puts a string telling that it has started the block, and another string at the end telling you that it has finished the block, and also telling you what the block returned. Test your method by sending it a code block. Inside the block, put another call to log, passing another block to it. (This is called nesting.) In other words, your output should look something like this:
    Beginning "outer block"...
    Beginning "some little block"...
    ..."some little block" finished, returning:  5
    Beginning "yet another block"...
    ..."yet another block" finished, returning:  I like Thai food!
    ..."outer block" finished, returning:  false
    
  • Better Program Logger. The output from that last logger was kind of hard to read, and it would just get worse the more you used it. It would be so much easier to read if it indented the lines in the inner blocks. To do this, you'll need to keep track of how deeply nested you are every time the log is called. To do this, use a global variable, which is a variable that you can see from anywhere in your code. To make a global variable, just precede your variable name with $, like these: $global, $nestingDepth, and $bigTopPeeWee. In the end, your logger should output code like this:
    Beginning "outer block"...
      Beginning "some little block"...
        Beginning "teeny-tiny block"...
        ..."teeny-tiny block" finished, returning:  lots of love
      ..."some little block" finished, returning:  42
      Beginning "yet another block"...
      ..."yet another block" finished, returning:  I love Indian food!
    ..."outer block" finished, returning:  true
    

Well, that's it for this tutorial. Congratulations! You've learned a lot. Maybe you feel like you don't remember any of it, or maybe you skipped over some parts... Relax. Programming isn't about what you know; it's about what you can figure out. As long as you know where to find out the things you forget, you're doing okay. I hope you don't think I wrote all this without looking things up every minute! Because I did. I also got a lot of help with the code examples in this tutorial. But where was I looking everything up, and who was I asking for help? Let me introduce you...