Functional Aspects: They Say Stay Away From Ternary

I remember myself working on a project where ternary operators were used very actively in various situations. The more I read that code, the less comfortable I felt with it, and eventually caught myself on these questions:

  • Why are there so many cases of this syntax construct.
  • Should I (am I expected to) use the ternaries as well for the consistency reasons?
  • Is this code good or not generally speaking?
  • And more importantly, whether I should use ternaries a lot or stay away from them?

The more developers I spoke with, the more diverse perspective I heard. Eventually, I got very confused and made a decision to not use the weird thing (due to the absence of a good framework or a “mental model”). The decision has been followed almost to a degree it becomes a mantra but…

After more than 7 years I had to reevaluate my opinion on the subject. Interestingly, when I spoke to other developers again, I received way more consistent responses. It seems that in the beginning of my career it was hard for me to hear a simple truth. The answer to “Are ternaries awesome or evil?” is as simple or as complex as “It depends“.

Sometimes, during a code review I need to explain the author my position on ternary operator use/misuse in a particular situation. Here’s the summary.

Good Ternary

Let’s imagine we’re writing a TypeScript function that constructs a SearchParameter object for an HTTP POST request to our API

createSearchParameter(searchForm: Form): SearchParameter {
  const isEmployeeBased = searchForm.employeeId.length > 0;
  return <SearchParameter> {
    kind: isEmployeeBased ? SearchKind.employee : SearchKind.report;
    filter: isEmployeeBased ? searchForm.employeeId : searchForm.reportId;
  };
}

This snippet uses two ternary operators. Each makes a decision based on the local constant isEmployeeBased. The first operator assigns the kind of a search request about to be made. The second operator assigns the filter value.

What is good about this code? Well, it is terse — there’s only one locally scoped state-alike constant, namely isEmployeeBased. There is no chance to accidentally reassign this value once the decision is made. The code is short and at the same time hard to break. What else can we wish for?

The ternary operator has shown its superpower when used in a right place in a right way. However, there’s always a way to misuse the construction.

Bad Ternaries

They are so numerous!

If-Else Replacement

This one is absolutely no different from a regular if statement

const searchParameter = new SearchParameter();
isEmployeeBased ? searchParameter.kind = SearchKind.employee : searchParameter.kind = SearchKind.report;
// ...

We’re not gaining much by replacing one syntactical form with another. This is still a flow controlling structure semantically. Then, why not use a regular if-else? (I don’t know an answer to this.)

Non-Trivial Branching

Here’s another way to make it bad

const searchParameter = new SearchParameter();
searchParameter.kind = isEmployeeBased ? SearchKind.employee :
  isReportBased ? SearchKind.report : SearchKind.fullTextSearch;
// ...

No. Just no. Please don’t use ternaries to express branching logic. This introduces a precedent which other developers will refer to when they want to write a five story analogue of this (or some other crazy code we all produce under time pressure). Simplicity on the edge of triviality.

Non-Trivial Branch Logic

const target = condition ? {
  // An extremely mysterious
  // Wall of code describes a
  // Twisted story about
  // Something called "result1"
  return result1;
}: {
  // An alternative story
  // Will explain a curious
  // Reader what are all
  // The secret steps one
  // Need to take to achieve
  // A desirable "result2"
  return result2;
};

This is just another form of an if we saw above. It can be fixed fairly easily by moving the body blocks into separate methods. If the methods are written as pure functions, we’re pretty safe in using them as clauses of the ternary:

const result = isFirstStoryPreferable ?
  this.firstStory() :
  this.secondStory();

It Depends!

When the expression and the branches are trivial (and I mean it); when we assign to a const — say YES to ternary.

When you ask yourself whether the expression or the branches are trivial or not, it’s a great indicator they are too complex already. Stay away from ternary and apply some kind of refactoring.

May the clean code be with you!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s