Quantcast
Channel: Press Up
Viewing all articles
Browse latest Browse all 101

Ternary Operators Considered Harmful

$
0
0

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 to def — 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 a if {} 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 and else.

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.

The post Ternary Operators Considered Harmful appeared first on Press Up.


Viewing all articles
Browse latest Browse all 101

Latest Images

Trending Articles



Latest Images