Skip to content

Use modern definitions for Monad and Alternative #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
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
98 changes: 40 additions & 58 deletions markdown/generated_html/a-fistful-of-monads.html
Original file line number Diff line number Diff line change
Expand Up @@ -254,29 +254,20 @@ <h2 id="the-monad-type-class">The Monad type class</h2>
applicative functors have the <code>Applicative</code> type class,
monads come with their own type class: <code>Monad</code>! Wow, who
would have thought? This is what the type class looks like:</p>
<pre class="haskell:hs"><code>class Monad m where
<pre class="haskell:hs"><code>class Applicative m =&gt; Monad m where
return :: a -&gt; m a
return = pure

(&gt;&gt;=) :: m a -&gt; (a -&gt; m b) -&gt; m b

(&gt;&gt;) :: m a -&gt; m b -&gt; m b
x &gt;&gt; y = x &gt;&gt;= \_ -&gt; y

fail :: String -&gt; m a
fail msg = error msg</code></pre>
x &gt;&gt; y = x &gt;&gt;= \_ -&gt; y</code></pre>
<p><img src="assets/images/a-fistful-of-monads/kid.png" class="right"
width="363" height="451" alt="this is you on monads" /></p>
<p>Let’s start with the first line. It says
<code>class Monad m where</code>. But wait, didn’t we say that monads
are just beefed up applicative functors? Shouldn’t there be a class
constraint in there along the lines of
<code>class (Applicative m) = &gt; Monad m where</code> so that a type
has to be an applicative functor first before it can be made a monad?
Well, there should, but when Haskell was made, it hadn’t occurred to
people that applicative functors are a good fit for Haskell so they
weren’t in there. But rest assured, every monad is an applicative
functor, even if the <code>Monad</code> class declaration doesn’t say
so.</p>
<code>class Applicative =&gt; Monad m where</code> which means that if
we want to create an instance of Monad for some type, we must have an
instance of Applicative for that type.</p>
<p>The first function that the <code>Monad</code> type class defines is
<code>return</code>. It’s the same as <code>pure</code>, only with a
different name. Its type is <code>(Monad m) =&gt; a -&gt; m a</code>. It
Copy link
Collaborator

Choose a reason for hiding this comment

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

Because the line return = pure was added, maybe change this part to say not just that return always does the same as pure but that it's explicitly defined to be pure?

Expand Down Expand Up @@ -305,22 +296,16 @@ <h2 id="the-monad-type-class">The Monad type class</h2>
attention to it for now because it comes with a default implementation
and we pretty much never implement it when making <code>Monad</code>
instances.</p>
<p>The final function of the <code>Monad</code> type class is
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe change the preceding paragraph to say that >> is the final function of the type class, instead of "next up", since the paragraph on fail has been deleted?

<code>fail</code>. We never use it explicitly in our code. Instead, it’s
used by Haskell to enable failure in a special syntactic construct for
monads that we’ll meet later. We don’t need to concern ourselves with
<code>fail</code> too much for now.</p>
<p>Now that we know what the <code>Monad</code> type class looks like,
let’s take a look at how <code>Maybe</code> is an instance of
<code>Monad</code>!</p>
<pre class="haskell:hs"><code>instance Monad Maybe where
return x = Just x
Nothing &gt;&gt;= f = Nothing
Just x &gt;&gt;= f = f x
fail _ = Nothing</code></pre>
<p><code>return</code> is the same as <code>pure</code>, so that one’s a
no-brainer. We do what we did in the <code>Applicative</code> type class
and wrap it in a <code>Just</code>.</p>
Just x &gt;&gt;= f = f x</code></pre>
<p>Both <code>return</code> and <code>(&gt;&gt;)</code> have <em>default
implementations</em>, so we omit them in instances. <code>return</code>
Copy link
Collaborator

Choose a reason for hiding this comment

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

Pandoc converts _italic text markdown_ to <em> tags, but unfortunately, the current /docs/style.css file redefines em to be bold. That means that to include italic text, you might have to change the CSS.

is the same as <code>pure</code>, it wraps a value in
<code>Just</code>.</p>
<p>The <code>&gt;&gt;=</code> function is the same as our
<code>applyMaybe</code>. When feeding the <code>Maybe a</code> to our
function, we keep in mind the context and return a <code>Nothing</code>
Expand Down Expand Up @@ -811,17 +796,16 @@ <h2 id="do-notation">do notation</h2>
produced right away, because the mechanism of falling through patterns
isn’t present in <code>let</code> expressions. When pattern matching
fails in a <code>do</code> expression, the <code>fail</code> function is
called. It’s part of the <code>Monad</code> type class and it enables
failed pattern matching to result in a failure in the context of the
current monad instead of making our program crash. Its default
implementation is this:</p>
<pre class="haskell:hs"><code>fail :: (Monad m) =&gt; String -&gt; m a
fail msg = error msg</code></pre>
<p>So by default it does make our program crash, but monads that
incorporate a context of possible failure (like <code>Maybe</code>)
usually implement it on their own. For <code>Maybe</code>, its
called. It’s part of the <code>MonadFail</code> type class and it
enables failed pattern matching to result in a failure in the context of
the current monad instead of making our program crash.</p>
<pre class="haskell:hs"><code>class Monad m =&gt; MonadFail m where
fail :: String -&gt; m a</code></pre>
<p>Monads that incorporate a context of possible failure (like
<code>Maybe</code>) usually implement it. For <code>Maybe</code>, its
implemented like so:</p>
<pre class="haskell:hs"><code>fail _ = Nothing</code></pre>
<pre class="haskell:hs"><code>instance MonadFail Maybe where
fail _ = Nothing</code></pre>
<p>It ignores the error message and makes a <code>Nothing</code>. So
when pattern matching fails in a <code>Maybe</code> value that’s written
in <code>do</code> notation, the whole value results in a
Expand Down Expand Up @@ -867,9 +851,7 @@ <h2 id="the-list-monad">The list monad</h2>
Let’s go ahead and see what the <code>Monad</code> instance for lists
looks like:</p>
<pre class="haskell:hs"><code>instance Monad [] where
return x = [x]
xs &gt;&gt;= f = concat (map f xs)
fail _ = []</code></pre>
xs &gt;&gt;= f = concat (map f xs)</code></pre>
<p><code>return</code> does the same thing as <code>pure</code>, so we
should already be familiar with <code>return</code> for lists. It takes
a value and puts it in a minimal default context that still yields that
Expand Down Expand Up @@ -986,26 +968,26 @@ <h2 id="the-list-monad">The list monad</h2>
a string and then we check if the character <code>'7'</code> is part of
that string. Pretty clever. To see how filtering in list comprehensions
translates to the list monad, we have to check out the
<code>guard</code> function and the <code>MonadPlus</code> type class.
The <code>MonadPlus</code> type class is for monads that can also act as
monoids. Here’s its definition:</p>
<pre class="haskell:hs"><code>class Monad m =&gt; MonadPlus m where
mzero :: m a
mplus :: m a -&gt; m a -&gt; m a</code></pre>
<p><code>mzero</code> is synonymous to <code>mempty</code> from the
<code>Monoid</code> type class and <code>mplus</code> corresponds to
<code>mappend</code>. Because lists are monoids as well as monads, they
can be made an instance of this type class:</p>
<pre class="haskell:hs"><code>instance MonadPlus [] where
mzero = []
mplus = (++)</code></pre>
<p>For lists <code>mzero</code> represents a non-deterministic
<code>guard</code> function and the <code>Alternative</code> type class.
The <code>Alternative</code> type class is for Applicatives that can
also act as monoids. Here’s its definition:</p>
<pre class="haskell:hs"><code>class Applicative f =&gt; Alternative f where
empty :: f a
(&lt;|&gt;) :: f a -&gt; f a -&gt; f a</code></pre>
<p><code>empty</code> is synonymous to <code>mempty</code> from the
<code>Monoid</code> type class and <code>(&lt;|&gt;)</code> corresponds
to <code>mappend</code>. Because lists are monoids as well as monads,
they can be made an instance of this type class:</p>
<pre class="haskell:hs"><code>instance Alternative [] where
empty = []
(&lt;|&gt;) = (++)</code></pre>
<p>For lists <code>empty</code> represents a non-deterministic
computation that has no results at all — a failed computation.
<code>mplus</code> joins two non-deterministic values into one. The
<code>guard</code> function is defined like this:</p>
<pre class="haskell:hs"><code>guard :: (MonadPlus m) =&gt; Bool -&gt; m ()
guard True = return ()
guard False = mzero</code></pre>
<code>(&lt;|&gt;)</code> joins two non-deterministic values into one.
The <code>guard</code> function is defined like this:</p>
<pre class="haskell:hs"><code>guard :: (Alternative m) =&gt; Bool -&gt; m ()
guard True = pure ()
guard False = empty</code></pre>
<p>It takes a boolean value and if it’s <code>True</code>, takes a
<code>()</code> and puts it in a minimal default context that still
succeeds. Otherwise, it makes a failed monadic value. Here it is in
Expand Down
75 changes: 30 additions & 45 deletions markdown/source_md/a-fistful-of-monads.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# A Fistful of Monads
# A Fistful of Monads

When we first talked about functors, we saw that they were a useful concept for values that can be mapped over.
Then, we took that concept one step further by introducing applicative functors, which allow us to view values of certain data types as values with contexts and use normal functions on those values while preserving the meaning of those contexts.
Expand Down Expand Up @@ -205,26 +205,20 @@ Wow, who would have thought?
This is what the type class looks like:

```{.haskell:hs}
class Monad m where
class Applicative m => Monad m where
return :: a -> m a
return = pure

(>>=) :: m a -> (a -> m b) -> m b

(>>) :: m a -> m b -> m b
x >> y = x >>= \_ -> y

fail :: String -> m a
fail msg = error msg
```

![this is you on monads](assets/images/a-fistful-of-monads/kid.png){.right width=363 height=451}

Let's start with the first line.
It says `class Monad m where`.
But wait, didn't we say that monads are just beefed up applicative functors?
Shouldn't there be a class constraint in there along the lines of `class (Applicative m) = > Monad m where` so that a type has to be an applicative functor first before it can be made a monad?
Well, there should, but when Haskell was made, it hadn't occurred to people that applicative functors are a good fit for Haskell so they weren't in there.
But rest assured, every monad is an applicative functor, even if the `Monad` class declaration doesn't say so.
It says `class Applicative => Monad m where`, which means that if we want to create an instance of `Monad` for some type, we must also have an instance of `Applicative` for that type.

The first function that the `Monad` type class defines is `return`.
It's the same as `pure`, only with a different name.
Expand All @@ -249,23 +243,16 @@ It's like function application, only instead of taking a normal value and feedin
Next up, we have `>>`.
We won't pay too much attention to it for now because it comes with a default implementation and we pretty much never implement it when making `Monad` instances.

The final function of the `Monad` type class is `fail`.
We never use it explicitly in our code.
Instead, it's used by Haskell to enable failure in a special syntactic construct for monads that we'll meet later.
We don't need to concern ourselves with `fail` too much for now.

Now that we know what the `Monad` type class looks like, let's take a look at how `Maybe` is an instance of `Monad`!

```{.haskell:hs}
instance Monad Maybe where
return x = Just x
Nothing >>= f = Nothing
Just x >>= f = f x
fail _ = Nothing
```

`return` is the same as `pure`, so that one's a no-brainer.
We do what we did in the `Applicative` type class and wrap it in a `Just`.
Both `return` and `(>>)` have _default implementations_, so we can omit them in instances.
`return` is the same as `pure`, it wraps a value in `Just`.

The `>>=` function is the same as our `applyMaybe`.
When feeding the `Maybe a` to our function, we keep in mind the context and return a `Nothing` if the value on the left is `Nothing` because if there's no value then there's no way to apply our function to it.
Expand Down Expand Up @@ -779,19 +766,19 @@ When matching on a pattern in a function fails, the next pattern is matched.
If the matching falls through all the patterns for a given function, an error is thrown and our program crashes.
On the other hand, failed pattern matching in `let` expressions results in an error being produced right away, because the mechanism of falling through patterns isn't present in `let` expressions.
When pattern matching fails in a `do` expression, the `fail` function is called.
It's part of the `Monad` type class and it enables failed pattern matching to result in a failure in the context of the current monad instead of making our program crash.
Its default implementation is this:
It's part of the `MonadFail` type class and it enables failed pattern matching to result in a failure in the context of the current monad, instead of making our program crash.

```{.haskell:hs}
fail :: (Monad m) => String -> m a
fail msg = error msg
class Monad m => MonadFail m where
fail :: String -> m a
```

So by default it does make our program crash, but monads that incorporate a context of possible failure (like `Maybe`) usually implement it on their own.
Monads that incorporate a context of possible failure (like `Maybe`) usually implement it.
For `Maybe`, its implemented like so:

```{.haskell:hs}
fail _ = Nothing
instance MonadFail Maybe where
fail _ = Nothing
```

It ignores the error message and makes a `Nothing`.
Expand Down Expand Up @@ -842,9 +829,7 @@ Let's go ahead and see what the `Monad` instance for lists looks like:

```{.haskell:hs}
instance Monad [] where
return x = [x]
xs >>= f = concat (map f xs)
fail _ = []
```

`return` does the same thing as `pure`, so we should already be familiar with `return` for lists.
Expand Down Expand Up @@ -962,33 +947,33 @@ ghci> [ x | x <- [1..50], '7' `elem` show x ]

We apply `show` to `x` to turn our number into a string and then we check if the character `'7'` is part of that string.
Pretty clever.
To see how filtering in list comprehensions translates to the list monad, we have to check out the `guard` function and the `MonadPlus` type class.
The `MonadPlus` type class is for monads that can also act as monoids.
To see how filtering in list comprehensions translates to the list monad, we have to check out the `guard` function and the `Alternative` type class.
The `Alternative` type class is for applicative functors that can also act as monoids.
Here's its definition:

```{.haskell:hs}
class Monad m => MonadPlus m where
mzero :: m a
mplus :: m a -> m a -> m a
class Applicative f => Alternative f where
empty :: f a
(<|>) :: f a -> f a -> f a
```

`mzero` is synonymous to `mempty` from the `Monoid` type class and `mplus` corresponds to `mappend`.
`empty` is synonymous to `mempty` from the `Monoid` type class and `(<|>)` corresponds to `mappend`.
Because lists are monoids as well as monads, they can be made an instance of this type class:

```{.haskell:hs}
instance MonadPlus [] where
mzero = []
mplus = (++)
instance Alternative [] where
empty = []
(<|>) = (++)
```

For lists `mzero` represents a non-deterministic computation that has no results at all --- a failed computation.
`mplus` joins two non-deterministic values into one.
For lists, `empty` represents a non-deterministic computation that has no results at all --- a failed computation.
`(<|>)` joins two non-deterministic values into one.
The `guard` function is defined like this:

```{.haskell:hs}
guard :: (MonadPlus m) => Bool -> m ()
guard True = return ()
guard False = mzero
guard :: (Alternative m) => Bool -> m ()
guard True = pure ()
guard False = empty
```

It takes a boolean value and if it's `True`, takes a `()` and puts it in a minimal default context that still succeeds.
Expand Down Expand Up @@ -1052,7 +1037,7 @@ ghci> [ x | x <- [1..50], '7' `elem` show x ]

So filtering in list comprehensions is the same as using `guard`.

### A knight's quest
### A knight's quest

Here's a problem that really lends itself to being solved with non-determinism.
Say you have a chess board and only one knight piece on it.
Expand Down Expand Up @@ -1178,7 +1163,7 @@ It can't check if the monad laws hold for a type though, so if we're making a ne
We can rely on the types that come with the standard library to satisfy the laws, but later when we go about making our own monads, we're going to have to manually check the if the laws hold.
But don't worry, they're not complicated.

### Left identity
### Left identity

The first monad law states that if we take a value, put it in a default context with `return` and then feed it to a function by using `>>=`, it's the same as just taking the value and applying the function to it.
To put it formally:
Expand Down Expand Up @@ -1211,7 +1196,7 @@ ghci> (\x -> [x,x,x]) "WoM"
We said that for `IO`, using `return` makes an I/O action that has no side-effects but just presents a value as its result.
So it makes sense that this law holds for `IO` as well.

### Right identity
### Right identity

The second law states that if we have a monadic value and we use `>>=` to feed it to `return`, the result is our original monadic value.
Formally:
Expand Down Expand Up @@ -1245,7 +1230,7 @@ So when we feed `[1,2,3,4]` to `return`, first `return` gets mapped over `[1,2,3
Left identity and right identity are basically laws that describe how `return` should behave.
It's an important function for making normal values into monadic ones and it wouldn't be good if the monadic value that it produced did a lot of other stuff.

### Associativity
### Associativity

The final monad law says that when we have a chain of monadic function applications with `>>=`, it shouldn't matter how they're nested.
Formally written:
Expand Down