Getting to Grips with Blocks II

In the last article we had a whistle stop tour of blocks. How they are defined, what purposes they serve and the differences between various Ruby versions. It was all very academic. But practically what benefit do we actually get from blocks. What makes them such a powerful tool for Ruby developers.

Procs and Lambdas

Before we get into some useful examples of blocks, we have to discuss Proc and lambda. These two allow you to define a block for later use using the call method.

These “callable objects” are intended for use down the line in our applications execution. To drive the point home we need an example. Anything to do with money springs to mind, exchange rates, rates for different clients, discounts and so on.

def discount( rate )
  Proc.new { |n| n - ( n * rate ) }
end

ten_percent_off = discount(0.1)
twenty_percent_off = discount(0.2)

ten_percent_off.call(100) # => 90.0
twenty_percent_off.call(60) # => 48.0

In the above example, we define a method discount that accepts a rate and returns a Proc object. No code is executed (well nothing is returned) all we have done is set up a little environment that takes a percentage off). We setup up two discounts and call them on the values 100 and 60 using call. Just so you know now the discount method would work just as well written using a lambda:

def discount( rate )
  lambda { |n| n - ( n * rate ) }
end

Why Two Ways to Define a Proc?

Proc and lambda have subtle differences. From the previous example we see the block accepts a parameter n, the lambda is pretty strict about these parameters, if we called it incorrectly like so:

ten_percent_off.call(10, "This should not be here")
# => ArgumentError: wrong number of arguments (2 for 1)

The Proc doesnt mind this sort of thing, it simply ignores the additional parameters. Similarly the lambda will complain if we do not pass enough parameters, the Proc again doesn’t mind and just assigns the excess parameters to nil. It sounds like Proc is a nice and more flexible way of doing things, however I rarely use Proc.new it’s almost always lambda, why? Well, 90% of the time I like airity (number of parameters) to be enforced and a horrible error thrown if I do something wrong. If that reason seems weak, then we can consider how Proc and lambda handle return.

def hello_me
  hiya = Proc.new { return "Hiya" }
  hiya.call
  puts "Woe is me, I will never fulfill my purpose"
end

hello_me
#=> Hiya

# Using a lambda
def hello_me
  hiya = lambda { return "Hiya" }
  hiya.call
  puts "Finally I fell like I have contributed"
end

hello_me
Finally I fell like I have contributed

As seen the Proc returns from the scope in which it was called, and the lambda returns from the block, allowing our final message line to fulfill its purpose. I am not saying “pick one, and never use the other”, far from it. Knowing the implications of both simply allows us to use the correct definition when appropriate. Still I do use lambda a heck of a lot more.

Practical Blocks

So now we want to look at how we can use these blocks in a practical way that is more than just iteration that we are used to.

Simplifying interfaces

The first thing that springs to mind is a technique I use on factory methods. A factory being a piece of code that returns an object based on it’s inputs. A good example would be producing text for a label print.

class AddressLabel
  def initialize
    @lines = []
  end

  def self.generate &block
    label = AddressLabel.new
    label.instance_eval(&block)
    label.print
  end

  def print
    @lines.each do |line|
      puts line
    end
  end

  def address(lines)
    lines.each do |line|
      @lines << line
    end
  end
end

AddressLabel.generate do
  address ["Lets Be Avenue", "Glasgow"]
end

# Another way of creating the same label would be ...
label = AddressLabel.new
label.address ["Lets Be Avenue", "Glasgow"]
label.print

Here the instance_eval evaluates the block within an instance of AddressLabel. Both methods of producing the label do the same thing only using the block format is way sexier as well as making it more natural to read. No doubt you will have seen this type of interface before in many of the gems available.

Setup and Tear Down

Another common use for blocks is pre and post processing. The most common example on the web is probably Ruby’s standard library for File.open

File.open('testfile', 'w') do |f|
  f.write 'Some text'
end

file = File.open('testfile2', 'w')
file.write 'More text'
file.close

The second example is probably what you will be used to with PHP, but we can see the nice block implementation takes care of closing the file behind us. You can apply this kind of implementation anywhere tear down actions are involved. I remember writing some php code that created PDF files. The code was something like

try {
  $pdf = new PDFlib();

  if ($pdf->begin_document("file_name", "") == 0) {
    die("Error: " . $pdf->get_errmsg());
  }

  $pdf->show(“hello world”);
  $pdf->end_page_ext(“”);
  $pdf->end_document(“”);
} catch (Exception $e) {
  exit();
}

This simply opens a new file writes some text and closes it again, there is some very basic exception handling to exit the script if things go awry. It was never meant to be the most elegant piece of code. However as we have such elegant examples of interfaces in the Ruby standard library, writing such code would feel just plain old “dirty” in Ruby.

It could be cleared up a lot in PHP (even more so with PHP anonymous functions) but the tools at our disposal pre PHP5.3 were just not up to the job of extracting all this open document / close document chores without wrapping up the PDFlib library.

So what about setup? Everything we just discussed was post processing so what benefit do blocks provide on setup. When creating methods that start to use a lot of conditional logic to determine the outcome I look at passing it blocks.

def more_than_ten_rows? rows
  if rows.length > 10
    yield rows
  end
end

more_than_ten_rows? (1..10).to_a do |row|
  puts row
end

So with this simple piece of code more_than_ten_rows? acts as a gate, allowing the block to run when we have more than the required number of rows. The alternatives would be to move the conditional logic to the presentation, or the presentation to the logic, using the block we have achieved some basic separation.

Wrapping up

I think it’s fair to say unless you get a good grip on blocks when learning Ruby you will never really fulfil your potential. It’s common to hear “blocks are everywhere” and it’s true, so you will be missing the party if you don’t get some quality time with them.

They are there to help us developers trivially fulfill code in a succinct clean way, using them will become more natural over time and once you get to that stage you will wonder how you got by without them.

It’s definitely worth using the PHP equivalent, I never used them (even though I had 5.3 on the production environment) until I came to writing this article, the syntax is more verbose than Ruby, but, hey, its a pretty decent trade off and I fully believe that understanding this relatively new addition in PHP will help you grasp a core fundamental in Ruby.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • http://www.matsinopoulos.gr Panayotis Matsinopoulos

    Hi,

    Thanks for this quite nice article. I believe, though, that you need to expose more the benefits that you are trying to expose.

    For example, on the ‘more_than_ten_rows?’ example, you are saying that you are achieving separation of logic from presentation, which is correct. However, the benefit of the example, i.e. of the ‘more_than_ten_rows? ….’ implementation is that you can call it multiple times with different piece of code/blocks without repeating the condition logic. You can do quite several different things when ‘more_than_ten_rows?’ condition is true, each thing being separately coded and condition defined only once. That’s the real benefit of it.

    BR
    Panayotis