Skip to content

Commit fc9e79d

Browse files
committed
introduce Semigroup
1 parent 2fe349f commit fc9e79d

2 files changed

Lines changed: 53 additions & 32 deletions

File tree

source_md/for-a-few-monads-more.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -529,9 +529,11 @@ Because a difference list is a function that prepends something to another list,
529529
Here's the `Monoid` instance:
530530

531531
```{.haskell:hs}
532+
instance Semigroup (DiffList a) where
533+
DiffList f <> DiffList g = DiffList (\xs -> f (g xs))
534+
532535
instance Monoid (DiffList a) where
533536
mempty = DiffList (\xs -> [] ++ xs)
534-
(DiffList f) <> (DiffList g) = DiffList (\xs -> f (g xs))
535537
```
536538

537539
Notice how for lists, `mempty` is just the `id` function and `<>` is actually just function composition.

source_md/functors-applicative-functors-and-monoids.md

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1486,18 +1486,20 @@ We call this property *associativity*.
14861486
`*` is associative, and so is `++`, but `-`, for example, is not.
14871487
The expressions `(5 - 3) - 4` and `5 - (3 - 4)` result in different numbers.
14881488

1489-
By noticing and writing down these properties, we have chanced upon *monoids*!
1490-
A monoid is when you have an associative binary function and a value which acts as an identity with respect to that function.
1489+
By noticing and writing down these properties, we have chanced upon *semigroups* and *monoids*!
1490+
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.
14911491
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.
14921492
`1` is the identity with respect to `*` and `[]` is the identity with respect to `++`.
1493-
There are a lot of other monoids to be found in the world of Haskell, which is why the `Monoid` type class exists.
1494-
It's for types which can act like monoids.
1495-
Let's see how the type class is defined:
1493+
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.
1494+
It's for types which can act like monoids and semigroups.
1495+
Let's see how the type classes are defined:
14961496

14971497
```{.haskell:hs}
1498-
class Monoid m where
1499-
mempty :: m
1498+
class Semigroup m where
15001499
(<>) :: m -> m -> m
1500+
1501+
class Semigroup m => Monoid m where
1502+
mempty :: m
15011503
mconcat :: [m] -> m
15021504
mconcat = foldr (<>) mempty
15031505
```
@@ -1508,6 +1510,8 @@ Let's take some time and get properly acquainted with it.
15081510

15091511
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.
15101512
This is different from `Functor` and `Applicative`, which require their instances to be type constructors which take one parameter.
1513+
We also see that all `Monoid`s must also be `Semigroup`s, just like all `Applicative`s must be `Functor`s.
1514+
This is because monoids are a special kind of semigroup, as they also have an identity value.
15111515

15121516
The first function is `mempty`.
15131517
It's not really a function, since it doesn't take parameters, so it's a polymorphic constant, kind of like `minBound` from `Bounded`.
@@ -1527,17 +1531,18 @@ Because the default implementation is fine for most instances, we won't concern
15271531
When making a type an instance of `Monoid`, it suffices to just implement `mempty` and `<>`.
15281532
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.
15291533

1530-
Before moving on to specific instances of `Monoid`, let's take a brief look at the monoid laws.
1534+
Before moving on to specific instances of `Monoid`, let's take a brief look at the semigroup and monoid laws.
15311535
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.
1532-
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.
1536+
It's possible to make instances of `Semigroup` and `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.
15331537
Otherwise, what's the point?
15341538
That's why when making instances, we have to make sure they follow these laws:
15351539

1536-
* `mempty <> x = x`{.label .law}
1537-
* `x <> mempty = x`{.label .law}
1538-
* `(x <> y) <> z = x <> (y <> z)`{.label .law}
1540+
* `(x <> y) <> z = x <> (y <> z)`{.label .law} (semigroup law)
1541+
* `mempty <> x = x`{.label .law} (monoid law)
1542+
* `x <> mempty = x`{.label .law} (monoid law)
15391543

1540-
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.
1544+
The first says that `<>` has to be associative i.e. that the order in which we use `<>` to reduce several semigroup values into one doesn't matter, and the other two state that `mempty` has to act as the identity with respect to `<>`.
1545+
Semigroups follow the first law, but monoids must follow all three.
15411546
Haskell doesn't enforce these laws, so we as the programmer have to be careful that our instances do indeed obey them.
15421547

15431548
### Lists are monoids
@@ -1547,9 +1552,11 @@ Like we've seen, the `++` function and the empty list `[]` form a monoid.
15471552
The instance is very simple:
15481553

15491554
```{.haskell:hs}
1555+
instance Semigroup [a] where
1556+
(<>) = (++)
1557+
15501558
instance Monoid [a] where
15511559
mempty = []
1552-
(<>) = (++)
15531560
```
15541561

15551562
Lists are an instance of the `Monoid` type class regardless of the type of the elements they hold.
@@ -1636,9 +1643,11 @@ Simple, just a *newtype* wrapper with one type parameter along with some derived
16361643
Its instance for `Monoid` goes a little something like this:
16371644

16381645
```{.haskell:hs}
1646+
instance Num a => Semigroup (Product a) where
1647+
Product x <> Product y = Product (x * y)
1648+
16391649
instance Num a => Monoid (Product a) where
16401650
mempty = Product 1
1641-
Product x <> Product y = Product (x * y)
16421651
```
16431652

16441653
`mempty` is just `1` wrapped in a `Product` constructor.
@@ -1690,9 +1699,11 @@ newtype Any = Any { getAny :: Bool }
16901699
Its instance looks goes like so:
16911700

16921701
```{.haskell:hs}
1702+
instance Semigroup Any where
1703+
Any x <> Any y = Any (x || y)
1704+
16931705
instance Monoid Any where
16941706
mempty = Any False
1695-
Any x <> Any y = Any (x || y)
16961707
```
16971708

16981709
The reason it's called `Any` is because `x <> y` will be `True` if *any* one of those two is `True`.
@@ -1721,9 +1732,11 @@ newtype All = All { getAll :: Bool }
17211732
And this is the instance:
17221733

17231734
```{.haskell:hs}
1735+
instance Semigroup All where
1736+
All x <> All y = All (x && y)
1737+
17241738
instance Monoid All where
17251739
mempty = All True
1726-
All x <> All y = All (x && y)
17271740
```
17281741

17291742
When we `<>` values of the `All` type, the result will be `True` only if *all* the values used in the `<>` operations are `True`:
@@ -1760,11 +1773,13 @@ With lists, numbers and boolean values, finding monoids was just a matter of loo
17601773
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:
17611774

17621775
```{.haskell:hs}
1763-
instance Monoid Ordering where
1764-
mempty = EQ
1776+
instance Semigroup Ordering where
17651777
LT <> _ = LT
17661778
EQ <> y = y
17671779
GT <> _ = GT
1780+
1781+
instance Monoid Ordering where
1782+
mempty = EQ
17681783
```
17691784

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

18601875
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.
18611876

1862-
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`.
1877+
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`.
18631878
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.
18641879
Here's the instance declaration:
18651880

18661881
```{.haskell:hs}
1867-
instance Monoid a => Monoid (Maybe a) where
1868-
mempty = Nothing
1882+
instance Semigroup a => Semigroup (Maybe a) where
18691883
Nothing <> m = m
18701884
m <> Nothing = m
18711885
Just m1 <> Just m2 = Just (m1 <> m2)
1886+
1887+
instance Semigroup a => Monoid (Maybe a) where
1888+
mempty = Nothing
18721889
```
18731890

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

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

1889-
This comes in use when you're dealing with monoids as results of computations that may have failed.
1890-
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.
1906+
This comes in use when you're dealing with semigroups as results of computations that may have failed.
1907+
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.
18911908

1892-
But what if the type of the contents of the `Maybe` aren't an instance of `Monoid`?
1893-
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.
1894-
But if we don't know if the contents are monoids, we can't use `<>` between them, so what are we to do?
1909+
But what if the type of the contents of the `Maybe` aren't an instance of `Semigroup`?
1910+
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.
1911+
But if we don't know if the contents are a semigroup, we can't use `<>` between them, so what are we to do?
18951912
Well, one thing we can do is to just discard the second value and keep the first one.
18961913
For this, the `First a` type exists and this is its definition:
18971914

@@ -1904,10 +1921,12 @@ We take a `Maybe a` and we wrap it with a *newtype*.
19041921
The `Monoid` instance is as follows:
19051922

19061923
```{.haskell:hs}
1907-
instance Monoid (First a) where
1908-
mempty = First Nothing
1924+
instance Semigroup (First a) where
19091925
First (Just x) <> _ = First (Just x)
19101926
First Nothing <> x = x
1927+
1928+
instance Monoid (First a) where
1929+
mempty = First Nothing
19111930
```
19121931

19131932
Just like we said.

0 commit comments

Comments
 (0)