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 usedo...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.
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.
Yes, I know. I considered mentioning that in the post, but decided it was tangential to the point.
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.
I added an edit and credited you 🙂
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.
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.
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.
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).
Huh, that’s actually an advantage of my convention that I had forgotten
about. Thanks!
I first heard of the intent-based approach (which I also use) from Jim Weirich at (I think) the first RailsConf .
I wouldn’t be surprised if the person I learned it from got it from Jim as well.
I’m fairly certain I heard it from either Jim Weirich or Dave Thomas at the Rails Edge in Reston, VA in 2006. 🙂
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 😉
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 aHash#fetch
example to both blocks above, another example of where block intent can be either procedural or functional.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.
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 🙂
Unfortunately Jim’s post has been taken down. Luckily, the wayback machine has archived it: https://web.archive.org/web/20140221124509/http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc
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?
“I think multiline braces look very un-Rubyish”. Well yes, if you have never used them, then you would think that, wouldn’t you.
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.
The link you have towards Jim Weirich post (http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc) where he proposed this throws a 404. I was hoping to convince a friend to use this convention with that post, but sense this is the next most popular post for it, it’s now my go to!
http://imgur.com/ABBrmcc