Practical Functional Programming in JavaScript - Side Effects and Purity
Tags: javascript, webdev, functional, node
Published: 2020-08-03 18:00:13

Today we'll discuss two fundamental qualities of JavaScript functions and systems: side effects and purity. I also demonstrate an approach to organizing programs around these qualities with a couple of functions from my functional programming library, rubico.

A function is **pure** if it satisfies the [following conditions](https://en.wikipedia.org/wiki/Pure_function):
 * Its return value is the same for the same arguments
 * Its evaluation has no side effects

A function's **side effect** is a [modification of some kind of state beyond a function's control](https://softwareengineering.stackexchange.com/questions/40297/what-is-a-side-effect) - for instance:
 * Changing the value of a variable;
 * Writing some data to disk;
 * Enabling or disabling a button in the User Interface.

Here are some more instances of side effects
 * reading data from a file
 * making a request to a REST API
 * writing to a database
 * reading from a database
 * logging out to console

Indeed, `console.log` is a side-effecting function.

```javascript
// console.log(message string) -> undefined
console.log('hey') // undefined
```

In pure math terms, `console.log` takes a string and returns undefined, which isn't so useful. However, `console.log` is very useful in practice because of its side effect: logging any arguments you pass it out to the console. I like `console.log` because it only does one thing and does it well: log stuff out to the console. When the most straightforward solutions to real life challenges involve a mixture of side-effects and pure computations at a similar execution time, it's useful to have functions like `console.log` that have isolated, predictable behavior. My opinion is it's misguided to try to temporally separate side-effects and pure computations in JavaScript for the sake of mathematical purity - it's just not practical. Rather, my approach is to isolate side effects to simple functions like `console.log`.

I'll demonstrate with a function `add10` with several different side effects. `add10` is not pure.

```javascript
let numCalls = 0

const add10 = number => {
  console.log('add10 called with', number)
  numCalls += 1
  console.log('add10 called', numCalls, 'times')
  return number + 10
}

add10(10) /*
add10 called with 10
add10 called 1 times
20
*/
```

`add10` has the side effects of logging out to the console, mutating the variable `numCalls`, and logging out again. Both `console.log` statements have side effects because they use the function `console.log`, which has the side effect of logging out to the console. The statement `numCalls += 1` also has a side effect because the variable `numCalls` is state beyond the control of the function.

By refactoring the console logs and the variable mutation to an outside function `add10WithSideEffects`, we can have a pure `add10`.

```javascript
let numCalls = 0

const add10 = number => number + 10

const add10WithSideEffects = number => {
  console.log('add10 called with', 10)
  numCalls += 1
  console.log('add10 called', numCalls, 'times')
  return add10(10)
}

add10WithSideEffects(10) /*
add10 called with 10
add10 called 1 times
20
*/
```

Keep in mind that while `add10` is now pure, all we've done is move our side effects outside the scope of `add10` and into the more explicit `add10WithSideEffects`. Now we're being explicit about the side effects at least, but it's still a bit messy in my eyes. As far as vanilla JavaScript goes, this code is fine. However, I think we can get cleaner with my functional programming library, rubico.

The functions are simple enough at their core so that if you don't want to use a library, you can take these versions of the functions in vanilla JavaScript. Introducing: `pipe` and `tap`

```javascript
/**
 * @name pipe
 *
 * @synopsis
 * pipe(funcs Array<function>)(value any) -> result any
 */
const pipe = funcs => function pipeline(value) {
  let result = value
  for (const func of funcs) result = func(result)
  return result
}

/**
 * @name tap
 *
 * @synopsis
 * tap(func function)(value any) -> value
 */
const tap = func => function tapping(value) {
  func(value)
  return value
}
```

 * **pipe** takes an array of functions and chains them all together, calling the next function with the previous function's output. We'll use `pipe` as a base foundation to organize our side effects.
 * **tap** takes a single function and makes it always return whatever input it was passed. When you use `tap` on a function, you're basically saying "don't care about the return from this function, just call the function with input and give me back my input". `tap` is great for functions responsible for a single side-effect like `console.log`. We'll use `tap` to separate our side-effects by function.

```javascript
const logCalledWith = number => console.log('add10 called with', number)

let numCalls = 0

const incNumCalls = () => numCalls += 1

const logNumCalls = () => console.log('add10 called', numCalls, 'times')

const add10 = number => number + 10

const add10WithSideEffects = pipe([
  tap(logCalledWith),
  tap(incNumCalls),
  tap(logNumCalls),
  add10,
])

add10WithSideEffects(10) /*
add10 called with 10
add10 called 1 times
20
*/
```

We've isolated the `console.log` and variable mutation side effects to the edges of our code by defining them in their own functions. The final program is a composition of those side effecting functions and a pure function `add10`. To be clear, `add10WithSideEffects` is not pure; all we've done is move our side effects out to their own functions and, in a way, declare them with `tap`. The goal here is not to be pure for purity's sake, but to have clean, readable code with organized side-effects.

 * `logCalledWith` takes a number and logs 'add10 called with' number
 * `incNumCalls` takes nothing and increments the global variable `numCalls`
 * `logNumCalls` takes nothing and logs the global variable `numCalls`

All of these functions are singly responsible for what they do. When used with pipe and tap in `add10WithSideEffects`, the side effects of our program are clear.

Thanks for reading! You can find the rest of this series in the [awesome resources](https://github.com/a-synchronous/rubico#awesome-resources) section of rubico.

Photo credits:
https://www.pinterest.com/pin/213639576046186615/

Sources:
https://en.wikipedia.org/wiki/Pure_function
https://softwareengineering.stackexchange.com/questions/40297/what-is-a-side-effect