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.
-
Weirich’s blog is no longer online but is archived by the Wayback Machine. ↩