DEV Community

Cover image for Rethinking JavaScript: The if statement
JavaScript Joel
JavaScript Joel

Posted on • Edited on

Rethinking JavaScript: The if statement

Thinking functionally has opened my mind about programming. It has given me a greater depth of understanding of code. It has also led me on a quest in which I found myself questioning many of the core features of the language.

I have recently been questioning the if statement itself.

Having written an entire application completely void of a single if statement, I have found if to be optional.

If you are unfamiliar with functional programming this probably sounds like the rantings of a mad man, but stick with me a bit and allow me to explain.

Learning multiple ways to solve the same problem amplifies your mental toolkit by magnitudes.

First, meet the if statement replacement, the ternary operator:

condition ? expr1 : expr2
Enter fullscreen mode Exit fullscreen mode

Functional programming has taught me to write small functions like this:(don’t yell at me for not currying, that’s for another article)

const add = (x, y) => x + y
Enter fullscreen mode Exit fullscreen mode

The if statement doesn’t fit into this type of function, but the ternary operator does!

const isGreaterThan5 = x => x > 5 ? 'Yep' : 'Nope'
Enter fullscreen mode Exit fullscreen mode

Soon I began to find almost every instance of an if statement could be replaced with an equivalent ternary operation.

// typical code you might stumble upon
function saveCustomer(customer) {
  if (isCustomerValid(customer)) {
    database.save(customer)
  } else {
    alert('customer is invalid')
  }
}

// ternary equivalent
function saveCustomer(customer) {
  return isCustomerValid(customer)
    ? database.save(customer)
    : alert('customer is invalid')
}

// ES6 style
const saveCustomer = customer =>
  isCustomerValid(customer)
    ? database.save(customer)
    : alert('customer is invalid')
Enter fullscreen mode Exit fullscreen mode

Then I started to think how the ternary operator could fit into an else-if statement. I was able to do this with a nested ternary, but it was really messy and not easy to understand.

// old school else-if
function customerValidation(customer) {
  if (!customer.email) {
    return error('email is require')
  } else if (!customer.login) {
    return error('login is required')
  } else if (!customer.name) {
    return error('name is required')
  } else {
    return customer
  }
}

// ES6 style custom formatted ternary magic
const customerValidation = customer =>
  !customer.email   ? error('email is required')
  : !customer.login ? error('login is required')
  : !customer.name  ? error('name is required')
                    : customer
Enter fullscreen mode Exit fullscreen mode

Now you can clearly see all the conditions defined on the left hand side and the values returned on the right side.

Even though it is possible to replace every if statement with a ternary operator, I am not saying you should do so. This is just another tool for you to use and consider.

But Why?

We are not just eliminating the if, but also eliminating statements and blocks in favor of expressions. While there is nothing "functional" about doing this. Learning to code without statements or blocks is one of multiple pre-functional steps that will allow you to write more functional code.

A note on readability

There is a direct correlation between "readability" and "exposure".

An example of this is seeing a new word for the first time like 'perfidious'. Having never seen it, you might have to read each letter and sound it out in your head. You might also prefer to use it's synonym 'disloyal', which you feel is "easier". After all, 'disloyal' is the word that you grew up with and have been using your whole life.

But after you have sounded the word out a couple times, know the definition and started using it in a few sentences, it will start to become second nature. You can now quickly glace at the word 'perfidious', pausing no longer than you would when reading other words as it becomes more and more familiar with time.

And now that you have added more words to your vocabulary, you can more effectively communicate because you have more tools to pull from your linguistic toolbox.

The structure of this ternary also mimics the structure of Ramda's cond which also has the conditions on the left and the actions on the right, so using one should make you more familiar with the other.

var fn = R.cond([
  [R.equals(0),   R.always('water freezes at 0°C')],
  [R.equals(100), R.always('water boils at 100°C')],
  [R.T,           temp => 'nothing special happens at ' + temp + '°C']
]);
fn(0); //=> 'water freezes at 0°C'
fn(50); //=> 'nothing special happens at 50°C'
fn(100); //=> 'water boils at 100°C'
Enter fullscreen mode Exit fullscreen mode

Even Mozilla suggests:

By breaking the ternary onto multiple lines and making use of extra whitespace, the ternary operator can be used to very cleanly replace a lengthy series of if/else statements. This provides a syntactically light way of expressing the same logic:

I promise, readability will come with time.

End

This Rethinking JavaScript article is just one in a series. More to come in the near future!

I would love to hear your feedback and thoughts on how you would rethink theif statement. Do you have a scenario where you are unsure how this will work? Tell me, let me know!

Continue to Part 2 Rethinking JavaScript: Death of the For Loop.

I know it’s a small thing, but it makes my day when I get those follow notifications here and Twitter (@joelnet). Or if you think I’m full of shit, tell me in the comments below.
Cheers!

Originally posted here: https://hackernoon.com/rethinking-javascript-the-if-statement-b158a61cd6cb

Top comments (28)

Collapse
 
martinbaillie42 profile image
Martin Baillie

Sorry, but you haven't used functional programming techniques to remove the need for if statements.

Instead you've replaced the if statement with an alternative syntax which should not be used for extended if else logic.

The if statement is most of the time better than the ternary operator because it is usually much more readable.

Collapse
 
srishanbhattarai profile image
Srishan Bhattarai

Agreed. In most cases, ternaries are not improvements but an obscure alternative to standard if else clauses.

Better ways to tackle if else clauses would be early returns and functional practices.

Collapse
 
joelnet profile image
JavaScript Joel • Edited

There is nothing obscure about a ternary statement. It is possible that you are less familiar with an ternary and more familiarity with an if statement (aka familiarity bias). This only has to do with what you have been more exposed to. But there is nothing about a ternary that makes it "obscure".

They key to this change (if -> ternary) is the elimination of statements and blocks and a shift to expressions.

The use of statements and blocks is an imperative style of programming and are unnecessary in functional programs.

When you eliminate statements and blocks, you will find that your code will naturally become more functional.

Collapse
 
joelnet profile image
JavaScript Joel • Edited

You are absolutely correct about eliminating if statements not being "functional".

Eliminating the if statement is a pre-functional step that will make it easier to create functional code.

The key difference being the transition from statements and blocks into expressions.

This minor change can allow you to use it in function compositions:

const computeStuff = R.compose(
  x => x % 3 === 0 ? 'Yes' : 'No',
  x => x + 2
)
Enter fullscreen mode Exit fullscreen mode

My ternary example is also structured in a similar way to Ramda's cond.

// from: http://ramdajs.com/docs/#cond
var fn = R.cond([
  [R.equals(0),   R.always('water freezes at 0°C')],
  [R.equals(100), R.always('water boils at 100°C')],
  [R.T,           temp => 'nothing special happens at ' + temp + '°C']
]);
fn(0); //=> 'water freezes at 0°C'
fn(50); //=> 'nothing special happens at 50°C'
fn(100); //=> 'water boils at 100°C'
Enter fullscreen mode Exit fullscreen mode

Readability is very subjective and depends on what you are already previously familiar with (aka familiarity bias).

For example, which code is more "readable" here, VB or C#?

Dim count As Integer = 0
Dim message As String

If count = 0 Then
    message = "There are no items."
ElseIf count = 1 Then
    message = "There is 1 item."
Else
    message = "There are " & count & " items."
End If
Enter fullscreen mode Exit fullscreen mode
int count = 0;
string message;

if (count == 0) {
    message = "There are no items.";
} else if (count == 1) {
    message = "There is 1 item.";
} else {
    message = "There are " & count & " items.";
}
Enter fullscreen mode Exit fullscreen mode

I would say the C# is more readable, but that is because I have spent more time in this land. Someone who spends their whole day in VB land will say the opposite.

To me, when done right, the ternary is much more readable.

I would argue that the ternary isn't any more or less readable than an if statement, but instead only less familiar.

Collapse
 
martinbaillie42 profile image
Martin Baillie

In your first example it is clean, readable and perfectly fine to use it in that way.

In your comparison of C# and VB both are perfectly understandable to any developer of almost any modern programming language.

The ternary is familiar to any moderately experienced developer. In the examples in your article they do not in any way improve upon the standard if statement or aid in functional programming.

The norm is to use if..else if..else for multiple clauses. Deviating from that norm needs a good reason. The examples in your article are not good reasons.

Thread Thread
 
joelnet profile image
JavaScript Joel

The reasons being that when you start to substitute statements and blocks with expressions, your code will be much easier to write functionally. It it hard to see the benefit from just this one article, but it's an important (small) step towards creating functional (or pre-functional) code.

The same arguments could be said about a for loop or switch statement, which like if are also imperative (non-functional) constructs.

I also remove for and switch for more functional code.

hackernoon.com/rethinking-javascri...
hackernoon.com/rethinking-javascri...

Thread Thread
 
martinbaillie42 profile image
Martin Baillie

Replacing a single if else statement with the ternary operator in a lambda function does enable you to write the logic in one terse expression on one line. I agree that this is a good thing.

This does not make the ternary operator functional and the if...else block not. It's just a, in this use case, more appropriate syntax for exactly the same result.

You could rewrite a single line arrow function that uses a ternary operator as a multiline arrow function using if..else and the function would still be functional, it would behave in exactly the same way.

The ternary operator is great for a single one line piece of logic. It is less great for multiple if..else if logic - just use an if block, it's easier.

Collapse
 
lobsterpants66 profile image
Chris Shepherd

It strikes me you have taken readable code and made it much harder to read and probably debug, for no noticeable advantage.

If I can't understand the code instantly when I'm called out at 3am it's useless to me.

Collapse
 
joelnet profile image
JavaScript Joel • Edited

They key to this change (if -> ternary) is the elimination of statements and blocks and a shift to expressions.

The use of statements and blocks is an imperative style of programming and are unnecessary in functional programs.

When you eliminate statements and blocks, you will find that your code will naturally become more functional.

One is not any more complex than the other, the complexity is the same! It is just that you are less familiar with the syntax because you have had more exposure to the other syntax and less to this, which has created a familiarity bias.

For me, it is the opposite, it is much more readable!

An example of this is seeing a new word for the first time like 'perfidious'. Having never seen it, you might have to read each letter and sound it out in your head. You might also prefer to use it's synonym 'disloyal', which you feel is "easier". After all, 'disloyal' is the word that you grew up with.

But after you have sounded the word out a couple times, know the definition and have used it in a few sentences, it will start to become second nature. You can now quickly glace at the word 'perfidious', pausing no longer than you would when reading other words as it becomes more and more familiar with time.

And now that you have added more words to your vocabulary, you can more effectively communicate because you have more tools to pull from your toolbox.

I promise, readability will come with time.

Cheers!

Collapse
 
lobsterpants66 profile image
Chris Shepherd

I'm perfectly familiar with the operator, I just find this usage needlessly difficult to follow and debug.

Mind you I avoid ifelse for the same reason 😊

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

This is terrible use of the ternary operator. You're mixing multiple imperative statements into a logical expression value.

  return isCustomerValid(customer)
    ? database.save(customer)
    : alert('customer is invalid')
Enter fullscreen mode Exit fullscreen mode

It's hard to image how database.save and alert resolve the same logical type, even if the actual type happens to be the same.

You're hiding an imperative conditional inside this ?: syntax. When I see ?: I think of conditional evaluation with multiple possibilities. I would absolutely not think of a branching imperative flow.

This hurts readability and is not clean code.

Collapse
 
joelnet profile image
JavaScript Joel

This is a contrived example that was made up to demonstrate specific functionality. The biggest problem with contrived examples is that there are always better ways to solve the problem. A perfect example is the validation, there are much better ways to validate inputs (but I am not writing about how to validate inputs).

The goal of the article is also not to demonstrate how to save things to the database. It is to demonstrate a ternary. So, yes this is a terrible example of how to save something to a database. And actually, the code itself doesn't even make sense! I mean why is a database.save function, which suggests this might be a node application co-exist with an alert which is only available in the browser? The whole things is nonsensical! But I didn't think anyone would get caught up on this.

I could have created an example that made more sense, something like...

const saveCustomer = customer =>
  doesCustomerExist(customer)
    ? database.update(customer)
    : database.insert(customer)
Enter fullscreen mode Exit fullscreen mode

... but then I could continue to run into problems when it could be suggested to write like this...

const saveCustomer = customer =>
  database[doesCustomerExist(customer) ? 'update' : 'insert'](customer)
Enter fullscreen mode Exit fullscreen mode

... which is not what I wanted to demonstrate.

Maybe I should leave more up to the imagination with something like this...

const doSomething = thing =>
  checkTheThing(thing)
    ? doOneThing(thing)
    : doOtherThing(thing)
Enter fullscreen mode Exit fullscreen mode

It's clearly a failure in the article that is detracting from the overall message.

Also, there is nothing imperative OR functional about this expression. It is simply an expression.

I am recommending the ternary as one of many ways to replace the if statement, which is definitely exclusively imperative.

I understand your opinion regarding the readability and I have a differing opinion; I absolutely prefer this syntax. There is nothing complicated about this code, it's actually incredibly simple, though you may be less exposed to this (because everyone is used to imperative codebases).

Even the basic examples on Mozilla's ternary page are in alignment with this article developer.mozilla.org/en-US/docs/W...

I'll try to work on better examples for future articles, I do agree that mixing database.save and alert is shitty and takes far too much attention away from the article.

Cheers!

Collapse
 
mortoray profile image
edA‑qa mort‑ora‑y

It's this pattern I disagree with:

const doSomething = thing =>
  checkTheThing(thing)
    ? doOneThing(thing)
    : doOtherThing(thing)

In particular I disagree with the do... functions. Ternary is meant for evaluation, such as:

const result = thing =>
  checkTheThing(thing)
    ? evalOneThing(thing)
    : evalOtherThing(thing)

There is a subtle, but important difference. This clearly results in a value that we want to use, whereas the do... form is branching to perform some action, it's unclear what result is desired.

Consider if the functions don't return anything. Then you'd have:

var voidResult = check ? voidFoo() : voidBar()

This won't even be allowed in many statically type languages, as they can't evaluate void. It also means the result cannot be used, implying that the expression itself isn't being used. Logically if a value isn't being used you'd expect you can skip the evaluation of it. But instead you have code where despite not using the result, you require the code to be evaluated nonetheless.

Moreso, you're implicitly relying on a short-circuiting of this expression, which may not actually be case in all languages. Consider that operators are short-forms for functions, in this case you have a function like this:

ternaryOp( cond, eval1, eval2 )

But you can't translate your code to this:

var result = ternaryOp( checkTheThing(thing), evalOneThing(thing), evalOtherThing(thing) );

This always evaluates both of the arguments, which is not correct for your code. It's failed a logical substituion test.

The form that I would allow is this:

var result = (checkTheThing(thing) ? evalOneThing : evalOtherThing)(thing);

This is okay because the ternary is a proper evaluation. It doesn't require compiler short-circuiting, it isn't hiding any code branch. It works with void return as well (dropping the assignment).

Thread Thread
 
joelnet profile image
JavaScript Joel

var voidResult = check ? voidFoo() : voidBar()
This won't even be allowed in many statically type languages, as they can't evaluate void

That is absolutely correct. Though this article is exclusive to JavaScript. There is quite a bit of JavaScript that doesn't translate over to other languages.

You can't even do this in C#

// ERROR
var result = voidFoo();
Enter fullscreen mode Exit fullscreen mode

You still always need to follow order of operations. For example:

// this function...
const result = ternaryOp( checkTheThing(thing), evalOneThing(thing), evalOtherThing(thing) );

// ...is equivalent to
const val1 = checkTheThing(thing)
const val2 = evalOneThing(thing)
const val3 = evalOtherThing(thing)
const result = ternaryOp(val1, val2, val3)
Enter fullscreen mode Exit fullscreen mode

...when compared to...

// this thing...
const result =
  checkTheThing(thing)
    ? doOneThing(thing)
    : doOtherThing(thing)

// ...is equivalent to
const val1 = checkTheThing(thing)
let result
if (val1) {
  result = doOneThing(thing)
} else {
  result = doOtherThing(thing)
}
Enter fullscreen mode Exit fullscreen mode

Moreso, you're implicitly relying on a short-circuiting of this expression

For sure. Though again, this article is exclusively about JavaScript. I'm still not completely sure this is short-circuiting, I have only heard that in reference to the && and || operators.

Another example:

const result = obj ? obj.name : 'unknown'
Enter fullscreen mode Exit fullscreen mode

The reason why this works is because both branches are not evaluated. This is not exclusive to my examples, but fundamental to how the ternary operator works in JavaScript.

The ternary works differently than VB.net's IIf operator, which evaluates both sides (this is close to your example). VB.net later added the ability to do Dim foo as String = If(bar = buz, cat, dog), which will only evaluate one side.

I would expect an understanding to how any operator works in your language prior to using it to be mandatory. JavaScript's ternary operator works in a fairly common way, for example, it works the same way in C and Java.

I will consider updating the article to clarify this is JavaScript as well as more examples on how the fundamentals of the ternary work.

All very good feedback.

Cheers!

Collapse
 
dorianamouroux profile image
Dorian Amouroux

For your customerValidation function, I would prefer to write my function validating the fields separately. All fields are required but the name is checked only if the login is checked only if the email is checked. I don't think the ternary operator is appropriate for this usecase.

Collapse
 
joelnet profile image
JavaScript Joel

Absolutely!

For validation, I would actually use something like Joi.

This is a problem with contrived examples. You think of a technique you want to demonstrate, then you find an example that will demonstrate it. Most times, there are better ways to solve the example you used. But those other ways, do not demonstrate the technique you wanted to demonstrate.

My goal was to demonstrate the ternary, not to show input validation.

I probably should have used some obscure cat example. Something I need to work on for sure.

Cheers!

Collapse
 
tensazangetsu profile image
TensaZangetsu

When i see ternary operator it looks messy, if you need in you app to lessen the code because of performance it's fine, but in reality development code looks cleaner, even someone with less experience in JavaScript can easily read your code.
Or better let me rephrase i show it to person that never programmed if else is understandable to them, ternary is not.

Collapse
 
joelnet profile image
JavaScript Joel

This is preference. I think if else looks messy compared to a ternary.

Collapse
 
tensazangetsu profile image
TensaZangetsu

It's not even preference. Thing you suggested is not readable and there is nor practical reason to do it.

This debate is not new, and almost all developers have come to agreement that sparing couple of lines and sacrificing readability for it is not acceptable.

The good code is code that doesn't need explanation, when you read it you know what it does. When you see ternary operator you can guess, but with if else it's just so simple. Imagine junior coming to firm to learn from your code if else just makes all sense in the world.

Thread Thread
 
joelnet profile image
JavaScript Joel

I guess we'll have to agree to disagree. Again, I believe in the opposite of what you have said. To me, it is more much more readable.

The purpose is not to simply shorten code. The purpose is learn alternatives to the if statement.

The reason why it is important to learn alternatives to an if statement is because I am trying to promote functional programming and if/else are imperative statements.

So, because if/else is imperative, it is therefore anti-functional. So if you want to start coding in a functional way, you will need to learn alternatives to the coding styles you might be comfortable with.

Learning multiple ways to solve the same problems makes you a better programmer.

Cheers!

Collapse
 
samsonasik profile image
Abdul Malik Ikhsan • Edited

if you do return early in function, you don’t need else. Using if is ok imo, with reduce else with return early when possible.

Collapse
 
joelnet profile image
JavaScript Joel

I love early returns and I use them in codebases that are imperative and OOP.

But when creating a functional codebase, 98% of my functions have a single statement, and that statement is a return.

When you eliminate statements and blocks, you will find that your codebase naturally becomes more functional.

Collapse
 
ztickm profile image
Salim MAHBOUBI

It's just less readable alternative syntax IMO

Collapse
 
joelnet profile image
JavaScript Joel

One is not any more complex than the other, the complexity is the same! It is just that you are less familiar with the syntax because you have had more exposure to the other syntax and less to this, which has created a familiarity bias.

For me, it is the opposite, it is much more readable!

An example of this is seeing a new word for the first time like 'perfidious'. Having never seen it, you might have to read each letter and sound it out in your head. You might also prefer to use it's synonym 'disloyal', which you feel is "easier". After all, 'disloyal' is the word that you grew up with.

But after you have sounded the word out a couple times, know the definition and have used it in a few sentences, it will start to become second nature. You can now quickly glace at the word 'perfidious', pausing no longer than you would when reading other words as it becomes more and more familiar with time.

And now that you have added more words to your vocabulary, you can more effectively communicate because you have more tools to pull from your toolbox.

I promise, readability will come with time.

Cheers!

Collapse
 
hyper_debugger profile image
Harrison

I don't think ternary operators should be used nested as you used it cause it's bad for readability. Also lambdas are not equivalent to functional programming, they are simply a tool in the toolset provided by functional programming techniques.

Collapse
 
joelnet profile image
JavaScript Joel

I find the opposite to be true. I believe nested ternary to be very readable. It really comes down to a matter of familiarity and ends up in a format similar to a switch or ramda's cond. I actually prefer ramda's cond is most situations.

These techniques themselves are not functional techniques. They are to provide alternatives to the traditional imperative code, but it is neither imperative or functional.

But since an if statement is anti-functional, learning alternatives to the if statement will help you when creating functional code.

Focusing in on this single article, it may not make as much sense, but when given the context of the larger body of articles I am writing, this technique becomes important.

Cheers!

Collapse
 
vpenkov profile image
VPenkov

Haven't checked James' article here, have you?
jrsinclair.com/articles/2017/javas...

Collapse
 
sri profile image
Sridhar Easwaran

Cool :)