Flow control, short-circuit expressions and functional programming in JavaScript
Tags: javascript, programming, functional
Published: 2021-12-30 11:19:58

A couple of days ago while doing code review I saw a snippet like this:

```jsx
React.useEffect(() => {
  someCondition && doSomething()
}, [someCondition, doSomething])
```

You don't need to know what `React.useEffect` does or anything, I just want to focus on the body of the callback function.

I suggested my colleague to use an `if` statement instead of the short-circuit expression. When asked why it was preferable to use an `if` instead, I didn't have a reasonable answer. It just _felt better_ to me.

But _feeling better_ is not an acceptable reason, specially because it is highly subjective. What _feels better_ to me won't necessarily _feel better_ for the rest of the team.

So I just did what any other person would do: I obsessed about it (😅), trying to rationalize why _it felt better_.

_**Disclaimer**: This article just describes my reasoning around this topic. Like it or not, that's completely up to you. I'm not suggesting this as a "best practice" or anything else._

---

One thing I learned from watching Rich Hickey's talks is to always start with a definition:

> In computer science, an [expression](https://en.wikipedia.org/wiki/Expression_%28computer_science%29) is a syntactic entity in a programming language that may be evaluated to determine its value.

Here's a bunch of expressions in JavaScript:

```js
42                              // 42 
'foo'                           // 'foo'
false                           // false
const nums = [1, 2, 3]          // ??
nums                            // [1, 2, 3]
```

Values in JavaScript evaluate to themselves, and variables _holding_ values evaluate to whatever they _hold_. Notice the fourth line in the snippet above: in JavaScript assignments are also expressions. What do you think the expression `const nums = [1, 2, 3]` evaluates to?

Well, it evaluates to `undefined`.

In other programming languages (like Python) a variable assignment is not an expression, but a statement. Here's the definition for statement:

> In computer programming, a [statement](https://en.wikipedia.org/wiki/Statement_(computer_science)) is a syntactic unit of an imperative programming language that expresses some action to be carried out.

The important word here is **action**. Keep that in mind for now.

Here's a bunch of statements in JavaScript:

```js
for (let n of nums) { /*...*/ }
while (true)        { /*...*/ }
if (nums.length)    { /*...*/ }
```

Ignoring the fact that assignment is an expression (a useless expression, if I do say so myself) it would be reasonable to think that _expressions_ are to **values** as _statements_ are to **actions**.

## Short-circuit evaluation

More definitions, yey:

> [Short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation), minimal evaluation, or McCarthy evaluation (after John McCarthy) is the semantics of some Boolean operators in some programming languages in which the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression.

Here's an example:

```js
true || false                 // true
```

In the previous snippet of code, the right-hand side expression of the OR operator is not evaluated since the first argument is enough to determine the value of the whole expression.

It's kinda weird to think about it like this using literals, since literals evaluate to themselves. We'll write this differently so it's easier to reason about:

```js
const aCondition = true
const anotherCondition = false

aCondition || anotherCondition   // true
```

Since `aCondition` is `true`, there's no need for looking up the value of `anotherCondition`, whatever that is.

Let's try with another example:

```js
const person = {
  get name() {
    console.log('Bayum!')
    return 'Bodoque'
  }
}

true || person.name           // true
```

If you run this code, you'll notice `'Bayum!'` is not logged to the console, since the left-hand side of the `||` operator is already `true`, which is good!

But what's the deal with this?

## Side effects, functional programming & Haskell

We'll take a brief detour and continue with, guess what, another definition:

> [Haskell](https://en.wikipedia.org/wiki/Haskell_(programming_language)) is a general-purpose, statically typed, purely functional programming language with type inference and lazy evaluation.

Let's write a little function with Haskell that prints `"42"` to the console:

```hs
doSomething = putStrLn "42"
```

Using `ghci`, which is the Glasgow Haskell Compiler interactive environment (think of a REPL), we can check the type of our `doSomething` function:

```ghci
Prelude> doSomething = putStrLn "42"
Prelude> :t doSomething 
doSomething :: IO ()
```

`doSomething` is a function that takes no arguments and its return type is `IO ()`, or `IO` of unit (an empty set of parentheses is called unit and it's _similar_ to `void` in JavaScript). In Haskell all functions with side effects have a return type of `IO` of something. Pure functions can't call effectful functions. If you want to have a side effect, the return type should always be `IO` of something.

Although not mandatory, we can explicitly write type annotations:

```hs
doSomething :: IO ()
doSomething = putStrLn "42"

-- Here's another function that takes two Ints 
-- and returns another Int, just for contrast
add :: Int -> Int -> Int 
add a b = a + b
```

Alright, detour is over, enough Haskell, let's get back on track.

## Short-circuit expressions and flow control

A function invocation can always be replaced by its return value if it depends only in its inputs. Another way to phrase it, is that a function invocation can only be replaced by its return value if the function has no side effects.

This property is called referential transparency. Referentially transparent functions are also known as pure functions.

When doing functional programming, our goal is to maximize the surface area of code that's written with pure functions: they are easier to test and easier to reason about. So for most of your functions in a program, you're going to be interested in their return values:

```js
const whatIsThis = someCondition && doSomething()
```

If we're not interested about the result of `doSomething`, then it's probably worthless to store the value of the expression into `whatIsThis`, but the expression will still have a value, whether it is used or not:

```js
function doSomething() {
  console.log("42")
}

someCondition && doSomething()   // `false` when `someCondition` is `false`
                                 // `undefined` when `someCondition` is `true`
```

If we don't care about the value of the expression, then `doSomething` is most likely an effectful function. But JavaScript is no Haskell so there's no way to tell if `doSomething` is effectful or not without looking at its implementation. And even then, it wouldn't be necessarily something straightforward to figure out.

I think this is why I prefer to use an `if` statement instead of a short-circuit expression for flow control in effectful functions: for me it makes it completely unambiguous that we don't care about the return value, hence it's a side effect.

## But what about effectful functions that DO return something?

We don't have a compiler like GHC to enforce purity in our functions, but we can still follow a similar convention that only effectful functions can call other effectful functions. Haskell does this using monads.

Instead of writing an explanation about this topic, let me point you to this really straightforward video that makes a wonderful job:

https://youtu.be/C2w45qRc3aU