-
Notifications
You must be signed in to change notification settings - Fork 47
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
base: main
Are you sure you want to change the base?
Changes from all commits
813f397
4271978
c803361
866319f
ceb1d8b
2c5b70c
8cd19ad
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 => 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</code></pre> | ||
x >> y = x >>= \_ -> 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) = > 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 => 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) => a -> m a</code>. It | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe change the preceding paragraph to say that |
||
<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 >>= f = Nothing | ||
Just x >>= 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 >>= f = f x</code></pre> | ||
<p>Both <code>return</code> and <code>(>>)</code> have <em>default | ||
implementations</em>, so we omit them in instances. <code>return</code> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pandoc converts |
||
is the same as <code>pure</code>, it wraps a value in | ||
<code>Just</code>.</p> | ||
<p>The <code>>>=</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> | ||
|
@@ -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) => String -> 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 => MonadFail m where | ||
fail :: String -> 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 | ||
|
@@ -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 >>= f = concat (map f xs) | ||
fail _ = []</code></pre> | ||
xs >>= 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 | ||
|
@@ -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 => MonadPlus m where | ||
mzero :: m a | ||
mplus :: m a -> m a -> 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 => Alternative f where | ||
empty :: f a | ||
(<|>) :: f a -> f a -> f a</code></pre> | ||
<p><code>empty</code> is synonymous to <code>mempty</code> from the | ||
<code>Monoid</code> type class and <code>(<|>)</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 = [] | ||
(<|>) = (++)</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) => Bool -> m () | ||
guard True = return () | ||
guard False = mzero</code></pre> | ||
<code>(<|>)</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) => Bool -> 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 | ||
|
There was a problem hiding this comment.
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 thatreturn
always does the same aspure
but that it's explicitly defined to bepure
?