natto's approach to local-first

November 2021

Tweets on code

May 2021

Exploring Codebases

February 2021

Replacing MDX with Notion

December 2020

Using Vue 3 with JSX and TypeScript

September 2020

Monads for JavaScript developers

September 2020

Comparing Vue and React

August 2020

VSCodeVim Tips

June 2020

Will this work in React Concurrent mode?

February 2020

ReasonReact hook recipes

June 2019

Strings are too powerful

June 2019

The three core React hook features

May 2019

Monads for JavaScript developers

Why are there so many monad articles and tutorials? Because people like me keep writing them 😎

Maybe it's because of the monad tutorial fallacy. Anyways, I hope this gives you, a JavaScript developer, a gist of what monads are and why people care about them.

This article is also a small experiment. Try clicking this dashed link. It will open a pane, as will the other dashed links. If you are at a computer (recommended), you'll find some interactive exercises as well.

I imagine you are familiar with the almighty JavaScript and have encountered types before, maybe with TypeScript or Flow. If not, the code examples should still be intelligible. No Haskell experience required!

Building intuition with JavaScript

Let's pretend for a moment that JavaScript is a pure language and side-effects are not allowed in functions.

```
function increment(x: number): number {
console.log('incrementing'); // side effect not allowed!
return x + 1;
}
```

How can we implement something similar to

`console.log`

without side effects? We could wrap the return value to include a string.```
function incrementWithLog(x: number): [number, string] {
return [x + 1, 'incrementing\n'];
}
function run() {
const valueWithLog = incrementWithLog(0);
const [value, log] = valueWithLog;
// value = 1
// log = 'incrementing\n'
}
```

Notice how we've created a context around the original value. Where we started with

`increment`

returning a `number`

, `incrementWithLog`

now returns a "tuple" `[number, string]`

where the `string`

represents the log message. Let's call this context `WithLog<T> = [T, string]`

where `T`

could be any type. In this example, `incrementWithLog`

returns a `WithLog<number>`

, with `T`

being the `number`

.What if we want to use this function multiple times?

```
function run() {
const initialValue = 0;
// smooth sailing with the original increment without log
return increment(increment(increment(initialValue)));
}
function runWithLog() {
const initialValue = 0;
// each incrementWithLog includes a log
const [result1, log1] = incrementWithLog(initialValue);
const [result2, log2] = incrementWithLog(result1);
const [result3, log3] = incrementWithLog(result2);
return [result3, log1 + log2 + log3];
}
```

This is okay but there's extra work dealing with the log message. We need to destructure each return value. We can't chain calls nicely like we can with

`increment`

. Can we make `WithLog`

easier to work with?Let's introduce a couple new functions. The first is

`wrap`

, which takes a plain old value and puts it in our context with an empty log (represented by an empty string `''`

).```
type WithLog<T> = [T, string];
// Put a value into the context
function wrap<T>(value: T): WithLog<T> {
return [value, ''];
}
```

Our second function

`bind`

is more complicated. It takes two arguments, a `WithLog<T>`

value and a function with type `T => WithLog<T>`

.```
// Apply a given function to a context value
function bind<T>(
valueWithLog: WithLog<T>,
f: T => WithLog<T>
): WithLog<T> {
const [value, existingLog] = valueWithLog;
const [newValue, newLog] = f(value);
return [newValue, existingLog + newLog];
}
```

It calls the given function with the value inside the existing context. It then concats the log strings together to form the new log. You can think of it as appending the new log message onto existing logs.

Exercise

Now it's your turn. Open this exercise and try implementing

`runWithLog`

using both `wrap`

and `bind`

.View the solution. **We don't even have to know how **** is implemented.** We know it's a tuple from above but that can change without affecting our implementation of

`runWithLog2`

looks a lot like our original `run`

with just `increment`

! There isn't any code dealing with the log messages. `wrap`

and `bind`

take care of that for us. **WithLog**

`runWithLog`

.Quick recap

We just made our own *feel like*.

`WithLog`

monad! We haven't formalized any of this yet but I hope you have an idea of what monads We have a context type

`WithLog<T>`

used to represent a value with a log message. We also defined two functions for working with `WithLog`

. `wrap`

puts a value in the `WithLog`

context. `bind`

applies a function `T => WithLog<T>`

to a context value `WithLog<T>`

to get another context value `WithLog<T>`

.Why learn monads?

For JavaScript developers, I don't think monads are that useful and are definitely not necessary to understand. However, ideas from functional programming are what inspired frameworks like React. Learning monads and alike gets you comfortable thinking about types at a higher level.

Haskell

Monads are usually associated with Haskell because they form the building blocks for writing programs. In the example above, we pretended that we couldn't have side effects inside functions. *This is actually true in Haskell!* You can't just add

`console.log`

inside your function.The core Haskell programming language doesn't have many "features" that you take for granted in other languages. In JavaScript, you can put side effects anywhere. In JavaScript, global and module state is easy.

In most functional programming languages, you don't have imperative statements like you do in most popular programming languages. Instead, everything is an expression. In Haskell, the

`IO`

monad gives programmers the ability to sequence effectful actions. For example, `putStr`

(the `console.log`

equivalent) has type `string => IO<void>`

.Again, this is not a Haskell tutorial but the high-level picture is that combining monads allows Haskell programmers to add "features" to their programming environment. Our **Haskell programmers get to (have to?) pick and choose what programming features to use.**

`WithLog`

monad adds the feature of logging strings. We'll see how the `Maybe`

monad below adds the feature of failure. People describe monads as "computational context". Notes

In Haskell, our

`wrap`

function is called `return`

. It's extremely confusing hence why I use `wrap`

in this article. Just know that this is not the real name.The function (second arg) given to

`bind`

can return a context value of another type parameter. `bind`

has type `(M<T>, T => M<U>) => M<U>`

. I restricted `U = T`

in the `bind`

example above to reduce the number of type variables. For example, we can do the following.```
function isEvenWithLog(x: number): WithLog<boolean> {
return [x % 2 === 0, 'called isEven\n'];
}
function runWithLog(x: number): WithLog<boolean> {
return bind(bind(wrap(x), incrementWithLog), isEvenWithLog);
}
```

If you wanted to stop here, I don't blame you. Thanks for sticking this far! The rest of the article defines monads more precisely and gives you more context to understand monads.

What is a Monad exactly?

Now that we have interacted with monads, let's define them more precisely. Monad is a type class that is defined in Haskell as the following.

```
class Applicative m => Monad m where
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
```

Monad is a type class with kind **Damnit paul**

`* -> *`

. The approximate English translation: **Monad is a generic type with one type parameter. It also has two associated functions, **** and ****.**

**return**

**bind**

If you're familiar with a type system like TypeScript, monads roughly look like

`M<A>`

where `M`

is the monad. For example, `Array<T>`

has the structure to be a potential monad.Let's translate the Haskell definition above into pseudo TypeScript. I've replaced

`return`

with `wrap`

because `return`

is a restricted JavaScript keyword (and extremely confusing). The `>>=`

symbol is `bind`

.```
interface MonadImplementation<M<_>> {
wrap: <A>(A) => M<A>,
bind: <A, B>(M<A>, A => M<B>) => M<B>,
}
```

Let's revisit our

`WithLog`

context. Here, we plug in our implementation for `wrap`

and `bind`

.```
type WithLog<T> = [T, string];
const WithLogMonadImplementation: MonadImplementation<WithLog> = {
wrap: <A>(x: A): WithLog<A> => [x, ''],
bind: <A, B>(m: WithLog<A>, f: A => WithLog<B>) => {
const [value, log] = m;
const [newValue, newLog] = f(value);
return [newValue, log + newLog];
},
};
```

This is the JavaScript implementation of our

`WithLog`

monad!Maybe Monad

The best way to understand monads is to implement one (or a couple). Let's try implementing the

`Maybe`

monad.`type Maybe<T> = { value: T } | undefined;`

This is a generic type representing potential failure. For example,

`Maybe<number>`

could either be a `{ value: number }`

or `undefined`

(failure!). Why don't we just use `T | undefined`

?Exercise

Try implementing

`wrap`

and `bind`

for `Maybe`

.`wrap`

is straightforward; it puts the given value into the `Maybe`

context. If the given value to `bind`

is `undefined`

(failure), we just continue failing by returning `undefined`

. Otherwise, we call the given function, which will return us another `Maybe`

value. Note that calling function `f`

might "fail" and return an `undefined`

.If you have a sequence of computations and any of them fails, we want the whole sequence to fail. Here's an example.

```
function parseColorHex(color: string): Maybe<number> {
switch (color) {
case 'red':
return { value: 0xff0000 };
case 'green':
return { value: 0x00ff00 };
case 'blue':
return { value: 0x0000ff };
}
return undefined;
}
function getProfileColor(user: User): Maybe<string> {
const profile = profiles[user.profileId];
if (profile === undefined) return undefined;
return { value: profile.color };
}
function getUser(userId: string): Maybe<User> {
const user = users[userId];
if (user === undefined) return undefined;
return { value: user };
}
function main(userId: string): Maybe<number> {
return bind(bind(bind(wrap(userId), getUser), getProfileColor), parseColorHex);
}
```

We're sequencing three functions here:

`getUser`

, `getProfileColor`

and `parseColorHex`

. If anything fails (returns `undefined`

), the entire sequence will fail (return `undefined`

). Note that if we don't fail, we will end with a `{value: answer}`

instead of `{value: {value: {value: answer}}}`

.Comparing to

`fmap`

The idea of

`fmap`

is more common function in JavaScript. I'm going to skip some Haskell details but let's meditate on the following function.```
// Apply f to every A inside m
fmap: (m: M<A>, f: A => B) => M<B>
// A common example
arrayMap: (arr: Array<A>, f: A => B) => Array<B>
```

`fmap`

applies a function to values inside a context value. The most common example is mapping over an array.Here's

`fmap`

for `Maybe`

. I'm putting it side-by-side with `bind`

.```
function fmap<A>(m: Maybe<A>, f: A => B): Maybe<B> {
if (m === undefined) {
return undefined;
}
return {
value: f(m.value);
}
}
```

```
function bind<A>(m: Maybe<A>, f: A => Maybe<B>): Maybe<B> {
if (m === undefined) {
return undefined;
}
return f(m.value);
}
```

Spot the differences

They're so similar! The most notable difference is the type of

`f`

, the function parameter. The `f`

for `fmap`

returns `B`

whereas `f`

for `bind`

returns `Maybe<B>`

.If you pass in a

`{value}`

to `fmap`

, you're definitely getting a `{value}`

back (never `undefined`

). However, `bind`

's given function can return `undefined`

. `bind`

's `f`

has the ability to modify the "structure" of the context value. In a way, you can think of monad's `bind`

as more powerful than `fmap`

.Nice to know

We’ll keep formalizing what monads are but if this is feeling tedious, remember that these concepts are not necessary in JavaScript!

Just satisfying the types is not enough to call it a monad. It also needs to satisfy a few rules so they act predictably. These are known as the Monad Laws.

Because working with monads is so common in Haskell, there is syntactic sugar called do notation.

Another way to think about monads is with

`join`

, a way to smash two "computational contexts" into one. For example, `WithLog<WithLog<T>> => WithLog<T>`

.Recap

A monad is a generic type that implements

`wrap`

and `bind`

. Although the topic may be overhyped, monads are essential in some programming languages, most notably Haskell. Monads enable side effects and allow sequencing actions in a pure functional programming language.I hope you found this useful. Please send any comments/feedback on twitter. Thanks!

Links

If you want to learn more about monads and type classes, here are some links that I found useful.

- Typeclassopedia - I highly recommend the Instances and Intuition sections under Monad, which helped me build my understanding for monads.

- You Could Have Invented Monads! - I took a lot of inspiration (copied/translated to JavaScript?) from this great monad tutorial.

- Comprehending Monads - Philip Wadler's original paper on monads

- Monads are burritos - A comic about monad tutorials (from monad tutorial fallacy)