cult3

What are Ruby Procs?

May 06, 2015

Table of contents:

  1. What is the difference between a block and a Proc?
  2. Why would you use a Proc over a Block?
  3. Converting between a blocks and Procs
  4. Conclusion

Last week we looked a blocks in Ruby. Blocks are one of the most commonly used, yet often misunderstood parts of the Ruby language.

If you are familiar with other programming languages, a block is basically a way of passing a chunk of code to a method to be evaluated.

However, unlike just about everything else in Ruby, a block is not an object.

If you’ve been exploring Ruby outside of this series, you may have come across the idea of a Proc. A Proc is very similar to a block and so today we will be looking at the similarities and differences between these two Ruby concepts and why you would use one over the other.

What is the difference between a block and a Proc?

So before we jump into the meat and potatoes, first let’s get our heads around what is the difference between a block and a Proc.

Blocks and Procs are basically the same thing, the difference is, a Proc is bound to a variable, whereas a block is just a one off chunk of code that is passed to a method.

For example, imagine we have the following method:

def do_something
  puts 'before block'
  yield
  puts 'after block'
end

When we call this method we can pass a block that will be called by the yield key word:

do_something { puts 'inside the block' }

However, whatever we pass into this method is just a one off chunk of code. We can’t reuse that block or even pass multiple blocks into this method.

A block is basically just part of the syntax of a method.

A Proc on the other hand has the characteristics of a block, but you can save it as an object:

proc = Proc.new { puts 'inside the block' }

If you call the class method you will see that it is an object of class Proc:

proc.class
# => Proc

When you pass a Proc into a method, instead of using the yield keyword, you can instead use the call method:

def do_something(proc)
  puts 'before block'
  proc.call
  puts 'after block'
end

Procs can also except arguments:

greeting = Proc.new { |name| puts "Hello #{name}!" }

greeting.call 'Philip'
# => "Hello Philip!"

greeting.call 'Mary'
# => "Hello Mary!"

Why would you use a Proc over a Block?

So if a block and a Proc are basically the same thing, what are the advantages and disadvantages of using one over the other?

Well firstly, as I mentioned above, because you can save a Proc, that also means you can reuse it:

def do_something(a, proc)
  puts proc.call(a)
end

double = Proc.new { |a| a * 2 }
treble = Proc.new { |a| a * 3 }

do_something(2, treble)

In this example I’ve created two Procs that accept a number and then either multiply it by 2 or multiply it by 3. In this case you can optionally choose which Proc you want to use in the method, or perhaps reuse that same logic by passing it to multiple methods.

Secondly, you can pass multiple Procs to a method, whereas you can only pass a single block:

def do_something(proc1, proc2)
  proc1.call
  proc2.call
end

proc1 = Proc.new { puts 'hello from proc1' }
proc1 = Proc.new { puts 'hello from proc2' }

do_something(proc1, proc1)

This is really useful when you want something to happen before and after the method is called, and it is something you will see regularly in Ruby code.

Converting between a blocks and Procs

Another common thing you will see in Ruby code is using the & operator to convert between blocks to Procs and Procs to blocks.

For example, imagine we have this method:

def do_something(&block)
  block.call
end

When this method is called with a block, the block will be converted into a Proc. You can then use the block as if it were a Proc inside of the method:

do_something { puts 'hello' }

The & character can also be used to convert a Proc into a block. This is commonly used when you want to call a method on an iterator object.

For example, image you had the following code:

colours = %w[red blue yellow]

colours.map { |colour| colour.reverse! }

puts colours

A much shorted and cleaner way of writing this is simply:

colours = %w[red blue yellow]

colours.map(&:reverse!)

puts colours

Conclusion

Procs are yet another important part of the Ruby language that makes it a real pleasure to work with. As we’ve seen in this article, a Proc is basically just a block that has been bound to a variable.

You will likely see Procs used quite a bit if you start to read the source code of popular ruby projects.

Procs and blocks can seem a bit daunting when your first encounter them, especially if you are new to programming in general.

Hopefully this has been a good introduction to the concept. We will be looking at more concrete uses of using Procs in the coming tutorials!

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.