The three kinds of effects in programming
Tags: functional, javascript, programming
Published: 2023-12-20 14:43:18

---
title: The three kinds of effects in programming
published: true
description: 
tags: functional, javascript, programming
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-12-20 13:42 +0000
---

Effects can be distinguished in three categories:

* control flow effects
* mutations
* input/output

## Control Flow

Control flow effects have their manifestations in data types:

* indeterministic control flow: `Array`/List
* computation without a result value: `null`
* unavoidable exception: `Error`
* short circuit: `break`/`continue`

The problem with imperative programming is that these data types are partly insufficient to encode their corresponding control flow:

* `Array` provides an indeterministic choice but if this choice changes, you need to mutate the array or endure a performance penalty
* `null` merely indicates the lack of a result value but offers no means to avoid further processing
* `Error` entails the same problems as `null` and only works with `try`/`catch` statements, the next generation `goto`
* `break`/`continue` are no expressions but special nullary operators tied to their special constructs

From a functional perspective, these types are more or less harmful. FP uses algebraic data types, continuations and monads to solve shortcomings in the imperative world.

## Mutations

Mutations alter existing program state observably and destructively during the runtime of a program.

They are a side effect that puts a constraint on evaluation order, i.e. order matters even when it doesn't have to. It also hampers idempotent functions. This may lead to non-intuitive program behavior and renders optimization/refactoring harder.

Functional programming uses tree structures to realize persistent data types with structural sharing.

## I/O

You cannot ignore the real world, even with functional programming. If you rely on input and output and you do if your program should to achieve something useful, side effects are inevitable.

The trick is to separate the pure description of effects from their execution:

```javascript
const Cont = k => {
  const o = {run: k};
  return o;
};

Cont.map = f => tx =>
  Cont(k => tx.run(x => k(f(x))));

Cont.and = tx => ty =>
  Cont(k =>
    tx.run(x =>
      ty.run(y =>
        k([x, y]))));

const sqr = x => x * x;

// database mocks

const tw = Cont(k => new Promise((res, rej) => res(k(3)))),
  tx = Cont(k => new Promise((res, rej) => res(k(5))));

const ty = Cont.map(sqr) (tw),
  tz = Cont.map(sqr) (tx);

const data = Cont.and(ty, tz);

// ^^^ pure ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

data.run(data => { // data yields [9, 25]
  // impure scope
});
```
Only in the impure scope you can observe the result value of running the effect. Everything else is pure.

Pure and impure scopes are separated and effect execution is deferred by conducting it inside the last continuation of the program, namely `data => { // impure scope }`.

`Cont` builds up a nested deferred function call tree. Functional programming thus only utilizes tree data structures and continuations to handle all three effect classes.