Let's Talk About Conditional Expressions
21/Dec 2011
This guest post is by Evan Light a test-obsessed developer, the author of several rarely used gems, and the curator of Ruby DCamp. When he’s not a talking head at conferences, he’s usually working at home as a freelance developer remotely mentoring a developer, working for one or more startups, playing with open source, keeping his wife and four cats company, hacking nonsensically, talking at people on the internet, and/or attempting to lose weight (or any combination of the above). What else do you do when you live 3 hours from civilization?
You know what bugs me? People who don’t write idiomatic conditionals in Ruby.
Many folks, especially those who came out of Java, .NET, or C/C++, use and abuse the ternary. The ternary is a humble little production rule that works like so:
<boolean expression> ? <eval this expression if truthy> : <eval if falsey>
The ternary can be useful if the truthy and falsey cases are terse and well-named. However, it’s common to see multi-line ternaries or, even worse, nested ternaries. For example:
By the way, the above example comes out of Rails.
Accepting that the above is just painful, how could we make it hurt less? Specifically, let’s focus on the nested ternary on Line 7 (accepting that there are other ways that the code can be made clearer).
For starters, let’s DRY up these Hash lookups:
Now let’s unravel this into more idiomatic Ruby. We’ll extract the outer ternary into a functional style if-else. Remember that every expression in Ruby returns the value of its last executed expression. The if-else returns either the value of the computed String in the if or the empty String else. It has the advantage of containing more English words and fewer characters that resemble modem line noise or PERL.
We can get rid of this else. After all, it doesn’t contain any logic. Let’s just make it the default value for method_prefix and then assign a new value only when prefix is truthy.
Ah so what this code really cares about, first and foremost, is whether prefix is falsey. When it’s falsey, it just returns an empty String! Otherwise, if it’s not, it generates a String possibly using the value of prefix if and only if prefix is not true – not a boolean.
So why should we try to avoid ternaries? Because, when used within a complex expression, they tend to lack the clarity of intent of an if-else. The lack of clarity likely resulted in the less DRY previous implementation. Once we introduced more clarity, it became obvious how this code could be DRY’d up, resulting in better readability.
I hope you found this article valuable. Feel free to ask questions and give feedback in the comments section of this post. Thanks!
Subscribe to the waiting list of the free, online “Intermediate Ruby Course“. As a bonus, get a free copy of the “Introduction to Rack” eBook (.pdf format).