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:

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.

This entry was posted in Ruby and tagged , , . Bookmark the permalink.
  • http://twitter.com/robertodecurnex Roberto Decurnex

    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.

    • http://avdi.org Avdi Grimm

      Yes, I know. I considered mentioning that in the post, but decided it was tangential to the point.

      • http://twitter.com/robertodecurnex Roberto Decurnex

        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. 

    • http://avdi.org Avdi Grimm

      I added an edit and credited you :-)

  • http://twitter.com/robertodecurnex Roberto Decurnex

    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.

  • http://theadmin.org edavis10

    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.

  • Chip Camden

    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.

  • http://twitter.com/skilldrick Nick Morgan

    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).

    • http://avdi.org Avdi Grimm

      Huh, that’s actually an advantage of my convention that I had forgotten
      about. Thanks!

  • Semprebon

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

    • http://avdi.org Avdi Grimm

      I wouldn’t be surprised if the person I learned it from got it from Jim as well.

      • http://ekenosen.net/nick nevans

        I’m fairly certain I heard it from either Jim Weirich or Dave Thomas at the Rails Edge in Reston, VA in 2006. :)

  • http://twitter.com/peterc Peter Çoopèr

    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 ;-)

    • http://avdi.org Avdi Grimm

      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.

  • http://twitter.com/mr_robgleeson Rob Gleeson

    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.

    • http://avdi.org Avdi Grimm

      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 :-)