Semantic style Ruby blocks

Here’s another blog post vouching for writing Ruby blocks using the semantic rule. Sometimes called “the Weirich rule” because of Jim Weirich’s “Braces vs. Do/End” article1 and an issue he filed against rubocop/ruby-style-guide. The rule unambiguously answers the question posed by Weirich: “When should you use {} for blocks and when should you use do/end?” His answer:

  • Use braces ({}) for blocks that return values.
  • Use keywords (do/end) for blocks executed for side effects.

An explanation

Let’s say we were iterating over some movies in order to print out their titles:

films = [
  "A Tale of Springtime",
  "A Tale of Winter",
  "A Summer's Tale",
  "A Tale of Autumn"
]

films.each do |title|
  puts title
end

An #each block only ever executes for side effects and never returns a value we can do stuff with. If we wanted to pre-format our list of films, we would need to iterate in such a way that mutates our film list (or creates a new list) before printing the films out:

films.map! { |title|
  title.upcase
}

films.each do |title|
  puts title
end

At a glance, a reader can see two different types of blocks performing two different functions: one mutates data; the other is executed for side effects (printing out a list of films). An author not using the semantic rule might have written this:

films.map! { |title| title.upcase }
films.each { |title| puts title }

Or this:

films.map! do |title|
  title.upcase
end

films.each do |title|
  puts title
end

Both of these are readable but do not communicate intent as clearly as the semantic style blocks. My primary goal when authoring code is to communicate my intent to other readers, so I appreciate the ability to be as precise in as few characters as possible.

Criticisms of the style

To me, there are two compelling reasons to be critical of semantic style blocks: the readability of do/end one-liners; and the inability of automated tools to format Ruby code consistently based on the semantic rule. I can agree that, sometimes, one-liners meant to execute side effects would make reading code more difficult:

films.each do |title| puts title end
# is less readable than:
films.each { |title| puts title }

But Ruby is so expressive that, in the off-chance you need to write a do/end one-liner, I’m confident there are other ways of achieving better readability without ditching the rule:

films.each do |title| puts(title) end

As for auto-formatting Ruby code while abiding by the semantic rule, this seems like a solvable problem in the long term. The “problem” is that Ruby is “too” expressive, and the tooling would have to understand a lot about the program and the author’s intent to reformat to braces or do/end correctly.

There is an interesting issue filed against standardrb discussing this. And, as of 1.0.0, standardrb has a relaxed, “let the author choose” approach to this block syntax. If you’re developing an application and your Ruby code is auto-formatted, I hope you will still choose to be intentional whenever you’re writing not-auto-formattable code.

  1. Weirich’s blog is no longer online but is archived by the Wayback Machine