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

Read on 👇^{}

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.^{}

- 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…^{}

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.^{}

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.

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")^{}

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:^{}

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…^{}

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.^{}

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.^{}

- "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.^{}

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^{}

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

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.^{}

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.

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…)^{}

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.
^{}

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]):^{}

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.^{}

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 wait, that's not all. You can also go backwards. Let me write a monad trait in the style of Cats.

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.^{}

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.^{}

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

You rock.
^{}

• • •

Missing some Tweet in this thread? You can try to
force a refresh

**
Keep Current with
Rock the JVM
**

Stay in touch and get notified when new unrolls are available from this author!

**
This Thread may be Removed Anytime!**

Twitter may remove this content at anytime! Save it as PDF for later use!

- Follow @ThreadReaderApp to mention us!
- From a Twitter thread mention us with a keyword "unroll"

`@threadreaderapp unroll`

Practice here first or read more on our help page!

Support us! We are indie developers!

This site is made by just two indie developers on a laptop doing marketing, support and development! Read more about the story.

**Become a Premium Member** ($3/month or $30/year) and get exclusive features!

Too expensive? **Make a small donation** by buying us coffee ($5) or help with server cost ($10)

Thank you for your support!

Check out other threads from @rockthejvm!

Read all threads by @rockthejvm