frontend = comonad, backend = monad

mikesol

Mike Solomon

Posted on March 25, 2021

frontend = comonad, backend = monad

I'm writing this short article based on two observations:

  1. The internet is filled with many "what is a monad" articles but comparatively few "what is a comonad" articles.
  2. These "what is a monad" articles are often written for frontend developers.

I'd like to set the record straight in this article by claiming, in the most hand-wavey and unscientific of ways, that frontend = comonad, backend = monad. So, if you are frontend engineer, learning about comonads is IMO the way to go.

This observation is not at all new. Phil Freeman's article The Future is Comonadic makes the point that working with UIs is tantamount to working with a comonad. So I'm writing this article mostly to amalgamate different strands of thought, clear up confusion, and put a name on certain industry trends.

Monad vs comonad

A monad m providing a context for type a is equipped with two operations:

pure :: a -> m a
join :: m (m a) -> m a
Enter fullscreen mode Exit fullscreen mode

A comonad w providing a context for type a is equipped with two operations:

extract :: w a -> a
duplicate :: w a -> w (w a)
Enter fullscreen mode Exit fullscreen mode

The only difference between the two is what's on the left and right of the arrow.

An example of a monad and a comonad

Let's take a quick gander at a monad and a comonad in the wild.

Our monad - Maybe

Maybe is a monad that provides a context of existing or not existing to a value of type a.

data Maybe a = Just a | Nothing
Enter fullscreen mode Exit fullscreen mode

pure for Maybe is defined like this:

pure :: a -> Maybe a
pure = Just
Enter fullscreen mode Exit fullscreen mode

We could have defined it like this:

pure :: a -> Maybe a
pure _ = Nothing
Enter fullscreen mode Exit fullscreen mode

But that wouldn't be very nice, as it would destroy all the input values. While perfectly legal, the community has rallied around the first pure with Just because it is closer to our intuition about how pure for Maybe should behave.

join is defined like this:

join :: Maybe (Maybe a) -> Maybe a
join (Just x) = x
join Nothing = Nothing
Enter fullscreen mode Exit fullscreen mode

As maybes pile up, join gives us a way to squish them back down to a single level of Maybe-ness.

Our comonad - Stream

Stream is a comonad that provides an infinite list of values.

data Stream a = Stream a (Stream a)
Enter fullscreen mode Exit fullscreen mode

In that context, extract gets the head of the stream

extract :: Stream a -> a
extract (Stream head rest) = head
Enter fullscreen mode Exit fullscreen mode

duplicate gets the tail of the stream.

duplicate :: Stream a -> Stream (Stream a)
duplicate (Stream head rest) = Stream rest
Enter fullscreen mode Exit fullscreen mode

You can then call extract on the result of duplicate to get a Stream and get its head with extract.

Backend vs frontend

Backend

In backend development, there are two main tasks:

  1. Reigning in the chaos (API calls, flaky hardware, working with the filesystem, GPUs, blech...)
  2. Inserting our business logic, the only thing we "know", into this madness to produce value for someone somewhere.

These correspond to:

  1. join : Reigning in the chaos.
  2. pure : Bringing our business logic to the party.

Frontend

In frontend development, there are two main tasks:

  1. Managing and anticipating possible future scenarios so that when a user does thing x or y it "just works".
  2. Rendering a UI for a user.

These correspond to:

  1. duplicate : Projecting into the future (the tail of our stream).
  2. extract : Rendering a UI.

Community trends

Massive community trends can be boiled down to the comonad vs monad divide.

Backend: Kubernetes is a monad

Kubernetes is a monad. You call pure on bits of your stack to get it into a pod, and you call join on pods so that you never have kubernetes-in-kubernetes: there's always just one level.

Frontend: Nextjs is a comonad

Nextjs is a comonad. You call extract to take JavaScript and make it a static webpage via its SSR, and React hooks are essentially duplicate: they project a value into the future by returning a callback.

Ask for a comonad tutorial

So, if you're a frontend dev and rolling your eyes at "not another monad tutorial", ask for a comonad tutorial. Chances are it will be much more relevant to your day-to-day work!

💖 💪 🙅 🚩
mikesol
Mike Solomon

Posted on March 25, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related