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
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
The if
statement doesn’t fit into this type of function, but the ternary operator does!
const isGreaterThan5 = x => x > 5 ? 'Yep' : 'Nope'
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')
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
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'
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)
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:
My ternary example is also structured in a similar way to Ramda's cond.
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#?
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 orswitch
statement, which likeif
are also imperative (non-functional) constructs.I also remove
for
andswitch
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.
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.
It's hard to image how
database.save
andalert
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 analert
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...
... but then I could continue to run into problems when it could be suggested to write like this...
... which is not what I wanted to demonstrate.
Maybe I should leave more up to the imagination with something like this...
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
andalert
is shitty and takes far too much attention away from the article.Cheers!
It's this pattern I disagree with:
In particular I disagree with the
do...
functions. Ternary is meant for evaluation, such as: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:
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:
But you can't translate your code to this:
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:
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).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#
You still always need to follow order of operations. For example:
...when compared to...
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:
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 doDim 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 thename
is checked only if thelogin
is checked only if theemail
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!
if you do return early in function, you don’t need
else
. Using if is ok imo, with reduceelse
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's just less readable alternative syntax IMO
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!
Haven't checked James' article here, have you?
jrsinclair.com/articles/2017/javas...
Cool :)