Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions source_md/for-a-few-monads-more.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ applyLog :: (Monoid m) => (a,m) -> (a -> (b,m)) -> (b,m)
applyLog (x,log) f = let (y,newLog) = f x in (y,log <> newLog)
```

Actually, this code works for any semigroup, not just monoids.
But I'll just ignore that, as we'll be using an identity value shortly anyway.

Because the accompanying value can now be any monoid value, we no longer have to think of the tuple as a value and a log, but now we can think of it as a value with an accompanying monoid value.
For instance, we can have a tuple that has an item name and an item price as the monoid value.
We just use the `Sum` newtype to make sure that the prices get added as we operate with the items.
Expand Down Expand Up @@ -526,12 +529,14 @@ fromDiffList (DiffList f) = f []
To make a normal list into a difference list we just do what we did before and make it a function that prepends it to another list.
Because a difference list is a function that prepends something to another list, if we just want that something, we apply the function to an empty list!

Here's the `Monoid` instance:
Here are the `Semigroup` and `Monoid` instances:

```{.haskell:hs}
instance Semigroup (DiffList a) where
DiffList f <> DiffList g = DiffList (\xs -> f (g xs))

instance Monoid (DiffList a) where
mempty = DiffList (\xs -> [] ++ xs)
(DiffList f) <> (DiffList g) = DiffList (\xs -> f (g xs))
```

Notice how for lists, `mempty` is just the `id` function and `<>` is actually just function composition.
Expand Down
93 changes: 56 additions & 37 deletions source_md/functors-applicative-functors-and-monoids.md
Original file line number Diff line number Diff line change
Expand Up @@ -1486,18 +1486,20 @@ We call this property *associativity*.
`*` is associative, and so is `++`, but `-`, for example, is not.
The expressions `(5 - 3) - 4` and `5 - (3 - 4)` result in different numbers.

By noticing and writing down these properties, we have chanced upon *monoids*!
A monoid is when you have an associative binary function and a value which acts as an identity with respect to that function.
By noticing and writing down these properties, we have chanced upon *semigroups* and *monoids*!
A semigroup is when you have an associative binary function, and a monoid is when you also have a value which acts as an identity with respect to that function.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
A semigroup is when you have an associative binary function, and a monoid is when you also have a value which acts as an identity with respect to that function.
A semigroup is when you have an associative binary function, and a monoid is when you have a semigroup and a value that acts as an identity with respect to that function.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having just "also" there doesn't pull the weight I feel. It's important to be crystal clear about this hierarchy.

Copy link
Copy Markdown
Contributor Author

@MatthijsBlom MatthijsBlom Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "that function" reference feels broken, that way.

Maybe a total rewrite?

Suggested change
A semigroup is when you have an associative binary function, and a monoid is when you also have a value which acts as an identity with respect to that function.
When you have an associative binary function, we call that a semigroup.
And if you also have a value that acts as an identity with respect to that function, then we call it a monoid instead.

( The 'instead' is there to make clear that it is the whole that is called the 'monoid', not just the identity element by itself. )

When something acts as an identity with respect to a function, it means that when called with that function and some other value, the result is always equal to that other value.
`1` is the identity with respect to `*` and `[]` is the identity with respect to `++`.
There are a lot of other monoids to be found in the world of Haskell, which is why the `Monoid` type class exists.
It's for types which can act like monoids.
Let's see how the type class is defined:
There are a lot of other monoids and semigroups to be found in the world of Haskell, which is why the `Monoid` and `Semigroup` type classes exist.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
There are a lot of other monoids and semigroups to be found in the world of Haskell, which is why the `Monoid` and `Semigroup` type classes exist.
There are a lot of other monoids and semigroups to be found in the world of Haskell, which is why the respective type classes (`Semigroup` and `Monoid`) exist.

would read a little cleaner I feel.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would agree, if this weren't the first time these classes were named/mentioned.

It's for types which can act like monoids and semigroups.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ordering in "monoids and semigroups" is a bit confusing, I feel, because semigroups come first in the sense that any monoid is a semigroup as well. But maybe it's not a big deal. Just in case, I add a suggestion but it's up to you whether to acccept it.

Suggested change
It's for types which can act like monoids and semigroups.
It's for types which can act like semigroups and monoids.

Copy link
Copy Markdown
Contributor Author

@MatthijsBlom MatthijsBlom Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not expect the reader's intuition to be clear on this yet, at this point in the text, so I do not expect a problem.

If these two words are to be swapped, I think the preceding two occurrences should be swapped as well, to preserve the 'rhythm'.

Currently the 'most important' of the two comes first, in these two sentences, somewhat in agreement with the title of this section.

Let's see how the type classes are defined:

```{.haskell:hs}
class Monoid m where
mempty :: m
class Semigroup m where
(<>) :: m -> m -> m

class Semigroup m => Monoid m where
mempty :: m
mconcat :: [m] -> m
mconcat = foldr (<>) mempty
```
Expand All @@ -1508,6 +1510,8 @@ Let's take some time and get properly acquainted with it.

First of all, we see that only concrete types can be made instances of `Monoid`, because the `m` in the type class definition doesn't take any type parameters.
This is different from `Functor` and `Applicative`, which require their instances to be type constructors which take one parameter.
We also see that all `Monoid`s must also be `Semigroup`s, just like all `Applicative`s must be `Functor`s.
This is because monoids are a special kind of semigroup, as in addition to the associative operation they also have a matching identity value.

The first function is `mempty`.
It's not really a function, since it doesn't take parameters, so it's a polymorphic constant, kind of like `minBound` from `Bounded`.
Expand All @@ -1527,29 +1531,32 @@ Because the default implementation is fine for most instances, we won't concern
When making a type an instance of `Monoid`, it suffices to just implement `mempty` and `<>`.
The reason `mconcat` is there at all is because for some instances, there might be a more efficient way to implement `mconcat`, but for most instances the default implementation is just fine.

Before moving on to specific instances of `Monoid`, let's take a brief look at the monoid laws.
Before moving on to specific instances of `Monoid`, let's take a brief look at the semigroup and monoid laws.
We mentioned that there has to be a value that acts as the identity with respect to the binary function and that the binary function has to be associative.
It's possible to make instances of `Monoid` that don't follow these rules, but such instances are of no use to anyone because when using the `Monoid` type class, we rely on its instances acting like monoids.
It's possible to make instances that don't follow these rules, but such instances are of no use to anyone because when using the `Monoid` type class, we rely on its instances acting like monoids.
Otherwise, what's the point?
That's why when making instances, we have to make sure they follow these laws:

* `mempty <> x = x`{.label .law}
* `x <> mempty = x`{.label .law}
* `(x <> y) <> z = x <> (y <> z)`{.label .law}
* `(x <> y) <> z = x <> (y <> z)`{.label .law} (semigroup law)
* `mempty <> x = x`{.label .law} (monoid law)
* `x <> mempty = x`{.label .law} (monoid law)

The first two state that `mempty` has to act as the identity with respect to `<>` and the third says that `<>` has to be associative i.e. that it the order in which we use `<>` to reduce several monoid values into one doesn't matter.
The first says that `<>` has to be associative i.e. that the order in which we use `<>` to reduce several values into one doesn't matter, and the other two state that `mempty` has to act as the identity with respect to `<>`.
Semigroups follow the first law, but monoids follow all three.
Haskell doesn't enforce these laws, so we as the programmer have to be careful that our instances do indeed obey them.

### Lists are monoids

Yes, lists are monoids!
Like we've seen, the `++` function and the empty list `[]` form a monoid.
The instance is very simple:
The instances are very simple:

```{.haskell:hs}
instance Semigroup [a] where
(<>) = (++)

instance Monoid [a] where
mempty = []
(<>) = (++)
```

Lists are an instance of the `Monoid` type class regardless of the type of the elements they hold.
Expand Down Expand Up @@ -1633,12 +1640,14 @@ newtype Product a = Product { getProduct :: a }
```

Simple, just a `newtype` wrapper with one type parameter along with some derived instances.
Its instance for `Monoid` goes a little something like this:
Its instances for `Semigroup` and `Monoid` go a little something like this:

```{.haskell:hs}
instance Num a => Semigroup (Product a) where
Product x <> Product y = Product (x * y)

instance Num a => Monoid (Product a) where
mempty = Product 1
Product x <> Product y = Product (x * y)
```

`mempty` is just `1` wrapped in a `Product` constructor.
Expand Down Expand Up @@ -1687,12 +1696,14 @@ newtype Any = Any { getAny :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
```

Its instance looks goes like so:
Its instances look like so:

```{.haskell:hs}
instance Semigroup Any where
Any x <> Any y = Any (x || y)

instance Monoid Any where
mempty = Any False
Any x <> Any y = Any (x || y)
```

The reason it's called `Any` is because `x <> y` will be `True` if *any* one of those two is `True`.
Expand All @@ -1718,12 +1729,14 @@ newtype All = All { getAll :: Bool }
deriving (Eq, Ord, Read, Show, Bounded)
```

And this is the instance:
And these are the instances:

```{.haskell:hs}
instance Semigroup All where
All x <> All y = All (x && y)

instance Monoid All where
mempty = All True
All x <> All y = All (x && y)
```

When we `<>` values of the `All` type, the result will be `True` only if *all* the values used in the `<>` operations are `True`:
Expand Down Expand Up @@ -1760,11 +1773,13 @@ With lists, numbers and boolean values, finding monoids was just a matter of loo
With `Ordering`, we have to look a bit harder to recognize a monoid, but it turns out that its `Monoid` instance is just as intuitive as the ones we've met so far and also quite useful:

```{.haskell:hs}
instance Monoid Ordering where
mempty = EQ
instance Semigroup Ordering where
LT <> _ = LT
EQ <> y = y
GT <> _ = GT

instance Monoid Ordering where
mempty = EQ
```

![did anyone ORDER pizza?!?! I can't BEAR these puns!](assets/images/functors-applicative-functors-and-monoids/bear.png){.right width=330 height=339}
Expand Down Expand Up @@ -1859,23 +1874,25 @@ The `Ordering` monoid is very cool because it allows us to easily compare things

Let's take a look at the various ways that `Maybe a` can be made an instance of `Monoid` and what those instances are useful for.

One way is to treat `Maybe a` as a monoid only if its type parameter `a` is a monoid as well and then implement `<>` in such a way that it uses the `<>` operation of the values that are wrapped with `Just`.
One way is to treat `Maybe a` as a monoid only if its type parameter `a` is a semigroup (or even a monoid) and then implement `<>` in such a way that it uses the `<>` operation of the values that are wrapped with `Just`.
We use `Nothing` as the identity, and so if one of the two values that we're `<>`ing is `Nothing`, we keep the other value.
Here's the instance declaration:
Here are the instance declarations:

```{.haskell:hs}
instance Monoid a => Monoid (Maybe a) where
mempty = Nothing
instance Semigroup a => Semigroup (Maybe a) where
Nothing <> m = m
m <> Nothing = m
Just m1 <> Just m2 = Just (m1 <> m2)

instance Semigroup a => Monoid (Maybe a) where
mempty = Nothing
```

Notice the class constraint.
It says that `Maybe a` is an instance of `Monoid` only if `a` is an instance of `Monoid`.
It says that `Maybe a` is an instance of `Monoid` only if `a` is an instance of `Semigroup`.
If we `<>` something with a `Nothing`, the result is that something.
If we `<>` two `Just` values, the contents of the `Just`s get `<>`ed and then wrapped back in a `Just`.
We can do this because the class constraint ensures that the type of what's inside the `Just` is an instance of `Monoid`.
We can do this because the class constraint ensures that the type of what's inside the `Just` is an instance of `Semigroup`.

```{.haskell:hs}
ghci> Nothing <> Just "andy"
Expand All @@ -1886,12 +1903,12 @@ ghci> Just (Sum 3) <> Just (Sum 4)
Just (Sum {getSum = 7})
```

This comes in use when you're dealing with monoids as results of computations that may have failed.
Because of this instance, we don't have to check if the computations have failed by seeing if they're a `Nothing` or `Just` value; we can just continue to treat them as normal monoids.
This comes in handy when you're dealing with semigroups as results of computations that may have failed.
Because of this instance, we don't have to check if the computations have failed by seeing if they're a `Nothing` or `Just` value; we can just continue to treat them as normal semigroups.

But what if the type of the contents of the `Maybe` aren't an instance of `Monoid`?
Notice that in the previous instance declaration, the only case where we have to rely on the contents being monoids is when both parameters of `<>` are `Just` values.
But if we don't know if the contents are monoids, we can't use `<>` between them, so what are we to do?
But what if the type of the contents of the `Maybe` aren't an instance of `Semigroup`?
Notice that in the previous instance declaration, the only case where we have to rely on the contents being a semigroup is when both parameters of `<>` are `Just` values.
But if we don't know if the contents are a semigroup, we can't use `<>` between them, so what are we to do?
Well, one thing we can do is to just discard the second value and keep the first one.
For this, the `First a` type exists and this is its definition:

Expand All @@ -1901,13 +1918,15 @@ newtype First a = First { getFirst :: Maybe a }
```

We take a `Maybe a` and we wrap it with a `newtype`.
The `Monoid` instance is as follows:
The `Semigroup` and `Monoid` instances are as follows:

```{.haskell:hs}
instance Monoid (First a) where
mempty = First Nothing
instance Semigroup (First a) where
First (Just x) <> _ = First (Just x)
First Nothing <> x = x

instance Monoid (First a) where
mempty = First Nothing
```

Just like we said.
Expand Down