How monads encapsulate side effects
Tags: functional
Published: 2021-03-26 11:04:25

---
title: How monads encapsulate side effects
published: true
description: Fun with effects and monads!
tags: functional
cover_image: https://images.unsplash.com/photo-1494059980473-813e73ee784b?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=1349&q=80
---

At Meeshkan, I'm always _thrilled_ to welcome new hires, and I'm extra-super-thrilled to welcome our two new PureScript developers to the team: **Vincent Orr** and **Muse Mekuria**. Their stories span France, England, Ethiopia and America, and their programming careers span many languages and projects, but what unites them at this team at this moment is our PureScript code base (among other things!).

Vince's technical interview resulted in an [article on modeling transactional logic with types](https://dev.to/mikesol/modeling-asynchronous-transactions-with-types-part-1-388b). For Muse's hire, I'd like to write an article about modeling effects with monads based on a discussion we had recently. The goal is to explain what `Effect` in PureScript (or `IO` in Haskell) _is_ and why it is a monad.

# Kleisli arrows

Monads hang out at the end of Kleisli arrows. A Kleisli arrow is a function from `a -> b` that wraps `b` in a something extra called `m`. That's a monad. Let's see some examples of Kleisli arrows in Haskell or PureScript:

```haskell
-- A Kleisli arrow (a -> m a) where m = (r -> a)
env :: forall r. a -> (r -> a)
env a _ = a

-- A Kleisli arrow (a -> m a) where m = Maybe
just :: a -> Maybe a
just = Just

-- Another Kleisli arrow (a -> m a) where m = Maybe
nothing :: a -> Maybe a
nothing _ = Nothing
```

Monads multiply. For example, in the case of Maybe, if there are _n_ functions from `a -> b`, there are _2n_ functions from `a -> Maybe b` because there are two branches in `Maybe` - `Just a` and `Nothing`.

# What do we mean when we say "effect"?

When we talk about effects, we are talking about two interrelated but distinct concepts:

1. Effect signals that the outside world is somehow changed as a result of our program doing something, and some of that change cannot potentially be propagated back into the program. For example, when I write to `console.log`, the luminosity of the screen, its connection with my retina, and the effect the information has on my brain exists in the world and cannot propagate back into the program.

2. Effect signals something that may go wrong. Because we are venturing into the outside world, we simply _don't know_ how things will go. For example, `console.log` could display something so heinous that, when going from my retina to my brain, it resulted in me throwing my computer out the window, causing the program to terminate. There is no way the program could have known that `console.log` would go south like this.

These two concepts have distinct representations.

1. A monad that signals "this changes the outside world" is _nominal_ in nature. It is an indication via the type system that a change happened. When we see `Effect Unit`, we know that by executing that monad, something will change. It is purely on the level of documentation.

2. A monad that signals "something may go wrong" is _actionable_ in nature. It is an indication via the type system that things may blow up and you can choose to deal with it or not.

# Effects as Kleisli arrows

Effect, in the way we typically talk about it (and in the way `IO` in Haskell and `Effect` in PureScript work) are both _nominal_ and _actionable_.

1. _nominal_: Something happened in the outside world.
2. _actionable_: That thing may have gone South, and you _can_ do something about it.

So if `Effect` works this way, how can we model it? Using `Maybe` above is a good start: it has everything we want:

```haskell
data Maybe a = Just a | Nothing
```

The `Just` branch is nominal and the `Nothing` branch is actionable. If we get a `Just`, we can breathe a sigh of relief, and if we get a `Nothing`, we need to do some sort of cleanup and/or quit the program.

So how is `Effect` like `Maybe`? In PureScript, `Effect` is a computation that happens in JavaScript. The success branch is the result of the computation, and the failure branch is an [`Error`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error).

There's one small wrinkle, though - we need to "wrap" a value in our effect (ie `Effect Unit`, `Effect Int`, etc). Meaning that it needs some context in the success case, just like `Just` is the context for `a` in `Just a`. There are various ways we can do this wrapping, and the one PureScript chooses is to wrap the result in a _thunk_, or function with 0 arguments. That guarantees that the execution of the code will be delayed until you call `myThunk()`.

# DIY effects

Now that we know how effects work, let's roll our own! We'll build them from the ground up, meaning no libraries - in just a few lines of code, we'll have our own effect system.

## Nominal

First, we'll do the _nominal_ bit. This acknowledges that a side effect happened (ie writing to the console) without any attempt to handle the case where logging errors out.

```javascript
// Main.js
exports.bindEffect = function(ma) {
  return function(aToMb) {
    return function () {
      return aToMb(ma())();
    }
  }
}

exports.log = function(s) {
  return function() {
    console.log(s);
  }
}
```

```haskell
module Main where

class Bind m where
  bind :: forall a b. m a -> (a -> m b) -> m b

data Unit = Unit

data Effect a

foreign import bindEffect :: forall a b. Effect a -> (a -> Effect b) -> Effect b

foreign import log :: String -> Effect Unit

instance bindEffect_ :: Bind Effect where
  bind = bindEffect

main :: Effect Unit
main = bind (log "hello") (\_ -> log "world")
```

## Actionable

Now let's add a bit more code to deal with errors. `naughty` provokes an error and `nice` catches it.

```javascript
exports.evil = new Error("I'm naughty, deal with it.");

exports.catchErrorEffect = function(ma) {
  return function(eToMa) {
    return function() {
      try {
        return ma();
      } catch (e) {
        return eToMa(e)();
      }
    }
  }
}

exports.throwErrorEffect = function(e) {
  return function() {
    throw e
  }
}

exports.bindEffect = function(ma) {
  return function(aToMb) {
    return function () {
      return aToMb(ma())();
    }
  }
}

exports.log = function(s) {
  return function() {
    console.log(s);
  }
}
```

```haskell
module Main where

data Unit = Unit

data Effect a

data Error

foreign import log :: String -> Effect Unit

class Bind m where
  bind :: forall a b. m a -> (a -> m b) -> m b

foreign import bindEffect :: forall a b. Effect a -> (a -> Effect b) -> Effect b

instance bindEffect_ :: Bind Effect where
  bind = bindEffect

class MonadThrow e m | m -> e where
  throwError :: forall a. e -> m a

foreign import throwErrorEffect :: forall a. Error -> Effect a

instance throwErrorEffect_ :: MonadThrow Error Effect where
  throwError = throwErrorEffect

class (MonadThrow e m) <= MonadError e m | m -> e where
  catchError :: forall a. m a -> (e -> m a) -> m a

foreign import catchErrorEffect :: forall a. Effect a -> (Error -> Effect a) -> Effect a

instance catchErrorEffect_ :: MonadError Error Effect where
  catchError = catchErrorEffect

foreign import evil :: Error

naughty :: Effect Unit
naughty = bind 
  (log "hello")
  (\_ -> bind (throwError evil) \_ -> log "world")

nice :: Effect Unit
nice = bind
  (log "hello")
  (\_ ->
    bind
      (catchError (throwError evil) (\_ -> log "dodged that bullet!"))
      (\_ -> log "world"))

main :: Effect Unit
main = nice

--main :: Effect Unit
--main = naughty
```