The Procedure/Function Block Convention in Ruby

Ruby lets you enclose blocks in either {...} or do...end delimiters. Which you choose is a matter of style.

There are two conventions that I know of for deciding which form to use.  The one I see people using most often these days is the “line count” convention: curly brackets for one-liners, do...end for multiline statements. E.g.:

objects.each {|o| o.frobnicate!}

objects.each do |o|
  o.fold!
  o.spindle!
  o.mutilate!
end

The alternative—and the convention I’ve been using for a while—is based on the semantic intent of the block, rather than on its length. It can be stated like this:

  • Use curly brackets for functional blocks, where the primary purpose of the block is to return a value.
  • Use do...end for procedural blocks, where the primary purpose of the block is its side-effects.  That is, the block is intended to change the state of the system in some way or to perform output.

Here are some examples of functional blocks:

# make a new array, with the contents of the old array stringified and whitespace trimmed
arr2 = arr.map{|x| x.to_s.trim }

# Read from the world, but don't change the world
options = open('options.yml') { |f|
  YAML.load(f)
}

# Generate some XML and assign it to a variable
xml = Nokogiri::XML::Builder.new { |xml|
  xml.root {
    xml.products {
      xml.widget {
        xml.id_ "10"
        xml.name "Awesome widget"
      }
    }
  }.to_xml

# Hash fetch with default value
err_count = stats.fetch(:error_count) { 0 }

And here are some examples of procedural blocks:

# Change an array in place
arr.map! do |x| x.to_s.trim end

# Write output to a file
open("$0.pid", 'w+') do |f|
  f.write($$.to_s)
end

# This generates a global routes map as a side-effect
Rails.application.routes.draw do
  resources :users
  # ...
end

# Generates a test_* method and adds it to the suite
test "should return the square of its input" do
  assert_equal(4, foo(2))
end

# Hash fetch with required key
out = options.fetch(:output) do 
  raise ArgumentError, "An output option must be specified"
end

I can’t take credit for this convention. I learned it from someone else (I want to say Nick Evans?). I’m sure there have been other blog posts about it, but I haven’t seen anyone talking about it recently.

I’m not going to tell you this is the way you should write your code. Nor am I perfectly consistent; there are a few cases where I use curly brackets for statements that are technically procedural, simply because I think it reads a lot better. Notably, for certain RSpec constructs:

describe "a user with a name" do
  subject{User.new(:name = "Bob")} 
  it { should be_valid }
end

But by and large I’ve been pretty happy with this convention, and I think it has some advantages.

  • It adds a visual cue about the intent of the code that wouldn’t otherwise be there.
  • I never liked switching back and forth between curlies and do...end just because I added or removed a little bit of code. Granted, editor macros can make this easier, but it just felt like arbitrary extra work.
  • The use of do...end feels like a procedural, imperative statement to me. Functional blocks that use do...end don’t read quite as well in my eyes.
  • EDIT As Nick Morgan points out below, chaining method calls onto an end looks kinda weird (do ... end.foo). Since it’s rare to want to chain method calls onto a procedural block, that’s not an issue for code that uses this convention.

So there ya go, I just thought I’d toss that out for anyone who hadn’t been exposed to it. Comments pro or con are welcome… or if you have another convention for block syntax that I didn’t list above, I’d love to hear about it.

EDIT: As Roberto Decurnex correctly points out below, curly bracket blocks also have a different parser precedence. Regardless of which block convention you adopt, always remember to put your method parameters inside parens if calling the method with a curly block.

EDIT: It occurred to me that there’s actually a third convention I know of: “pick one form and stick to it”. But I haven’t seen that one in the wild lately.

EDIT THE THIRD: Revisiting this post, I’ll add that everyone I know who uses this convention, including James Gray, traces their use back to Jim Weirich, so I’ve taken to calling it the “Weirich convention”. Here’s Jim posting about it all the way back in 2004.

22 comments

  1. avdi, it’s not just about style. It’s about precedence in some edge cases :S

    some_method other_method { … }

    won’t be the same as

    some_method other_method do … end 

    {} will be taken as the other_method block while do end will be taken as the some_method block.

      1. Yep, you are right, is really out of the scope of the post. I was not able to avoid the noise of  “Which you choose is a matter of style.” :p.

        Btw, I really like this convention. It’s easier to get some sense of the block purpose in advanced. 

  2. avdi, it’s not just about style. It’s about precedence in some edge cases :S

    some_method other_method { … }

    won’t be the same as

    some_method other_method do … end 

    {} will be taken as the other_method block while do end will be taken as the some_method block.

  3. I like that distinction. I’ve always tried to follow the {} for 1 liners and do/end for multi-line but you’re right about code expanding over time and having to convert {}s to do/ends.

  4. The functional vs. side-effects convention has a parallel in Haskell:  in a “do” block, a monad gets passed from one statement to the next, which is most often used to create side-effects (e.g., I/O).  That enhances, in my mind, the signal your convention offers.

  5. I generally go for the oneliner brace, multiliner do/end, but I’ve found if I’m calling a method on the return value of the block, I prefer braces (because end.do_something looks weird to me).

  6. I first heard of the intent-based approach (which I also use) from Jim Weirich at (I think) the first  RailsConf .

  7. I find that second approach bizarre. do .. end isn’t good news on a one-liner and the distinction in functionality is already made in the method name.

    For me it’s more a stylistic issue. Like the differences between embedded and multi-line quotations in written English. But let it not be said that English isn’t my first love, well above and beyond Ruby 😉

    1. The distinction in functionality isn’t made in the method name with any kind of reliability. The obvious example is any resource-controlling block like open() { ... }. But you also reminded me to add a Hash#fetch example to both blocks above, another example of where block intent can be either procedural or functional.

  8. I’m sorry, but I find some of those examples absolutely horrible :-/ 

    using { } to denote multi-line blocks feels very unlike Ruby to me, and I recently inherited an old Rails application that does the exactly same thing as you, although I don’t think the person who wrote blocks like that had any reason to, he just preferred to, and he did it everywhere, at every opportunity. 

    I find it harder to read(as in to see the beginning and end), plus to be very aesthetically unpleasing.

    1. I’ve since confirmed that some other old-line Rubyists, such as Jim Weirich and James Edward Gray, also use the procedure/function block convention. So perhaps it’s not that the code is unlike Ruby, but unlike Rails 🙂

  9. I hadn’t really heard of this style till recently, and I hate it. I think multiline braces look very un-Rubyish, and I’m perfectly happy to chain things off end. More problematic, though, is that this convention makes an artificial distinction that isn’t enforced by the interpreter. Since it isn’t enforced by the interpreter, it cannot be guaranteed to be accurate, and therefore is of no use (or perhaps negative use) as a clue of the block’s purpose. That is, it’s no better than comments.

    I’ve never had a problem determining by inspection whether a block was being used for its side effects or not. Have you?

      1. Don’t jump to conclusions. I have used multiline braces in Ruby (when a lambda was required, and before the new lambda syntax existed). I did so only because I had to, and I recall thinking at the time how inconsistent it was with every other bit of Ruby code I’d ever written or seen.

Leave a Reply

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