
Conditionals are, if we’re honest, an embarrassingly large part of most programs deployed in the world. I say “embarrassingly” because a two-year-old understands conditionals, and we as developers often fancy ourselves creators of vast systems of profound, innate, and valuable complexity.
Sometimes our use of conditionals is just for making a series of little form elements easier to use, and sometimes they’re the state-machine at the heart of the system itself. In either case, programs abound with statements that contain at least a passing resemblance to:
if (y < 7)
x = 'red';
else
x = 'green';
It’s practically unavoidable. When we’re all just building state-dependent machines of varied elaborateness and complexity, fundamental state decisions will have to be made. And they’re also a source of a lot of errors and code that can be hard to read and reason about.
What’s a Ternary Operator?
If you’ve been programming for a while but don’t know the term “ternary operator,” it’s likely that you understand what I mean as an “inline if” or “conditional operator.” Ever since CPL, almost every major language has some variation of the ternary operator, ?:
. Here’s the snippet of JavaScript from above, using ternary “if” operators instead of the keywords:
x = y < 7 ? 'red' : 'green';
Basically, the ?
can be read is “was that true? then” and the colon can be read as simply “else” or “if it wasn’t.”
As a sidenote, one of the more interesting cases on the Wikipedia page of the ternary operators is Python, which looks like:
result = x if a > b else y
It’s nice about Python that it totally eschews the ?
and :
. But the long and elaborate argument about whether or not this reads more clearly and fluently than the symbolic form is something I think I’ll steer clear of.
The Case for Ternary Operators
Now given this article’s title, you’re probably expecting me to tear into ternary operators — and I will, at least a bit. But there are reasons to use them:
- Ternary operators save lines — Programmers are vain creatures, so if I can write a compact three line method or a sprawling 10-ish lines, I’d frequently rather the three.
- Ternary operators save characters — Programmers are lazy. Many of us prefer the use of short keywords and method names over longer ones — some languages use the
function
keyword, some are proud todef
— and ternary operators are aligned with that keystroke-saving spirit. Including the curly braces you’d use in many languages, you can save a lot of characters with a?
and a:
instead of aif {} else {}
. It’s like a 200% saving. - Ternary operators show you’re smart — It is, even if you’d rather deny it, part of the appeal of the shorter
?:
syntax: it’s kind of inscrutable to a non-programmer. They’ll probably be able to piece it together after some thought and consideration that “There’s a choice being made by the part at left of the question mark and it’s choices are on either side of the colon.” But it’s not something an average person can automatically read like English.
Now I really do think each of those is a contributing factor to the appeal of the ternary operator. For both vertical and horizontal conciseness, and a bit of demonstrated cleverness, you can’t do a lot better than the clever little ?
and :
combo.
Abbreviations Are Bad
If conciseness is more valuable, I should use a single-line ternary operation. If clarity is more valuable, I should use the English keywords of
if
andelse
.
At the heart of the thesis that ternary operators are bad is the belief that abbreviations, in general, are bad. If you understand what a ternary operator is and you’ve thought about it at all, you’ve realized the choice is this: if conciseness is more valuable, I should use a single-line ternary operation. If clarity is more valuable, I should use the English keywords of if
and else
.
One of the most important questions in programming is who your program is meant for — the writer, a reader, or a computer. Fundamentally if you believe that your program’s primary audience is the hardware on which it runs, then you’ll behave differently than if you believe that the most important audience is the humans that — at least for the foreseeable future — will be charged with making changes to that program.
Abbreviations are great for the humans writing them. It’s easier to type $sq
than $searchQuery
. If you’re optimizing for the act of writing — because once it works, who cares what it looks like? — saving all the keystrokes you can is just the right thing to do. But many thoughtful people agree that programs are more frequently read and understood by humans than they are written by humans, so optimizing for writing seems foolish.
Similarly, if your goal is the most efficient and computer-friendly code in the world, you may sacrifice variable and method names, but you’ll also sometimes favor less-comprehensible methods for getting results because they’re easier or faster for the computer. This isn’t always a good thing, though, as computers behave in ways humans don’t expect. (Perhaps sometime we’ll finally and forever fix that .2 + .1 = 0.30000000000000004 problem…)
The negative case against abbreviations is also an affirmative case for the human reading your program.
The negative case against abbreviations — using fewer letters, using symbols over words, etc — is also an affirmative case for the human reading your program. Whether it’s you in a few days or months, or someone else when you find a new job, or someone trying to help add a feature to your open-source software library, that person probably (because of the hegemonic hold of English on computer programming languages) will be fairly comfortable with English, but probably not be very conversant in the obscure and specific abbreviations used in your system.
Abbreviations — reading, parsing, and reinterpreting them — are a small but non-trivial mental tax you require the people reading your program to pay. That’s why you should be as clear and explicit as you can in all the parts of your program. That’s why you should favor keywords like if
and else
and use special-use characters like ?
and :
carefully.
What Has Been Demonstrated
Just as Dijkstra wasn’t dogmatically against GOTOs, I’m not actually 100% against the use of ?:
ternary operators. It’s just that, even after having programmed regularly for nearly a decade, I sometimes have to stare at them a little longer to remember how to read them and what effect they’ll have on the code that surrounds them. But I’ll use them from time to time, and I think when you’re really realizing some benefit from their concision it’s a great tool to have in your toolbelt.
But that conciseness can be unreadable, an undefendable mistake. Take for example, this snippet:
return (a<b) ? (b<c) ? b : (a<c) ? c : a : (a<c) ? a : (b<c) ? c : b;
Ow! That I’m unequivocally against. Nested conditionals are usually a good sign that something could use a rethink and a refactoring. Nesting ternary operations is tearing off your hands and replacing them with chainsaws. You solve one problem really well — you cut things super effectively; your code is super condensed. But you’re also making everything else harder or impossible (doing anything but cutting with your chain-saw hands, or understanding and reasoning about the state-decision being made).
So much of programming comes down to micro-choices where small things that don’t matter much on their own aggregate into large and consequential choices made by accident.
The point about nested conditionals is also worth emphasizing. Part of the beauty of using if
and else
blocks is that you’re making your conditionals bigger and more annoying. Ternary operators let you hide some of the ugly cyclomatic complexity inside dense and concise code.
But when your use of the ?:
operators is as simple as our first example above, it’s largely a wash. I’d vote against it if we were pair programming together, but if you’d typed it and didn’t ask, I’d probably let it slide.
So much of programming comes down to micro-choices where small things that don’t matter much on their own aggregate into large and consequential choices made by accident. The conciseness of a ternary operator is one of these micro-choices, so always stay aware of where it’s leading you.
I just wanted to say that with one line:
Basically, the ? can be read is “was that true? then” and the colon can be read as simply “else” or “if it wasn’t.”
You finally clarified how I can read these! I tend to agree that if/else is best if readability is the goal, as in WordPress, but the ternary has it’s uses. I just never quite understood how they worked until I read your article. Thanks for that. 🙂
I also tend to favor readability for the same reasons you suggested, when returning back to that code later or someone else has to dive into it, going into a completely condensed shorthand behemoth of code is definitely not the greatest experience. I guess that’s why proper documentation/comments are important too so if readability wasn’t prioritized, for the sleepy developer or the pair of fresh eyes, there can be some explanation as to what’s going on in the code.
Thanks for the good read.
Pingback: Ternary Operators Considered Harmful | WPShout.com
Pingback: 1p – Ternary Operators Considered Harmful | Profit Goals
Pingback: Ternary Operators Considered Harmful
Pingback: Ternary Operators in Javascript aren’t just harmful, they’re selfish and evil | dvolvr
I used to be against them, however found the following things changed that opinion:
1. Single line assignment and declaration
E.g. int index = skipFirst ? 1 : 0;
2. For more complex conditionals or assignments where there’s a strong relationship between both sides
E.g. string title = caption.isShortEnough() ? caption.text : caption.shortText;
I find it’s often far clearer to stick the 3 parts on seperate lines, and this still comes out clearer than the corresponding if statement.
E.g. a = someCondtion()
? someValue1()
: someValue2();
A single assignment statement vs the two you’d need in an if statement generally is easier for me to immediately analyse.
Once you go beyond simple, ifs are better. But simple is best.
Pingback: Sorting and Ordering Nested Data in WordPress: Fun with PHP Arrays and usort() | WPShout
Pingback: Sorting and Ordering Nested Data in WordPress: Fun with PHP Arrays and usort() - The Amazing Host
Pingback: Ternary Operators Considered Harmful – Press Up | BacApa
Pingback: Bacapa No. 2016/02/002