Mind-bending functional programming with TypeScript - part 2
Tags: typescript, functional
Published: 2020-04-08 21:37:29

---
title: Mind-bending functional programming with TypeScript - part 2
published: true
description: Learn about interesting concepts from the world of functional programming.
tags: typescript, functional
---

- [Part 1: Introduction](https://dev.to/edbentley/mind-bending-functional-programming-with-typescript-part-1-112b)
- **Part 2: Effects**

Let's imagine you want to write a function which writes to a file in Node. Here's a simple example:

```ts
import * as fs from "fs";

function writeHelloWorld(path: string) {
  fs.writeFileSync(path, "Hello world!");
}

writeHelloWorld("my-file.md");
```

What's the type of our function `writeHelloWorld`? `fs.writeFileSync` returns `void`, so naturally so does `writeHelloWorld`.

But let's think about this for a second. What if we took the same function and made it empty? Like this:

```ts
function writeHelloWorld(path: string) {
  // Nothing to see here!
}
```

It's got the exact same type `(path: string) => void`. But of course they do very different things. What the first example does - writing a file - is called an _effect_. It's certainly not a pure function.

> If you're familiar with React hooks, this is exactly the same concept as `useEffect`, which you use for things like fetching data (which is very much an effect).

We're going to run on the assumption here that writing pure functions is good, and having side effects is bad.

So how could we make `writeHelloWorld` pure? Is this possible?

How about we make it _lazy_ by having `writeHelloWorld` return a function?

```ts
import * as fs from "fs";

function writeHelloWorld(path: string) {
  return () => fs.writeFileSync(path, "Hello world!");
}

const runEffects = writeHelloWorld("my-file.md");
```

Is this code now pure?

- no side effects ✅
- same input always produces same output ✅

Seems pure to me! But it doesn't do anything. We also need to add:

```ts
runEffects();
```

Only this part of our code is impure, because without that, nothing would happen.

Is this cheating? Sort of, but it works.

In fact we can make our _entire_ program pure, despite it being full of effects like file writes and random numbers all over the place. So long as every function with effects is lazy, we can combine all of our effects together into one lazy function - like a big red button to run the program:

```ts
// WARNING: do not call this function unless you're sure what you're doing!
function runMyPureProgram() {
  return () => {
    writeSomeFiles();
    getSomeRandomNumbers();
    getTodaysDate();
  }
}
```

Calling `runMyPureProgram()` is impure, yet the program itself is pure.

But how do we ensure our functions are pure? If I look at `getSomeRandomNumbers` and it returns `() => number[]`, how do I know if it has an effect or not? If only there was some way the compiler could tell us this... 🤔

Well it's called _Type_ Script for a reason! Let's imagine a new type:

```ts
type Effect<T> = () => T;
```

What we say is, any impure function with effects must return the type `Effect`. `T` is the return value - for example, if our effect was to read a file as a string it could be `Effect<string>`.

We can write:

```ts
function writeHelloWorld(path: string): Effect<void> {
  return () => fs.writeFileSync(path, "Hello world!");
}
```

Now, just by looking at the type signature of `writeHelloWorld`, we know it's going to be doing some effectful things. In this case, `T` is `void` since it doesn't return a value.

So if I start adding new impure things in my code, TypeScript will complain until I actually handle the effects correctly at the end of the program.

Through some lazy functions and a new type called `Effect`, we've both kept our program pure _and_ we know where the impure effectful things are hiding. 🤯

If you're like me, at this point you might be thinking: "This is great! Wait - what's the point?". It seems like we still have a bunch of impure stuff littered around our codebase, and we've now made the code more awkward than it was before. All in the name of "purity".

Let's stop for a second for a quiz. Imagine this contrived example:

```ts
function funcA(): number {
  return funcB();
}

function funcB(): number {
  return funcC();
}

function funcC(): number {
  return 5;
}
```

We want to change `funcC` to return a random number. I'll give you three ways we could do this, which one is the 'best'?

#### 1. The impure way:

```ts
function funcC(): number {
  return Math.random();
}
```

Easy! Nothing else to change, let's go home now.

#### 2. Dependency injection:

```ts
function funcA(randomGenerator: () => number): number {
  return funcB(randomGenerator);
}

function funcB(randomGenerator: () => number): number {
  return funcC(randomGenerator);
}

function funcC(randomGenerator: () => number): number {
  return randomGenerator();
}
```

Our functions are still nice and pure, but we had to add an argument to them all, which is a lot of typing...

#### 3. Effects

```ts
function funcA(): Effect<number> {
  return funcB();
}

function funcB(): Effect<number> {
  return funcC();
}

function funcC(): Effect<number> {
  return () => Math.random();
}
```

Everything's still pure but we had to change the return type of everything to make it lazy.

So I'll ask again, which one's the 'best'?

I would say typing effects are a blessing and a curse. It's exactly _because_ they require you to change your function signatures you're _forced_ to program in a way where effects are kept to a minimum in your code. And you're more likely to put them all in one place.

It's like immutability - it can be more annoying to do, but can provide a cleaner codebase overall. Whether the short-term pain is worth the long-term gain is always difficult to say though.

Even if you're now convinced to use the `Effect` type, this still isn't perfect. There's nothing in TypeScript which _forces_ us to add an `Effect` type for effects, we just have to be vigilant about it. If you want to be forced to do it, that's when you head to pure a functional language like Haskell.

### Introducing: IO

The [fp-ts](https://gcanti.github.io/fp-ts/) library already has a type just like `Effect`, and its name is `IO`. In fact, its type is exactly as we defined `Effect`.

```ts
// taken from source code
export interface IO<A> {
  (): A
}
```

The benefit of using `IO` is it comes with a lot of helper functions for handling effects. Plus, it provides functions for effectful things (like generating random numbers) which are already typed as `IO`. Here are some simple examples:

### Add a number

Let's start here:

```ts
import { IO } from "fp-ts/lib/IO";
import { random } from "fp-ts/lib/Random";

function funcA(): IO<number> {
  return funcB();
}

function funcB(): IO<number> {
  // importing random from fp-ts is the same as
  // return () => Math.random();
  return random;
}
```

Imagine we want to add 1 in `funcA`:

```ts
function funcA(): IO<number> {
  return funcB() + 1;
}
```

Oops! We get the error:

```
Operator '+' cannot be applied to types 'IO<number>' and 'number'.
```

We could change `funcA` to this:

```ts
function funcA(): IO<number> {
  return () => funcB()() + 1;
}
```

But that's annoying and plain ugly. Instead fp-ts gives us `io.map`:

```ts
import { IO, io } from "fp-ts/lib/IO";

function funcA(): IO<number> {
  return io.map(funcB(), (x) => x + 1);
}
```

Much like you call `.map` on an array, you can `map` something of type `IO` too.

### Logging a random number

We can use `io.chain` to combine two effects into a single effect:

```ts
import { IO, io } from "fp-ts/lib/IO";
import { random } from "fp-ts/lib/Random";
import { log } from "fp-ts/lib/Console";

function logRandomNumber(): IO<void> {
  return io.chain(random, log);
}

const program = logRandomNumber();

// this logs a random number when called
program();
```

In this way we can combine all of our effects into one `program`, which we then call right at the very end.

There's much more to learn that we haven't covered (I haven't even mentioned error handling), so do check out these resources if you want to find out more:

- [Getting started with fp-ts: IO](https://dev.to/gcanti/getting-started-with-fp-ts-io-36p6)
- [fp-ts IO docs](https://gcanti.github.io/fp-ts/modules/IO.ts.html)
- [How to deal with dirty side effects in your pure functional JavaScript](https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/)
- [Learn you a Haskell: Input and Output](http://learnyouahaskell.com/input-and-output)