Rethinking JavaScript: The if statement

JavaScript Joel on November 06, 2017

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... [Read Full]
markdown guide
 

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.

 

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.

 

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.

 

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
)

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'

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
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.";
}

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.

 

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.

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...

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.

 

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.

 

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.

 

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.

 

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!

 

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 😊

 

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')

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.

 

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)

... 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)

... 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)

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!

 

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).

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();

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)

...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)
}

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'

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!

 

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.

 

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!

 

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.

 

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

 

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.

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!

 
 

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!

 

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.

 

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!

 
 
 
code of conduct - report abuse