diff --git a/markdown/generated_html/a-fistful-of-monads.html b/markdown/generated_html/a-fistful-of-monads.html index 226f740..5c95877 100644 --- a/markdown/generated_html/a-fistful-of-monads.html +++ b/markdown/generated_html/a-fistful-of-monads.html @@ -254,29 +254,20 @@

The Monad type class

applicative functors have the Applicative type class, monads come with their own type class: Monad! Wow, who would have thought? This is what the type class looks like:

-
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
+ x >> y = x >>= \_ -> y

this is you on monads

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.

+class Applicative => Monad m where 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.

The first function that the Monad type class defines is return. It’s the same as pure, only with a different name. Its type is (Monad m) => a -> m a. It @@ -305,22 +296,16 @@

The Monad type class

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!

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.

+ Just x >>= f = f x +

Both return and (>>) have default +implementations, so we 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 @@ -811,17 +796,16 @@

do notation

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:

-
fail :: (Monad m) => String -> m a
-fail msg = error msg
-

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. For Maybe, its +called. 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.

+
class Monad m => MonadFail m where
+    fail :: String -> m a
+

Monads that incorporate a context of possible failure (like +Maybe) usually implement it. For Maybe, its implemented like so:

-
fail _ = Nothing
+
instance MonadFail Maybe where
+    fail _ = Nothing

It ignores the error message and makes a Nothing. So when pattern matching fails in a Maybe value that’s written in do notation, the whole value results in a @@ -867,9 +851,7 @@

The list monad

Let’s go ahead and see what the Monad instance for lists looks like:

instance Monad [] where
-    return x = [x]
-    xs >>= f = concat (map f xs)
-    fail _ = []
+ xs >>= f = concat (map f xs)

return does the same thing as pure, so we should already be familiar with return for lists. It takes a value and puts it in a minimal default context that still yields that @@ -986,26 +968,26 @@

The list monad

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. Here’s its definition:

-
class Monad m => MonadPlus m where
-    mzero :: m a
-    mplus :: m a -> m a -> m a
-

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

-
instance MonadPlus [] where
-    mzero = []
-    mplus = (++)
-

For lists mzero represents a non-deterministic +guard function and the Alternative type class. +The Alternative type class is for Applicatives that can +also act as monoids. Here’s its definition:

+
class Applicative f => Alternative f where
+    empty :: f a
+    (<|>) :: f a -> f a -> f a
+

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:

+
instance Alternative [] where
+    empty = []
+    (<|>) = (++)
+

For lists empty represents a non-deterministic computation that has no results at all — a failed computation. -mplus joins two non-deterministic values into one. The -guard function is defined like this:

-
guard :: (MonadPlus m) => Bool -> m ()
-guard True = return ()
-guard False = mzero
+(<|>) joins two non-deterministic values into one. +The guard function is defined like this:

+
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. Otherwise, it makes a failed monadic value. Here it is in diff --git a/markdown/source_md/a-fistful-of-monads.md b/markdown/source_md/a-fistful-of-monads.md index 9eaf959..00c3d55 100644 --- a/markdown/source_md/a-fistful-of-monads.md +++ b/markdown/source_md/a-fistful-of-monads.md @@ -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. @@ -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. @@ -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. @@ -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`. @@ -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. @@ -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. @@ -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. @@ -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: @@ -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: @@ -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: