Rock the JVM Profile picture
Teaching #Scala, #Akka, #Spark and the JVM. Videos regularly at https://t.co/RHCs3tw9nZ with articles at https://t.co/iiFGd0okMq 🚀

Apr 7, 2021, 30 tweets

Monads are monoids in the category of endofunctors - a #Scala 3 journey, tweet-size, no psychobabble

Read on 👇

You'll need to know some abstract Scala and heard about:
- type lambdas
- functors
- monoids
- monads

at least in the practical sense that I talk about on the blog and YouTube channel. If you haven't, go to the blog/channel and run a quick search. I have them all.

Step 1 - monoids

Monoids are glorified combine functions with some properties. You've seen this in Cats.

Monoid reading here:

blog.rockthejvm.com/semigroups-and…

Step 2.

The thing is, this concept of "combining" can be generalized. The monoid above is equivalent to this other (stranger) form.

Notice that the API is returning functions instead of values.

These monoids are equivalent.

Why did I do that?

This (stranger) form can be further generalized:
- the Unit can be abstracted to some type U
- the tuple can be abstracted to some type P (for "product")

Now, notice that the methods return functions, which are the same as Function1 instances. We can generalize even the function concept:

This little monster is a representation of a "monoid in a monoidal category", or "monoid in the category of T", for short.

The monoid we know from Cats can easily derive from the general monster:

That monster can be taken one level up (or down?) by making it higher-kinded. Nothing else changed otherwise:

Step 3 - functors

Functors describe "mappable" things. You may know it from Cats. I talk about them in the practical #Scala sense on the blog and YouTube.

blog.rockthejvm.com/what-the-funct…

These functors have some properties (going to skip them).

Functors operate _between_ categories. In our case (puny Scala programmers), functors map to and from the _same_ category (Scala types).

In math jargon, those are called endofunctors. We have them here for free.

So we have
- "monoids in the category of"
- "endofunctors"

We need to stick the words together.

Step 4.1.

As programmers, we naturally transform values through functions. But for functors?

Functors have this thing called "natural transformations". Notice the structure similarity.

Example of a functor natural transformation in real life: List.headOption.

Other examples: .toList, .toTry, .toEither, .toOption

Step 4.2.

Squint at this type until colors blend.
It's found in Cats all the time.

Step 4.3.

Just as we compose functions in the style of compose(f, g) = x => f(g(x)), we can also compose functors.

But for endofunctors, we only care about composing them with functors of the same type.

Remember that F[F[A]] thing.

Here's a recap of the types involved.

Notice that nothing means anything anymore. I feel ya.

Step 5.

I've conveniently laid out the pieces so you can plug them into the little monster's type signature. This is what we get:

(read about type lambdas here: blog.rockthejvm.com/scala-3-type-l…)

Breathe.

Once we fit the pieces together, the monoid's methods are:

Because nothing means anything anymore, let's get back to the surface and get some air.

Here's an implementation of this mega-monoid for lists (which are functors; we can write a given Functor[List]):

Notice a really weird use of "combine".

In our first monoid, "combine" meant putting two values through a function. But "combine" can also mean
- parallelizing two futures
- cross-product of two sets
- flattening lists two levels deep (picture)

The whole damn concept changed.

The thing is, this general combine method is too general because it can mean anything and take any shape. So we came up with a much more useful primary API:

In other words, if you can define that kind of monoid, you've just written a monad.

Step 6.

But wait, that's not all. You can also go backwards. Let me write a monad trait in the style of Cats.

But if I can do that, then I can write the following methods for free:

I can also safely replace "extends Functor" with the type constraint (it "is" a functor), but kept it here for clarity.

So in reality, a Monad doesn't just extend Functor, but it also extends something more.

In other words, if you can write a monad, then you've just written a monoid in the category of (endo)functors.

Step 7.

Monads are not "just" monoids in the category of endofunctors, they are exactly monoids in the category of endofunctors.

You rock.

Share this Scrolly Tale with your friends.

A Scrolly Tale is a new way to read Twitter threads with a more visually immersive experience.
Discover more beautiful Scrolly Tales like this.

Keep scrolling