diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 91a75b9..aadc246 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -12,6 +12,11 @@ jobs: steps: - name: Checkout uses: actions/checkout@v5 + - name: Install pandoc + run: | + sudo apt install pandoc + - name: Generate HTML from Markdown + run: make - name: Upload artifact uses: actions/upload-pages-artifact@v4 with: diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50e074e --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +docs/*.html +!docs/index.html + +.DS_Store diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..5e22fb4 --- /dev/null +++ b/Makefile @@ -0,0 +1,13 @@ +## +# Build LYAH web site from Markdown sources using Pandoc and sed +# + +all: site + +site: + cd markdown && ./generate.sh + +clean: + find ./docs -name '*.html' -not -name 'index.html' -delete + +# end diff --git a/README.md b/README.md index 7f2a09f..3e9c8d5 100644 --- a/README.md +++ b/README.md @@ -22,11 +22,13 @@ The whole thing is completely free to read online, but the original is also avai - [ ] refactor web code - [ ] make UI more modern (whilst retaining as much of the original "feel" as possible) - [x] make content pages mobile-friendly (*still needs work) -- [ ] make homepage mobile-friendly +- [ ] make index page mobile-friendly - [x] prepare "content edit request" interface on GitHub - [ ] add exercises - [ ] update content (just overall, the outdated parts) +For some of these points, there are more focused issues on GitHub. + Don't forget to star the GitHub repository if you like it! 🙂 ## Discussion diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html deleted file mode 100644 index 79bae0b..0000000 --- a/docs/a-fistful-of-monads.html +++ /dev/null @@ -1,2165 +0,0 @@ - - - -A Fistful of Monads - Learn You a Haskell for Great Good! - - - - - - - - - - - -
-
- -

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

- -

-In this chapter, we'll learn about -monads, which are just beefed up applicative functors, much like -applicative functors are only beefed up functors. -

- -more cool than u - -

-When we started off with functors, we saw that it's possible to map functions -over various data types. We saw that for this purpose, the Functor -type class was introduced and it had us asking the question: when we have a -function of type a -> b and some data type -f a, how do we map that function over the data type -to end up with f b? We saw how to map something over -a Maybe a, a list [a], an IO -a etc. We even saw how to map a function a -> b -over other functions of type r -> a to get -functions of type r -> b. To answer this question -of how to map a function over some data type, all we had to do was look at the type -of fmap: -

- -
-fmap :: (Functor f) => (a -> b) -> f a -> f b
-
- -

-And then make it work for our data type by writing the appropriate Functor instance. -

- -

-Then we saw a possible improvement of functors and said, hey, what if that -function a -> b is already wrapped inside a -functor value? Like, what if we have Just (*3), how -do we apply that to Just 5? What if we don't want to -apply it to Just 5 but to a Nothing instead? Or if we have [(*2),(+4)], -how would we apply that to [1,2,3]? How would that -work even? For this, the Applicative type class was -introduced, in which we wanted the answer to the following type: -

- -
-(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
-
- -

-We also saw that we can take a normal value and wrap it inside a data type. For -instance, we can take a -1 and wrap it so that it becomes a -Just 1. Or we can make it into a [1]. Or an I/O action that does nothing and just yields -1. The function that does this is called pure. -

- -

-Like we said, an applicative value can be seen as a value with an added context. -A fancy value, to put it in technical terms. For instance, the character 'a' is just a normal character, whereas Just 'a' has some added context. Instead of a Char, we have a Maybe Char, -which tells us that its value might be a character, but it could also be an -absence of a character. -

- -

-It was neat to see how the Applicative type class -allowed us to use normal functions on these values with context and how that -context was preserved. Observe: -

- -
-ghci> (*) <$> Just 2 <*> Just 8
-Just 16
-ghci> (++) <$> Just "klingon" <*> Nothing
-Nothing
-ghci> (-) <$> [3,4] <*> [1,2,3]
-[2,1,0,3,2,1]
-
- -

-Ah, cool, so now that we treat them as applicative values, Maybe a values represent computations that might have -failed, [a] values represent computations that have -several results (non-deterministic computations), IO -a values represent values that have side-effects, etc. -

- -

-Monads are a natural extension of applicative functors and with them we're -concerned with this: if you have a value with a context, m -a, how do you apply to it a function that takes a normal a and returns a value with a context? That is, how do you -apply a function of type a -> m b to a value of -type m a? So essentially, we will want this function: -

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

-If we have a fancy value and a function that takes a normal value but -returns a fancy value, how do we feed that fancy value into the function? This -is the main question that we will concern ourselves when dealing with monads. We write -m a instead of f a because -the m stands for Monad, but monads are just -applicative functors that support >>=. The ->>= function is pronounced as bind. -

- -

-When we have a normal value a and a normal function -a -> b it's really easy to feed the value to the -function — you just apply the function to the value normally and that's -it. But when we're dealing with values that come with certain contexts, it takes -a bit of thinking to see how these fancy values are fed to functions and how to -take into account their behavior, but you'll see that it's easy as one two -three. -

- - -

Getting our feet wet with Maybe

- -monads, grasshoppa - -

-Now that we have a vague idea of what monads are about, let's see if we can make -that idea a bit less vague. -

- -

-Much to no one's surprise, Maybe is a monad, so let's -explore it a bit more and see if we can combine it with what we know about -monads. -

- -
-Make sure you understand applicatives at this point. It's good if you have a -feel for how the various Applicative instances work -and what kind of computations they represent, because monads are nothing more -than taking our existing applicative knowledge and upgrading it. -
- -

-A value of type Maybe a represents a value of type -a with the context of possible failure attached. A -value of Just "dharma" means that the string "dharma" is there whereas a value of -Nothing represents its absence, or if you look at the -string as the result of a computation, it means that the computation has failed. -

- -

-When we looked at Maybe as a functor, we saw that if -we want to fmap a function over it, it gets mapped -over the insides if it's a Just value, otherwise the -Nothing is kept because there's nothing to map it -over! -

- -

-Like this: -

- -
-ghci> fmap (++"!") (Just "wisdom")
-Just "wisdom!"
-ghci> fmap (++"!") Nothing
-Nothing
-
- -

-As an applicative functor, it functions similarly. However, applicatives also -have the function wrapped. Maybe is an applicative -functor in such a way that when we use <*> to -apply a function inside a Maybe to a value that's -inside a Maybe, they both have to be Just values for the result to be a Just value, otherwise the result is Nothing. It makes sense because if you're missing either -the function or the thing you're applying it to, you can't make something up out -of thin air, so you have to propagate the failure: -

- -
-ghci> Just (+3) <*> Just 3
-Just 6
-ghci> Nothing <*> Just "greed"
-Nothing
-ghci> Just ord <*> Nothing
-Nothing
-
- -

-When we use the applicative style to have normal functions act on Maybe values, it's similar. All the values have to be Just values, otherwise it's all for Nothing! -

- -
-ghci> max <$> Just 3 <*> Just 6
-Just 6
-ghci> max <$> Just 3 <*> Nothing
-Nothing
-
- -

-And now, let's think about how we would do >>= -for Maybe. Like we said, >>= -takes a monadic value, and a function that takes a normal value and returns a -monadic value and manages to apply that function to the monadic value. How does -it do that, if the function takes a normal value? Well, to do that, it has to -take into account the context of that monadic value. -

- -

-In this case, >>= would take a Maybe a value and a function of type a -> Maybe b -and somehow apply the function to the Maybe a. To -figure out how it does that, we can use the intuition that we have from Maybe being an applicative functor. Let's say that we have -a function \x -> Just (x+1). It takes a number, -adds 1 to it and wraps it in a Just: -

- - -
-ghci> (\x -> Just (x+1)) 1
-Just 2
-ghci> (\x -> Just (x+1)) 100
-Just 101
-
- -

-If we feed it 1, it evaluates to Just 2. If we give it the number 100, the result is Just 101. -Very straightforward. Now here's the kicker: how do we feed a -Maybe value to this function? If we think about how -Maybe acts as an applicative functor, answering this -is pretty easy. If we feed it a Just value, take what's inside -the Just and apply the function to it. If give it -a Nothing, hmm, well, then we're left with a -function but Nothing to apply it to. In that case, -let's just do what we did before and say that the result is Nothing. -

- -

-Instead of calling it >>=, let's call it -applyMaybe for now. It takes a Maybe a and a function that returns a Maybe b and manages to apply that function to the Maybe a. Here it is in code: -

- -
-applyMaybe :: Maybe a -> (a -> Maybe b) -> Maybe b
-applyMaybe Nothing f  = Nothing
-applyMaybe (Just x) f = f x
-
- -

-Okay, now let's play with it for a bit. We'll use it as an infix function so that -the Maybe value is on the left side and the function on -the right: -

- -
-ghci> Just 3 `applyMaybe` \x -> Just (x+1)
-Just 4
-ghci> Just "smile" `applyMaybe` \x -> Just (x ++ " :)")
-Just "smile :)"
-ghci> Nothing `applyMaybe` \x -> Just (x+1)
-Nothing
-ghci> Nothing `applyMaybe` \x -> Just (x ++ " :)")
-Nothing
-
- -

-In the above example, we see that when we used applyMaybe with a Just value and a function, the function simply got applied to the value inside the Just. When we tried to use it with a Nothing, the whole result was Nothing. -What about if the function returns a Nothing? Let's -see: -

- -
-ghci> Just 3 `applyMaybe` \x -> if x > 2 then Just x else Nothing
-Just 3
-ghci> Just 1 `applyMaybe` \x -> if x > 2 then Just x else Nothing
-Nothing
-
- -

-Just what we expected. If the monadic value on the left is a Nothing, the whole thing is Nothing. -And if the function on the right returns a Nothing, -the result is Nothing again. This is very similar to when -we used Maybe as an applicative and we got a Nothing result if somewhere in there was a Nothing. -

- -

-It looks like that for Maybe, we've figured out how -to take a fancy value and feed it to a function that takes a normal value and -returns a fancy one. We did this by keeping in mind that a Maybe value represents a computation that might have -failed. -

- -

-You might be asking yourself, how is this useful? It may seem like applicative -functors are stronger than monads, since applicative functors allow us to take a -normal function and make it operate on values with contexts. We'll see that -monads can do that as well because they're an upgrade of applicative functors, -and that they can also do some cool stuff that applicative functors can't. -

- -

-We'll come back to Maybe in a minute, but first, -let's check out the type class that belongs to monads. -

- - -

The Monad type class

- -

-Just like functors have the Functor type class and 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
-    return :: a -> m a
-
-    (>>=) :: 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 -

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

- -

-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 takes a value and puts it -in a minimal default context that still holds that value. In other words, it -takes something and wraps it in a monad. It always does the same thing as the -pure function from the Applicative type class, which means we're already -acquainted with return. We already used return when doing I/O. We used it to take a value and make -a bogus I/O action that does nothing but yield that value. For Maybe it takes a value and wraps it in a Just. -

- -
-Just a reminder: return is nothing like the -return that's in most other languages. It doesn't end -function execution or anything, it just takes a normal value and puts it in a -context. -
- -hmmm yaes - -

-The next function is >>=, or bind. It's like -function application, only instead of taking a normal value and feeding it to a -normal function, it takes a monadic value (that is, a value with a context) and -feeds it to a function that takes a normal value but returns a monadic value. -

- -

-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! -

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

- -

-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. If it's a Just we take -what's inside and apply f to it. -

- -

-We can play around with Maybe as a monad: -

- -
-ghci> return "WHAT" :: Maybe String
-Just "WHAT"
-ghci> Just 9 >>= \x -> return (x*10)
-Just 90
-ghci> Nothing >>= \x -> return (x*10)
-Nothing
-
- -

-Nothing new or exciting on the first line since we already used pure with Maybe and we know that return is just pure with a -different name. The next two lines showcase >>= -a bit more. -

- -

-Notice how when we fed Just 9 to the function \x -> return (x*10), the x -took on the value 9 inside the function. It seems as -though we were able to extract the value from a Maybe -without pattern-matching. And we still didn't lose the context of our Maybe value, because when it's Nothing, -the result of using >>= will be Nothing as well. -

- - -

Walk the line

- -pierre - -

-Now that we know how to feed a Maybe a value to a -function of type -a -> Maybe b while taking into account the context -of possible failure, let's see how we can use >>= repeatedly to handle computations of several -Maybe a values. -

- -

-Pierre has decided to take a break from his job at the fish farm and try -tightrope walking. He's not that bad at it, but he does have one problem: birds -keep landing on his balancing pole! They come and they take a short rest, chat -with their avian friends and then take off in search of breadcrumbs. This -wouldn't bother him so much if the number of birds on the left side of the pole -was always equal to the number of birds on the right side. But sometimes, all -the birds decide that they like one side better and so they throw him off -balance, which results in an embarrassing tumble for Pierre (he's using a safety -net). -

- -

-Let's say that he keeps his balance if the number of birds on the left side of -the pole and on the right side of the pole is within three. So if there's -one bird on the right side and four birds on the left side, he's okay. But if a -fifth bird lands on the left side, then he loses his balance and takes a dive. -

- -

-We're going to simulate birds landing on and flying away from the pole and see -if Pierre is still at it after a certain number of birdy arrivals and -departures. For instance, we want to see what happens to Pierre if first one -bird arrives on the left side, then four birds occupy the right side and then -the bird that was on the left side decides to fly away. -

- -

-We can represent the pole with a simple pair of integers. The first component -will signify the number of birds on the left side and the second component the -number of birds on the right side: -

- -
-type Birds = Int
-type Pole = (Birds,Birds)
-
- -

-First we made a type synonym for Int, called -Birds, because we're using integers to represent -how many birds there are. And then we made a type synonym (Birds,Birds) and we called it Pole (not to be -confused with a person of Polish descent). -

- -

-Next up, how about we make a function that takes a number of birds and lands -them on one side of the pole. Here are the functions: -

- -
-landLeft :: Birds -> Pole -> Pole
-landLeft n (left,right) = (left + n,right)
-
-landRight :: Birds -> Pole -> Pole
-landRight n (left,right) = (left,right + n)
-
- -

-Pretty straightforward stuff. Let's try them out: -

- -
-ghci> landLeft 2 (0,0)
-(2,0)
-ghci> landRight 1 (1,2)
-(1,3)
-ghci> landRight (-1) (1,2)
-(1,1)
-
- -

-To make birds fly away we just had a negative number of birds land on one side. Because -landing a bird on the Pole returns a Pole, we can chain applications of landLeft and landRight: -

- -
-ghci> landLeft 2 (landRight 1 (landLeft 1 (0,0)))
-(3,1)
-
- -

-When we apply the function landLeft 1 to (0,0) we get (1,0). Then, we land a bird on the right side, resulting -in (1,1). Finally two birds land on the left -side, resulting in (3,1). We apply a function to -something by first writing the function and then writing its parameter, but here -it would be better if the pole went first and then the landing function. If we -make a function like this: -

- -
-x -: f = f x
-
- -

-We can apply functions by first writing the parameter and then the function: -

- -
-ghci> 100 -: (*3)
-300
-ghci> True -: not
-False
-ghci> (0,0) -: landLeft 2
-(2,0)
-
- -

-By using this, we can repeatedly land birds on the pole in a more readable -manner: -

- -
-ghci> (0,0) -: landLeft 1 -: landRight 1 -: landLeft 2
-(3,1)
-
- -

-Pretty cool! This example is equivalent to the one before where we repeatedly -landed birds on the pole, only it looks neater. Here, it's more obvious that we -start off with (0,0) and then land one bird one the -left, then one on the right and finally two on the left. -

- -

So far so good, but -what happens if 10 birds land on one side? -

- -
-ghci> landLeft 10 (0,3)
-(10,3)
-
- -

-10 birds on the left side and only 3 on the right? That's sure to send poor -Pierre falling through the air! This is pretty obvious here but what if we had a -sequence of landings like this: -

- -
-ghci> (0,0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
-(0,2)
-
- -

-It might seem like everything is okay but if you follow the steps here, you'll -see that at one time there are 4 birds on the right side and no birds on the -left! To fix this, we have to take another look at our landLeft and landRight -functions. From what we've seen, we want these functions to be able to fail. -That is, we want them to return a new pole if the balance is okay but fail if -the birds land in a lopsided manner. And what better way to add a context of -failure to value than by using Maybe! Let's rework -these functions: -

- -
-landLeft :: Birds -> Pole -> Maybe Pole
-landLeft n (left,right)
-    | abs ((left + n) - right) < 4 = Just (left + n, right)
-    | otherwise                    = Nothing
-
-landRight :: Birds -> Pole -> Maybe Pole
-landRight n (left,right)
-    | abs (left - (right + n)) < 4 = Just (left, right + n)
-    | otherwise                    = Nothing
-
- -

-Instead of returning a Pole these functions now -return a Maybe Pole. They still take the number of -birds and the old pole as before, but then they check if landing that many birds -on the pole would throw Pierre off balance. We use guards to check if the -difference between the number of birds on the new pole is less than 4. If it is, -we wrap the new pole in a Just and return that. If it -isn't, we return a Nothing, indicating failure. -

- -

-Let's give these babies a go: -

- -
-ghci> landLeft 2 (0,0)
-Just (2,0)
-ghci> landLeft 10 (0,3)
-Nothing
-
- -

-Nice! When we land birds without throwing Pierre off balance, we get a new pole -wrapped in a Just. But when many more birds end up on -one side of the pole, we get a Nothing. This is cool, -but we seem to have lost the ability to repeatedly land birds on the pole. We -can't do landLeft 1 (landRight 1 (0,0)) any more -because when we apply landRight 1 to (0,0), we don't get a Pole, but -a Maybe Pole. landLeft 1 -takes a Pole and not a Maybe -Pole. -

- -

-We need a way of taking a Maybe Pole and feeding it -to a function that takes a Pole and returns a Maybe Pole. Luckily, we have >>=, -which does just that for Maybe. Let's give it a go: -

- -
-ghci> landRight 1 (0,0) >>= landLeft 2
-Just (2,1)
-
- -

-Remember, landLeft 2 has a type of Pole -> Maybe Pole. We couldn't just feed it the Maybe Pole that is the result of landRight 1 (0,0), so we use >>= to take that value with a context and give -it to landLeft 2. >>= does indeed allow us to treat the Maybe value as a value with context because if we feed a -Nothing into landLeft 2, -the result is Nothing and the failure is propagated: -

- -
-ghci> Nothing >>= landLeft 2
-Nothing
-
- -

-With this, we can now chain landings that may fail because >>= allows us to feed a -monadic value to a function that takes a normal one. -

- -

-Here's a sequence of birdy landings: -

- -
-ghci> return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
-Just (2,4)
-
- -

-At the beginning, we used return to take a pole and -wrap it in a Just. We could have just applied landRight 2 to (0,0), it would -have been the same, but this way we can be more consistent by using >>= -for every function. -Just (0,0) gets fed to landRight -2, resulting in Just (0,2). This, in turn, -gets fed to landLeft 2, resulting in Just (2,2), and so on. -

- -

-Remember this example from before we introduced failure into Pierre's routine: -

- -
-ghci> (0,0) -: landLeft 1 -: landRight 4 -: landLeft (-1) -: landRight (-2)
-(0,2)
-
- -

-It didn't simulate his interaction with birds very well because in the middle -there his balance was off but the result didn't reflect that. But let's give -that a go now by using monadic application (>>=) -instead of normal application: -

- -
-ghci> return (0,0) >>= landLeft 1 >>= landRight 4 >>= landLeft (-1) >>= landRight (-2)
-Nothing
-
- -iama banana - -

-Awesome. The final result represents failure, which is what we expected. Let's -see how this result was obtained. First, return puts - (0,0) into a default context, making it a Just (0,0). Then, Just (0,0) >>= - landLeft 1 happens. Since the Just (0,0) is a Just value, landLeft 1 gets applied - to (0,0), resulting in a Just - (1,0), because the birds are still relatively balanced. Next, Just (1,0) >>= landRight 4 takes place and the - result is Just (1,4) as the balance of the birds is - still intact, although just barely. Just (1,4) gets - fed to landLeft (-1). This means that landLeft (-1) (1,4) takes place. Now because of how landLeft works, this results in a Nothing, because the resulting pole is off balance. Now - that we have a Nothing, it gets fed to landRight (-2), but because it's a Nothing, the result is automatically Nothing, as we have nothing to apply landRight (-2) to. -

- -

-We couldn't have achieved this by just using Maybe -as an applicative. If you try it, you'll get stuck, because applicative functors -don't allow for the applicative values to interact with each other very much. -They can, at best, be used as parameters to a function by using the applicative -style. The applicative operators will fetch their results and feed them to the -function in a manner appropriate for each applicative and then put the final -applicative value together, but there isn't that much interaction going on -between them. Here, however, each step relies on the previous one's result. On -every landing, the possible result from the previous one is examined and the -pole is checked for balance. This determines whether the landing will succeed or -fail. -

- -

-We may also devise a function that ignores the current number of birds on the -balancing pole and just makes Pierre slip and fall. We can call it banana: -

- -
-banana :: Pole -> Maybe Pole
-banana _ = Nothing
-
- -

-Now we can chain it together with our bird landings. It will always cause our walker -to fall, because it ignores whatever is passed to it and always returns a -failure. Check it: -

- -
-ghci> return (0,0) >>= landLeft 1 >>= banana >>= landRight 1
-Nothing
-
- -

-The value Just (1,0) gets fed to banana, but it produces a Nothing, which -causes everything to result in a Nothing. How -unfortunate! -

- -

-Instead of making functions that ignore their input and just return a -predetermined monadic value, we can use the >> -function, whose default implementation is this: -

- -
-(>>) :: (Monad m) => m a -> m b -> m b
-m >> n = m >>= \_ -> n
-
- -

-Normally, passing some value to a function that ignores its parameter and always -just returns some predetermined value would always result in that predetermined value. With -monads however, their context and meaning has to be considered as well. Here's -how >> acts with Maybe: -

- -
-ghci> Nothing >> Just 3
-Nothing
-ghci> Just 3 >> Just 4
-Just 4
-ghci> Just 3 >> Nothing
-Nothing
-
- -

-If you replace >> with >>= -\_ ->, it's easy to see why it acts like it does. -

- -

-We can replace our banana function in the chain with a >> and then a Nothing: -

- -
-ghci> return (0,0) >>= landLeft 1 >> Nothing >>= landRight 1
-Nothing
-
- -

-There we go, guaranteed and obvious failure! -

- -

-It's also worth taking a look at what this would look like if we hadn't made the -clever choice of treating Maybe values as values with -a failure context and feeding them to functions like we did. Here's how a -series of bird landings would look like: -

- -
-routine :: Maybe Pole
-routine = case landLeft 1 (0,0) of
-    Nothing -> Nothing
-    Just pole1 -> case landRight 4 pole1 of
-        Nothing -> Nothing
-        Just pole2 -> case landLeft 2 pole2 of
-            Nothing -> Nothing
-            Just pole3 -> landLeft 1 pole3
-
- -john joe glanton - -

-We land a bird on the left and then we examine the possibility of failure and -the possibility of success. In the case of failure, we return a Nothing. In the case of success, we land birds on the -right and then do the same thing all over again. Converting this monstrosity -into a neat chain of monadic applications with >>= -is a classic example of how the Maybe monad saves us -a lot of time when we have to successively do computations that are based on -computations that might have failed. -

- -

-Notice how the Maybe implementation of >>= features exactly this logic of seeing if a value -is Nothing and if it is, returning a Nothing right away and if it isn't, going forward with what's -inside the Just. -

- -

-In this section, we took some functions that we had and saw that they would work -better if the values that they returned supported failure. By turning those -values into Maybe values and replacing normal -function application with >>=, we got a -mechanism for handling failure pretty much for free, because >>= is supposed to preserve the context of the value -to which it applies functions. In this case, the context was that our values -were values with failure and so when we applied functions to such values, the -possibility of failure was always taken into account. -

- - -

do notation

- -

-Monads in Haskell are so useful that they got their own special syntax called -do notation. We've already encountered do notation when we were doing I/O and there we said that -it was for gluing together several I/O actions into one. Well, as it turns out, -do notation isn't just for IO, but can be used -for any monad. Its principle is still the same: gluing together monadic -values in sequence. We're going to take a look at how do notation works and why it's useful. -

- -

-Consider this familiar example of monadic application: -

- -
-ghci> Just 3 >>= (\x -> Just (show x ++ "!"))
-Just "3!"
-
- -

-Been there, done that. Feeding a monadic value to a function that returns one, -no big deal. Notice how when we do this, x becomes -3 inside the lambda. Once we're inside that lambda, -it's just a normal value rather than a monadic value. Now, what if we had another ->>= -inside that function? Check this out: -

- -
-ghci> Just 3 >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
-Just "3!"
-
- -

-Ah, a nested use of >>=! In the outermost -lambda, we feed Just "!" to the lambda \y -> Just (show x ++ y). Inside this lambda, the y becomes "!". x is still 3 because we got it -from the outer lambda. All this sort of reminds me of the following expression: -

- -
-ghci> let x = 3; y = "!" in show x ++ y
-"3!"
-
- -

-The main difference between these two is that the values in the former example -are monadic. They're values with a failure context. We can replace any of them -with a failure: -

- -
-ghci> Nothing >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
-Nothing
-ghci> Just 3 >>= (\x -> Nothing >>= (\y -> Just (show x ++ y)))
-Nothing
-ghci> Just 3 >>= (\x -> Just "!" >>= (\y -> Nothing))
-Nothing
-
- -

-In the first line, feeding a Nothing to a function -naturally results in a Nothing. In the second line, -we feed Just 3 to a function and the x becomes 3, but then we feed a -Nothing to the inner lambda and the result of that is -Nothing, which causes the outer lambda to produce -Nothing as well. So this is sort of like assigning -values to variables in let expressions, only that the -values in question are monadic values. -

- -

-To further illustrate this point, let's write this in a script and have each -Maybe value take up its own line: -

- -
-foo :: Maybe String
-foo = Just 3   >>= (\x ->
-      Just "!" >>= (\y ->
-      Just (show x ++ y)))
-
- -

-To save us from writing all these annoying lambdas, Haskell gives us -do notation. It allows us to write the previous piece -of code like this: -

- -
-foo :: Maybe String
-foo = do
-    x <- Just 3
-    y <- Just "!"
-    Just (show x ++ y)
-
- -90s owl - -

-It would seem as though we've gained the ability to temporarily extract things -from Maybe values without having to check if the -Maybe values are Just -values or Nothing values at every step. How cool! If any of the -values that we try to extract from are Nothing, the -whole do expression will result in a Nothing. We're yanking out their (possibly existing) values -and letting >>= worry about the context that -comes with those values. It's important to remember that -do expressions are just different syntax for chaining -monadic values. -

- -

-In a do expression, every line is a monadic value. To -inspect its result, we use <-. If we have a -Maybe String and we bind it with <- to a variable, that variable will be a String, just like when we used >>= -to feed monadic values to lambdas. The last monadic value in a do expression, like Just (show x ++ y) here, -can't be used with <- to bind its result, because that -wouldn't make sense if we translated the do -expression -back to a chain of >>= applications. Rather, -its result is the result of the whole glued up monadic value, taking into -account the possible failure of any previous ones. -

- -

-For instance, examine the following line: -

- -
-ghci> Just 9 >>= (\x -> Just (x > 8))
-Just True
-
- -

-Because the left parameter of >>= is a -Just value, the lambda is applied to 9 and the result is a Just True. If we -rewrite this in do notation, we get: -

- -
-marySue :: Maybe Bool
-marySue = do
-    x <- Just 9
-    Just (x > 8)
-
- -

-If we compare these two, it's easy to see why the result of the whole monadic -value is the result of the last monadic value in the do -expression with all the previous ones chained into it. -

- -

-Our tightrope walker's routine can also be expressed with do -notation. landLeft and landRight -take a number of birds and a pole and produce a pole wrapped in a Just, unless the tightrope walker -slips, in which case a Nothing is produced. We used ->>= to chain successive steps because each one -relied on the previous one and each one had an added context of possible -failure. Here's two birds landing on the left side, then two birds landing on -the right and then one bird landing on the left: -

- -
-routine :: Maybe Pole
-routine = do
-    start <- return (0,0)
-    first <- landLeft 2 start
-    second <- landRight 2 first
-    landLeft 1 second
-
- -

-Let's see if he succeeds: -

- -
-ghci> routine
-Just (3,2)
-
- -

-He does! Great. When we were doing these routines by explicitly writing ->>=, we usually said something like -return (0,0) >>= landLeft 2, because -landLeft 2 is a function that returns a -Maybe value. With do -expressions however, each line must feature a monadic value. So we explicitly pass -the previous Pole to the landLeft -landRight functions. If we examined the variables to -which we bound our Maybe values, start would be (0,0), first would be (2,0) and so on. -

- -

-Because do expressions are written line by line, they may look -like imperative code to some people. But the thing is, they're just sequential, as each value in -each line relies on the result of the previous ones, along with their contexts -(in this case, whether they succeeded or failed). -

- -

Again, let's take a look at what this piece of code would look like if we hadn't -used the monadic aspects of Maybe:

- -
-routine :: Maybe Pole
-routine =
-    case Just (0,0) of
-        Nothing -> Nothing
-        Just start -> case landLeft 2 start of
-            Nothing -> Nothing
-            Just first -> case landRight 2 first of
-                Nothing -> Nothing
-                Just second -> landLeft 1 second
-
- -

-See how in the case of success, the tuple inside Just -(0,0) becomes start, the result of -landLeft 2 start becomes first, etc. -

- -

-If we want to throw the Pierre a banana peel in do -notation, we can do the following: -

- -
-routine :: Maybe Pole
-routine = do
-    start <- return (0,0)
-    first <- landLeft 2 start
-    Nothing
-    second <- landRight 2 first
-    landLeft 1 second
-
- -

-When we write a line in do notation without binding -the monadic value with <-, it's just like putting ->> after the monadic value whose result we -want to ignore. We sequence the monadic value but we ignore its result because -we don't care what it is and it's prettier than writing -_ <- Nothing, which is equivalent to the above. -

- -

-When to use do notation and when to explicitly use ->>= is up to you. I think this example lends -itself to explicitly writing >>= because each -step relies specifically on the result of the previous one. With do notation, we had to specifically write on which pole the -birds are landing, but every time we used that came directly before. But still, -it gave us some insight into do notation. -

- -

-In do notation, when we bind monadic values to -names, we can utilize pattern matching, just like in let -expressions and function parameters. Here's an example of pattern matching in a -do expression: -

- -
-justH :: Maybe Char
-justH = do
-    (x:xs) <- Just "hello"
-    return x
-
- -

-We use pattern matching to get the first character of the string "hello" -and then we present it as the result. So justH evaluates to -Just 'h'. -

- -

-What if this pattern matching were to -fail? 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: -

- -
-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 implemented -like so: -

- -
-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 -Nothing. This is preferable to having our program -crash. Here's a do expression with a pattern that's -bound to fail: -

- -
-wopwop :: Maybe Char
-wopwop = do
-    (x:xs) <- Just ""
-    return x
-
- -

-The pattern matching fails, so the effect is the same as if the whole line with -the pattern was replaced with a Nothing. Let's try -this out: -

- -
-ghci> wopwop
-Nothing
-
- -

-The failed pattern matching has caused a failure within the context of our -monad instead of causing a program-wide failure, which is pretty neat. -

- - -

The list monad

-dead cat -

-So far, we've seen how Maybe values can be viewed as -values with a failure context and how we can incorporate failure handling into -our code by using >>= to feed them to functions. -In this section, we're going to take a look at how to use the monadic aspects of -lists to bring non-determinism into our code in a clear and readable manner. -

- -

-We've already talked about how lists represent non-deterministic values -when they're used as applicatives. A value like 5 -is deterministic. It has only one result and we know exactly what it is. On the -other hand, a value like [3,8,9] contains several -results, so we can view it as one value that is actually many values at the same -time. Using lists as applicative functors showcases this -non-determinism nicely: -

- -
-ghci> (*) <$> [1,2,3] <*> [10,100,1000]
-[10,100,1000,20,200,2000,30,300,3000]
-
- -

-All the possible combinations of multiplying elements from the left list with -elements from the right list are included in the resulting list. When dealing -with non-determinism, there are many choices that we can make, so we just try -all of them, and so the result is a non-deterministic value as well, only it has -many more results. -

- -

-This context of non-determinism translates to monads very nicely. 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 _ = []
-
- -

-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 value. In other words, it makes a -list that has only that one value as its result. This is useful for when we want -to just wrap a normal value into a list so that it can interact with -non-deterministic values. -

- -

-To understand how >>= works for lists, it's -best if we take a look at it in action to gain some intuition first. >>= is about taking a value with a context (a monadic -value) and feeding it to a function that takes a normal value and returns one -that has context. If that function just produced a normal value instead of one -with a context, >>= wouldn't be so useful -because after one use, the context would be lost. Anyway, let's try feeding a -non-deterministic value to a function: -

- -
-ghci> [3,4,5] >>= \x -> [x,-x]
-[3,-3,4,-4,5,-5]
-
- -

-When we used >>= with Maybe, the monadic value was fed into the function while -taking care of possible failures. Here, it takes care of non-determinism for us. -[3,4,5] is a non-deterministic value and we feed it -into a function that returns a non-deterministic value as well. The result is -also non-deterministic, and it features all the possible results of taking -elements from the list [3,4,5] and passing them to -the function \x -> [x,-x]. This function takes a -number and produces two results: one negated and one that's unchanged. So when -we use >>= to feed this list to the function, -every number is negated and also kept unchanged. The x -from the lambda takes on every value from the list that's fed to it. -

- -

-To see how this is achieved, we can just follow the implementation. First, we -start off with the list [3,4,5]. Then, we map the -lambda over it and the result is the following: -

- -
-[[3,-3],[4,-4],[5,-5]]
-
- -

-The lambda is applied to every element and we get a list of lists. Finally, we -just flatten the list and voilà! We've applied a non-deterministic function to a -non-deterministic value! -

- -

-Non-determinism also includes support for failure. The empty list [] is pretty much the equivalent of Nothing, because it signifies the absence of a result. -That's why failing is just defined as the empty list. The error message gets -thrown away. Let's play around with lists that fail: -

- -
-ghci> [] >>= \x -> ["bad","mad","rad"]
-[]
-ghci> [1,2,3] >>= \x -> []
-[]
-
- -

-In the first line, an empty list is fed into the lambda. Because the list has no -elements, none of them can be passed to the function and so the result is an -empty list. This is similar to feeding Nothing to a -function. In the second line, each element gets passed to the function, but the -element is ignored and the function just returns an empty list. Because the -function fails for every element that goes in it, the result is a failure. -

- -

-Just like with Maybe values, we can chain several -lists with >>=, propagating the -non-determinism: -

- -
-ghci> [1,2] >>= \n -> ['a','b'] >>= \ch -> return (n,ch)
-[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
-
- -concatmap - -

-The list [1,2] gets bound to n and ['a','b'] gets bound to -ch. Then, we do return (n,ch) (or [(n,ch)]), -which means taking a pair of (n,ch) and putting it in -a default minimal context. In this case, it's making the smallest possible list -that still presents (n,ch) as the result and features -as little non-determinism as possible. Its effect on the context is minimal. -What we're saying here is this: for every element in [1,2], -go over every element in ['a','b'] and produce a -tuple of one element from each list. -

- -

-Generally speaking, because return takes a value and -wraps it in a minimal context, it doesn't have any extra effect (like failing -in Maybe or resulting in more non-determinism for -lists) but it does present something as its result. -

- -
-When you have non-deterministic values interacting, you can view their -computation as a tree where every possible result in a list represents a -separate branch. -
- -

-Here's the previous expression rewritten in do notation: -

- - -
-listOfTuples :: [(Int,Char)]
-listOfTuples = do
-    n <- [1,2]
-    ch <- ['a','b']
-    return (n,ch)
-
- -

-This makes it a bit more obvious that n takes on -every value from [1,2] and ch takes on every value from ['a','b']. Just like with Maybe, -we're extracting the elements from the monadic values and treating them like -normal values and >>= takes care of the context -for us. The context in this case is non-determinism. -

- -

-Using lists with do notation really reminds me of -something we've seen before. Check out the following piece of code: -

- -
-ghci> [ (n,ch) | n <- [1,2], ch <- ['a','b'] ]
-[(1,'a'),(1,'b'),(2,'a'),(2,'b')]
-
- -

-Yes! List comprehensions! In our do notation example, -n became every result from [1,2] -and for every such result, ch was assigned a result -from ['a','b'] and then the final line put (n,ch) into a default context (a singleton list) to present -it as the result without introducing any additional non-determinism. In this -list comprehension, the same thing happened, only we didn't have to write -return at the end to present (n,ch) as the result because the output part of a list -comprehension did that for us. -

- -

-In fact, list comprehensions are just syntactic sugar for using lists as -monads. In the end, list comprehensions and lists in do -notation translate to using >>= to do -computations that feature non-determinism. -

- -

-List comprehensions allow us to filter our output. For instance, we can filter a -list of numbers to search only for that numbers whose digits contain a 7: -

- -
-ghci> [ x | x <- [1..50], '7' `elem` show x ]
-[7,17,27,37,47]
-
- -

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

-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 -action: -

- -
-ghci> guard (5 > 2) :: Maybe ()
-Just ()
-ghci> guard (1 > 2) :: Maybe ()
-Nothing
-ghci> guard (5 > 2) :: [()]
-[()]
-ghci> guard (1 > 2) :: [()]
-[]
-
- -

-Looks interesting, but how is it useful? In the list monad, we use it to filter -out non-deterministic computations. Observe: -

- -
-ghci> [1..50] >>= (\x -> guard ('7' `elem` show x) >> return x)
-[7,17,27,37,47]
-
- -

-The result here is the same as the result of our previous list comprehension. -How does guard achieve this? Let's first -see how guard functions in conjunction with >>: -

- -
-ghci> guard (5 > 2) >> return "cool" :: [String]
-["cool"]
-ghci> guard (1 > 2) >> return "cool" :: [String]
-[]
-
- -

-If guard succeeds, the result contained within it -is an empty tuple. So then, we use >> to ignore -that empty tuple and present something else as the result. However, if guard fails, then so will the return later on, because feeding an empty list to a -function with >>= always results in an empty -list. A guard basically says: if this boolean is -False then produce a failure right here, otherwise -make a successful value that has a dummy result of () -inside it. All this does is to allow the computation to continue. -

- -

-Here's the previous example rewritten in do notation: -

- -
-sevensOnly :: [Int]
-sevensOnly = do
-    x <- [1..50]
-    guard ('7' `elem` show x)
-    return x
-
- -

-Had we forgotten to present x as the final result by -using return, the resulting list would just be a list -of empty tuples. Here's this again in the form of a list comprehension: -

- -
-ghci> [ x | x <- [1..50], '7' `elem` show x ]
-[7,17,27,37,47]
-
- -

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

- -

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. We want to find out -if the knight can reach a certain position in three moves. We'll just use a pair -of numbers to represent the knight's position on the chess board. The first -number will determine the column he's in and the second number will determine -the row. -

- -hee haw im a horse - -

-Let's make a type synonym for the knight's current position on the chess board: -

- -
-type KnightPos = (Int,Int)
-
- -

-So let's say that the knight starts at (6,2). Can he -get to (6,1) in exactly three moves? Let's see. If we -start off at (6,2) what's the best move to make next? -I know, how about all of them! We have non-determinism at our disposal, so -instead of picking one move, let's just pick all of them at once. Here's a -function that takes the knight's position and returns all of its next moves: -

- -
-moveKnight :: KnightPos -> [KnightPos]
-moveKnight (c,r) = do
-    (c',r') <- [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
-               ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
-               ]
-    guard (c' `elem` [1..8] && r' `elem` [1..8])
-    return (c',r')
-
- -

-The knight can always take one step horizontally or vertically and two steps -horizontally or vertically but its movement has to be both horizontal and -vertical. (c',r') takes on every value from the list -of movements and then guard makes sure that the new -move, (c',r') is still on the board. If it's not, -it produces an empty list, which causes a failure and return (c',r') isn't carried out for that position. -

- -

-This function can also be written without the use of lists as a monad, but we -did it here just for kicks. Here is the same function done with filter: -

- -
-moveKnight :: KnightPos -> [KnightPos]
-moveKnight (c,r) = filter onBoard
-    [(c+2,r-1),(c+2,r+1),(c-2,r-1),(c-2,r+1)
-    ,(c+1,r-2),(c+1,r+2),(c-1,r-2),(c-1,r+2)
-    ]
-    where onBoard (c,r) = c `elem` [1..8] && r `elem` [1..8]
-
- -

-Both of these do the same thing, so pick one that you think looks nicer. Let's -give it a whirl: -

- -
-ghci> moveKnight (6,2)
-[(8,1),(8,3),(4,1),(4,3),(7,4),(5,4)]
-ghci> moveKnight (8,1)
-[(6,2),(7,3)]
-
- -

-Works like a charm! We take one position and we just carry out all the possible -moves at once, so to speak. So now that we have a non-deterministic next position, -we just use >>= to feed it to moveKnight. Here's a function that takes a position and -returns all the positions that you can reach from it in three moves: -

- -
-in3 :: KnightPos -> [KnightPos]
-in3 start = do
-    first <- moveKnight start
-    second <- moveKnight first
-    moveKnight second
-
- -

-If you pass it (6,2), the resulting list is quite -big, because if there are several ways to reach some position in three moves, -it crops up in the list several times. The above without do notation: -

- - -
-in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight
-
- -

-Using >>= once gives us all possible moves from -the start and then when we use >>= the second -time, for every possible first move, every possible next move is computed, and -the same goes for the last move. -

- -

-Putting a value in a default context by applying return -to it and then feeding it to a function with >>= -is the same as just normally applying the function to that value, but we did it -here anyway for style. -

- -

-Now, let's make a function that takes two positions and tells us if you can -get from one to the other in exactly three steps: -

- -
-canReachIn3 :: KnightPos -> KnightPos -> Bool
-canReachIn3 start end = end `elem` in3 start
-
- -

-We generate all the possible positions in three steps and then we see if the -position we're looking for is among them. So let's see if we can get from -(6,2) to (6,1) in three -moves: -

- -
-ghci> (6,2) `canReachIn3` (6,1)
-True
-
- -

-Yes! How about from (6,2) to (7,3)? -

- -
-ghci> (6,2) `canReachIn3` (7,3)
-False
-
- -

-No! As an exercise, you can change this function so that when you can reach one -position from the other, it tells you which moves to take. Later on, we'll see -how to modify this function so that we also pass it the number of moves to take -instead of that number being hardcoded like it is now. -

- - -

Monad laws

- -the court finds you guilty of peeing all over
-everything - -

-Just like applicative functors, and functors before them, monads come with a few -laws that all monad instances must abide by. Just because something is made an -instance of the Monad type class doesn't mean that -it's a monad, it just means that it was made an instance of a type class. For -a type to truly be a monad, the monad laws must hold for that type. These laws -allow us to make reasonable assumptions about the type and its behavior. -

- -

-Haskell allows any type to be an instance of any type class as long as the types check -out. It can't check if the monad laws hold for a type though, so if we're making -a new instance of the Monad type class, we have to be -reasonably sure that all is well with the monad laws for that type. 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 -if the laws hold. But don't worry, they're not complicated. -

- -

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: -

- - - -

-If you look at monadic values as values with a context and return as taking a value and putting it in a default -minimal context that still presents that value as its result, it makes sense, -because if that context is really minimal, feeding this monadic value to a -function shouldn't be much different than just applying the function to the -normal value, and indeed it isn't different at all. -

- -

-For the Maybe monad return -is defined as Just. The Maybe -monad is all about possible failure, and if we have a value and want to put it -in such a context, it makes sense that we treat it as a successful computation -because, well, we know what the value is. Here's some return usage with Maybe: -

- -
-ghci> return 3 >>= (\x -> Just (x+100000))
-Just 100003
-ghci> (\x -> Just (x+100000)) 3
-Just 100003
-
- -

-For the list monad return puts something in a -singleton list. The >>= implementation for -lists goes over all the values in the list and applies the function to them, but -since there's only one value in a singleton list, it's the same as applying the -function to that value: -

- -
-ghci> return "WoM" >>= (\x -> [x,x,x])
-["WoM","WoM","WoM"]
-ghci> (\x -> [x,x,x]) "WoM"
-["WoM","WoM","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

- -

-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: -

- - - -

-This one might be a bit less obvious than the first one, but let's take a look -at why it should hold. When we feed monadic values to functions by using >>=, those functions take normal values and return -monadic ones. return is also one such function, if -you consider its type. Like we said, return puts a -value in a minimal context that still presents that value as its result. This -means that, for instance, for Maybe, it doesn't -introduce any failure and for lists, it doesn't introduce any extra -non-determinism. Here's a test run for a few monads: -

- -
-ghci> Just "move on up" >>= (\x -> return x)
-Just "move on up"
-ghci> [1,2,3,4] >>= (\x -> return x)
-[1,2,3,4]
-ghci> putStrLn "Wah!" >>= (\x -> return x)
-Wah!
-
- -

-If we take a closer look at the list example, the implementation for ->>= is: -

- -
-xs >>= f = concat (map f xs)
-
- -

-So when we feed [1,2,3,4] to -return, first return -gets mapped over [1,2,3,4], resulting in -[[1],[2],[3],[4]] and then this gets concatenated and -we have our original list. -

- -

-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

- -

-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: -

- - - -

-Hmmm, now what's going on here? We have one monadic value, m and two monadic functions f -and g. When we're doing (m ->>= f) >>= g, we're feeding m to f, which results in a monadic value. Then, we feed that monadic value to g. -In the expression m >>= (\x -> f x >>= g), we take a -monadic value and we feed it to a function that feeds the result of f x to g. It's not easy to see -how those two are equal, so let's take a look at an example that makes this -equality a bit clearer. -

- -

-Remember when we had our tightrope walker Pierre walk a rope while birds landed -on his balancing pole? To simulate birds landing on his balancing pole, we -made a chain of several functions that might produce failure: -

- -
-ghci> return (0,0) >>= landRight 2 >>= landLeft 2 >>= landRight 2
-Just (2,4)
-
- -

-We started with Just (0,0) and then bound that value -to the next monadic function, landRight 2. The result -of that was another monadic value which got bound into the next -monadic function, and so on. If we were to explicitly parenthesize this, we'd -write: -

- -
-ghci> ((return (0,0) >>= landRight 2) >>= landLeft 2) >>= landRight 2
-Just (2,4)
-
- -

-But we can also write the routine like this: -

- -
-return (0,0) >>= (\x ->
-landRight 2 x >>= (\y ->
-landLeft 2 y >>= (\z ->
-landRight 2 z)))
-
- -

-return (0,0) is the same as -Just (0,0) and when we feed it to the lambda, -the x becomes (0,0). -landRight takes a number of birds and a pole (a tuple -of numbers) and that's what it gets passed. This results in a Just (0,2) and when we feed this to the next lambda, -y is (0,2). This goes on -until the final bird landing produces a Just (2,4), -which is indeed the result of the whole expression. -

- -

-So it doesn't matter how you nest feeding values to monadic functions, what -matters is their meaning. Here's another way to look at this law: consider -composing two functions, f and g. Composing two functions is implemented like so: -

- -
-(.) :: (b -> c) -> (a -> b) -> (a -> c)
-f . g = (\x -> f (g x))
-
- -

-If the type of g is a -> b -and the type of f is b -> c, -we arrange them into a new function which has a type of a -> c, so that its parameter is passed between those -functions. Now what if those two functions were monadic, that is, what if the -values they returned were monadic values? If we had a function of type -a -> m b, we couldn't just pass its result to a -function of type b -> m c, because that function -accepts a normal b, not a monadic one. We could -however, use >>= to make that happen. So by -using >>=, we can compose two monadic -functions: -

- -
-(<=<) :: (Monad m) => (b -> m c) -> (a -> m b) -> (a -> m c)
-f <=< g = (\x -> g x >>= f)
-
- -

-So now we can compose two monadic functions: -

- -
-ghci> let f x = [x,-x]
-ghci> let g x = [x*3,x*2]
-ghci> let h = f <=< g
-ghci> h 3
-[9,-9,6,-6]
-
- -

-Cool. So what does that have to do with the associativity law? Well, when we -look at the law as a law of compositions, it states that -f <=< (g <=< h) should be the same as -(f <=< g) <=< h. This is just another -way of saying that for monads, the nesting of operations shouldn't matter. -

- -

-If we translate the first two laws to use <=<, -then the left identity law states that for every monadic function f, -f <=< return -is the same as writing just f and the right identity law -says that return <=< f is also no different from -f. -

- -

-This is very similar to how if f -is a normal function, (f . g) . h is the same as f . (g . h), f . id -is always the same as f and id . -f -is also just f. -

- -

-In this chapter, we took a look at the basics of monads and learned how the -Maybe monad and the list monad work. In the next -chapter, we'll take a look at a whole bunch of other cool monads and we'll also -learn how to make our own. -

- -
- - - - -
- - - - diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index 95fd357..548d559 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -20,6 +20,7 @@ h1 { h1 { font-size:40px; line-height:40px; + font-weight:normal; } } h2 { @@ -44,6 +45,9 @@ a:hover { text-decoration:none; } em { + font-style:italic; +} +strong { font-style:normal; font-weight:bold; } @@ -70,7 +74,7 @@ img.center { display:block; max-width:100%; } -pre.code { +pre > code { color:white; /*background-color:#522812;*/ background-color:black; @@ -92,6 +96,9 @@ pre.code { margin-bottom:25px; background-image:url(https://s3.amazonaws.com/lyah/note.jpg); } +.hintbox > p { + margin-bottom:0px; +} .label { white-space:nowrap; font-family:Consolas, "Courier New", monospace; @@ -114,7 +121,8 @@ pre.code { background-color:#99DDcc; color:black; } -.fixed { +:not(pre) > code:not(.label, .function, .type, .class, .law) { + white-space:nowrap; font-family:Consolas, "Courier New", monospace; background-color:#ddd; font-weight:normal; @@ -149,6 +157,7 @@ pre.code { padding-left:25px; margin:0px; color:#408156; + margin-top:25px; margin-bottom:25px; } .chapters > li { @@ -215,7 +224,6 @@ pre.code { width:unset; } } - .prevlink { padding-left:20px; background-image:url(https://s3.amazonaws.com/lyah/prv.png); diff --git a/docs/chapters.html b/docs/chapters.html deleted file mode 100644 index 5ae1f3b..0000000 --- a/docs/chapters.html +++ /dev/null @@ -1,189 +0,0 @@ - - - -Chapters - Learn You a Haskell for Great Good! - - - - - - - - - -
-
-

Learn You a Haskell for Great Good!

-

-

    -
  1. - Introduction - -
  2. -
  3. - Starting Out - -
  4. -
  5. - Types and Typeclasses - -
  6. -
  7. - Syntax in Functions - -
  8. -
  9. - Recursion - -
  10. -
  11. - Higher Order Functions - -
  12. -
  13. - Modules - -
  14. -
  15. - Making Our Own Types and Typeclasses - -
  16. -
  17. - Input and Output - -
  18. -
  19. - Functionally Solving Problems - -
  20. -
  21. - Functors, Applicative Functors and Monoids - -
  22. -
  23. - A Fistful of Monads - -
  24. -
  25. - For a Few Monads More - -
  26. -
  27. - Zippers - -
  28. -
-

- This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License because I couldn't find a license with an even longer name. -

-
- - - - -
- - - - diff --git a/docs/faq.html b/docs/faq.html deleted file mode 100644 index e388dc6..0000000 --- a/docs/faq.html +++ /dev/null @@ -1,58 +0,0 @@ - - - -FAQ - Learn You a Haskell for Great Good! - - - - - - - - - -
-
-

FAQ

-turtle??? -

Can I put this tutorial on my site or change it or whatever?

-

Sure, it's licensed under a creative commons license, so you can share and change this, as long as you do it with a smile on your face and for non-commercial purposes.

-

Do you recommend any other Haskell reading material?

-

There are loads of awesome tutorials out there, but I'd just like to point out how great Real World Haskell is. It's really great. I feel it complements this tutorial nicely. This tutorial focuses mainly on using simple examples to ease beginners into learning Haskell, whereas Real World Haskell really shows you how to do useful stuff with it.

-

Another great Haskell resource is Try Haskell, which allows you to try Haskell right in your browser and offers a rad interactive walkthrough.

-

How do I get in touch with you?

-

The best way would be to shoot me an email to bonus at learnyouahaskell dot com. I kinda suck at email though, so please, please don't be mad if I don't reply in a timely fashion!

-

Your book is cool but I want some exercises too!

-

Coming soon! A lot of people have been asking me to add exercises, so I'll be putting some up soonish.

-

Tell me about yourself!

-

My name is Miran Lipovača, I reside in Ljubljana, Slovenia. Most of my time is spent on doing nothing in particular, but when I'm not doing nothing I'm either programming, drawing, boxing or playing bass. I even have a cool bass tabs site. I also have a collection of stuffed owls and sometimes I talk to them and they talk back.

-
- -
-
- - - - -
- - - - diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html deleted file mode 100644 index 3b69547..0000000 --- a/docs/for-a-few-monads-more.html +++ /dev/null @@ -1,3121 +0,0 @@ - - - -For a Few Monads More - Learn You a Haskell for Great Good! - - - - - - - - - - - -
-
- -

For a Few Monads More

- -there are two kinds of people in the world, my friend. those who learn them a haskell and those who have the job of coding java - -

-We've seen how monads can be used to take values with contexts and apply them to -functions and how using >>= or do notation allows us to focus on the values themselves while the -context gets handled for us. -

- -

-We've met the Maybe monad and seen how it adds a -context of possible failure to values. We've learned about the list monad and -saw how it lets us easily introduce non-determinism into our programs. We've also -learned how to work in the IO monad, even before we -knew what a monad was! - -

-In this chapter, we're going to learn about a few other monads. We'll see how -they can make our programs clearer by letting us treat all sorts of values as -monadic ones. Exploring a few monads more will also solidify our intuition for -monads. -

- -

-The monads that we'll be exploring are all part of the mtl -package. A Haskell package is a collection of modules. The mtl package comes with the Haskell Platform, so you -probably already have it. To check if you do, type ghc-pkg list in the command-line. This will show which -Haskell packages you have installed and one of them should be mtl, followed by a version number. -

- - -

Writer? I hardly know her!

- -

-We've loaded our gun with the Maybe monad, the list -monad and the IO monad. Now let's put the Writer monad in the chamber and see what happens when we -fire it! -

- -

-Whereas Maybe is for values with an added context of -failure and the list is for non-deterministic values, the Writer monad is for values that have another value attached -that acts as a sort of log value. Writer allows us to -do computations while making sure that all the log values are combined into one -log value that then gets attached to the result. -

- -

-For instance, we might want to equip our values with strings that explain what's -going on, probably for debugging purposes. Consider a function that takes a -number of bandits in a gang and tells us if that's a big gang or not. That's a -very simple function: -

- -
-isBigGang :: Int -> Bool
-isBigGang x = x > 9
-
- -

-Now, what if instead of just giving us a True or -False value, we want it to also return a log string -that says what it did? Well, we just make that string and return it alongside -our Bool: -

- -
-isBigGang :: Int -> (Bool, String)
-isBigGang x = (x > 9, "Compared gang size to 9.")
-
- -

-So now instead of just returning a Bool, we return a -tuple where the first component of the tuple is the actual value and the second -component is the string that accompanies that value. There's some added context -to our value now. Let's give this a go: -

- -
-ghci> isBigGang 3
-(False,"Compared gang size to 9.")
-ghci> isBigGang 30
-(True,"Compared gang size to 9.")
-
- -when you have to poop, poop, don't talk - -

-So far so good. isBigGang takes a normal value and -returns a value with a context. As we've just seen, feeding it a normal value is -not a problem. Now what if we already have a value that has a log string -attached to it, such as (3, "Smallish gang."), -and we want to feed it to isBigGang? It seems like -once again, we're faced with this question: if we have a function that takes a -normal value and returns a value with a context, how do we take a value with a -context and feed it to the function? -

- -

-When we were exploring the Maybe monad, we made a -function applyMaybe, which took a Maybe a value and a function of type a -> Maybe b -and fed that Maybe a value into the function, even -though the function takes a normal a instead of a Maybe -a. It did this by minding the context that comes with Maybe a values, which is that they are values with possible -failure. But inside the a -> Maybe b function, we -were able to treat that value as just a normal value, because applyMaybe (which later became >>=) -took care of checking if it was a Nothing or a Just value. -

- -

-In the same vein, let's make a function that takes a value with an attached log, -that is, an (a,String) value and a function of type -a -> (b,String) and feeds that value into the -function. We'll call it applyLog. But because an -(a,String) value doesn't carry with it a context of -possible failure, but rather a context of an additional log value, applyLog is going to make sure that the log of the original -value isn't lost, but is joined together with the log of the value that results -from the function. Here's the implementation of applyLog: -

- -
-applyLog :: (a,String) -> (a -> (b,String)) -> (b,String)
-applyLog (x,log) f = let (y,newLog) = f x in (y,log ++ newLog)
-
- -

-When we have a value with a context and we want to feed it to a function, we -usually try to separate the actual value from the context and then try to apply -the function to the value and then see that the context is taken care of. In the -Maybe monad, we checked if the value was a Just x and if it was, we took that x and applied the function to it. In this case, -it's very easy to find the actual value, because we're dealing with a pair where -one component is the value and the other a log. So first we just take the value, -which is x and we apply the function f to it. We get a pair of (y,newLog), where -y is the new result and newLog -the new log. But if we returned that as the result, the old log value wouldn't -be included in the result, so we return a pair of (y,log ++ -newLog). We use ++ to append the new log to -the old one. -

- -

-Here's applyLog in action: -

- -
-ghci> (3, "Smallish gang.") `applyLog` isBigGang
-(False,"Smallish gang.Compared gang size to 9")
-ghci> (30, "A freaking platoon.") `applyLog` isBigGang
-(True,"A freaking platoon.Compared gang size to 9")
-
- -

-The results are similar to before, only now the number of people in the gang had -its accompanying log and it got included in the result log. Here are a few more -examples of using applyLog: -

- -
-ghci> ("Tobin","Got outlaw name.") `applyLog` (\x -> (length x, "Applied length."))
-(5,"Got outlaw name.Applied length.")
-ghci> ("Bathcat","Got outlaw name.") `applyLog` (\x -> (length x, "Applied length"))
-(7,"Got outlaw name.Applied length")
-
- -

-See how inside the lambda, x is just a normal string -and not a tuple and how applyLog takes care of -appending the logs. -

- -

Monoids to the rescue

- -
-Be sure you know what monoids are at this point! Cheers. -
- -

-Right now, applyLog takes values of type (a,String), but is there a reason that the log has to be a -String? It uses ++ to append -the logs, so wouldn't this work on any kind of list, not just a list of -characters? Sure it would. We can go ahead and change its type to this: -

- -
-applyLog :: (a,[c]) -> (a -> (b,[c])) -> (b,[c])
-
- -

-Now, the log is a list. The type of values contained in the list has to be the -same for the original list as well as for the list that the function returns, -otherwise we wouldn't be able to use ++ to stick them -together. -

- -

-Would this work for bytestrings? There's no reason it shouldn't. However, the -type we have now only works for lists. It seems like we'd have to make a -separate applyLog for bytestrings. But wait! Both -lists and bytestrings are monoids. As such, they are both instances of the -Monoid type class, which means that they implement -the mappend function. And for both lists and -bytestrings, mappend is for appending. Watch: -

- -
-ghci> [1,2,3] `mappend` [4,5,6]
-[1,2,3,4,5,6]
-ghci> B.pack [99,104,105] `mappend` B.pack [104,117,97,104,117,97]
-Chunk "chi" (Chunk "huahua" Empty)
-
- -

-Cool! Now our applyLog can work for any monoid. We -have to change the type to reflect this, as well as the implementation, because -we have to change ++ to mappend: -

- -
-applyLog :: (Monoid m) => (a,m) -> (a -> (b,m)) -> (b,m)
-applyLog (x,log) f = let (y,newLog) = f x in (y,log `mappend` newLog)
-
- -

-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. Here's a function that adds drink to some cowboy food: -

- -
-import Data.Monoid
-
-type Food = String
-type Price = Sum Int
-
-addDrink :: Food -> (Food,Price)
-addDrink "beans" = ("milk", Sum 25)
-addDrink "jerky" = ("whiskey", Sum 99)
-addDrink _ = ("beer", Sum 30)
-
- -

-We use strings to represent foods and an Int -in a Sum newtype wrapper to keep -track of how many cents something costs. Just a reminder, doing mappend with Sum results in the -wrapped values getting added together: -

- -
-ghci> Sum 3 `mappend` Sum 9
-Sum {getSum = 12}
-
- -

-The addDrink function is pretty simple. If we're -eating beans, it returns "milk" along with Sum 25, so 25 cents wrapped in Sum. If we're eating jerky we drink whiskey and if we're -eating anything else we drink beer. Just normally applying this function to a -food wouldn't be terribly interesting right now, but using applyLog to feed a food that comes with a price itself into -this function is interesting: -

- -
-ghci> ("beans", Sum 10) `applyLog` addDrink
-("milk",Sum {getSum = 35})
-ghci> ("jerky", Sum 25) `applyLog` addDrink
-("whiskey",Sum {getSum = 124})
-ghci> ("dogmeat", Sum 5) `applyLog` addDrink
-("beer",Sum {getSum = 35})
-
- -

-Milk costs 25 cents, but if we eat -it with beans that cost 10 cents, we'll end up paying -35 cents. Now it's clear how the attached value -doesn't always have to be a log, it can be any monoid value and how two such -values are combined into one depends on the monoid. When we were doing logs, -they got appended, but now, the numbers are being added up. -

- -

-Because the value that addDrink returns is a tuple of -type (Food,Price), we can feed that result to addDrink again, so that it tells us what we should drink -along with our drink and how much that will cost us. Let's give it a shot: -

- -
-ghci> ("dogmeat", Sum 5) `applyLog` addDrink `applyLog` addDrink
-("beer",Sum {getSum = 65})
-
- -

-Adding a drink to some dog meat results in a beer and an additional -30 cents, so ("beer", Sum 35). -And if we use applyLog to feed that to addDrink, we get another beer and the result is -("beer", Sum 65). -

- -

The Writer type

- -

-Now that we've seen that a value with an attached monoid acts like a monadic -value, let's examine the Monad instance for types of -such values. The Control.Monad.Writer module exports -the Writer w a type along with its Monad instance and some useful functions for dealing with -values of this type. -

- -

-First, let's examine the type itself. To attach a monoid to a value, we just -need to put them together in a tuple. The Writer w a -type is just a newtype wrapper for this. Its -definition is very simple: -

- -
-newtype Writer w a = Writer { runWriter :: (a, w) }
-
- -

-It's wrapped in a newtype so that it can be made an -instance of Monad and that its type is separate from -a normal tuple. The a type parameter represents the type -of the value and the w type parameter the type of the -attached monoid value. -

- -

-Its Monad instance is defined like so: -

- -
-instance (Monoid w) => Monad (Writer w) where
-    return x = Writer (x, mempty)
-    (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
-
- - -when you have to poop, poop, don't talk -

-First off, let's examine >>=. Its -implementation is essentially the same as applyLog, -only now that our tuple is wrapped in the Writer -newtype, we have to unwrap it when pattern matching. -We take the value x and apply the function f to it. This gives us a Writer w a value and we use a let expression to pattern match on it. We present -y as the new result and use mappend to combine the old monoid value with the new one. -We pack that up with the result value in a tuple and then wrap that with the -Writer constructor so that our result is a Writer value instead of just an unwrapped tuple. -

- -

-So, what about return? It has to take a value and put -it in a default minimal context that still presents that value as the result. So -what would such a context be for Writer values? If we -want the accompanying monoid value to affect other monoid values as little as -possible, it makes sense to use mempty. mempty is used to present identity monoid values, such as -"" and Sum 0 and empty -bytestrings. Whenever we use mappend between mempty and some other monoid value, the result is that -other monoid value. So if we use return to make a -Writer value and then use >>= -to feed that value to a function, the resulting monoid value will be only what -the function returns. Let's use return on the number -3 a bunch of times, only we'll pair it with a -different monoid every time: -

- -
-ghci> runWriter (return 3 :: Writer String Int)
-(3,"")
-ghci> runWriter (return 3 :: Writer (Sum Int) Int)
-(3,Sum {getSum = 0})
-ghci> runWriter (return 3 :: Writer (Product Int) Int)
-(3,Product {getProduct = 1})
-
- -

-Because Writer doesn't have a Show instance, we had to use runWriter to convert our Writer -values to normal tuples that can be shown. For String, the monoid value is the empty string. With Sum, it's 0, because if we add 0 -to something, that something stays the same. For Product, -the identity is 1. -

- -

-The Writer instance doesn't feature an implementation -for fail, so if a pattern match fails in do notation, error is called. -

- -

Using do notation with Writer

- -

-Now that we have a Monad instance, we're free to use -do notation for Writer -values. It's handy for when we have a several Writer -values and we want to do stuff with them. Like with other monads, we can treat -them as normal values and the context gets taken for us. In this case, -all the monoid values that come attached get mappended -and so are reflected in the final result. Here's a simple example of using -do notation with Writer to -multiply two numbers: -

- -
-import Control.Monad.Writer
-
-logNumber :: Int -> Writer [String] Int
-logNumber x = Writer (x, ["Got number: " ++ show x])
-
-multWithLog :: Writer [String] Int
-multWithLog = do
-    a <- logNumber 3
-    b <- logNumber 5
-    return (a*b)
-
- -

-logNumber takes a number and makes a Writer value out of it. For the monoid, we use a list of -strings and we equip the number with a singleton list that just says that we -have that number. multWithLog is a Writer value which multiplies 3 -and 5 and makes sure that their attached logs get -included in the final log. We use return to present -a*b as the result. Because return just takes something and puts it in a minimal -context, we can be sure that it won't add anything to the log. Here's what we -see if we run this: -

- -
-ghci> runWriter multWithLog
-(15,["Got number: 3","Got number: 5"])
-
- -

-Sometimes we just want some monoid value to be included at some particular -point. For this, the -tell function is useful. It's part of the -MonadWriter type class and in the case of -Writer it takes a monoid value, like ["This is going on"] and creates a Writer value that presents the dummy value () as its result but has our desired monoid value attached. -When we have a monadic value that has () as its -result, we don't bind it to a variable. Here's multWithLog -but with some extra reporting included: -

- -
-multWithLog :: Writer [String] Int
-multWithLog = do
-    a <- logNumber 3
-    b <- logNumber 5
-    tell ["Gonna multiply these two"]
-    return (a*b)
-
- -

-It's important that return (a*b) is the last line, -because the result of the last line in a do expression -is the result of the whole do expression. Had we -put tell as the last line, () would have been the result of this do expression. We'd lose the result of the multiplication. -However, the log would be the same. Here is this in action: -

- -
-ghci> runWriter multWithLog
-(15,["Got number: 3","Got number: 5","Gonna multiply these two"])
-
- -

Adding logging to programs

- -

-Euclid's algorithm is an algorithm that takes two numbers and computes their -greatest common divisor. That is, the biggest number that still divides both of -them. Haskell already features the gcd -function, which does exactly this, but let's implement our own and then equip it -with logging capabilities. Here's the normal algorithm: -

- - - -
-gcd' :: Int -> Int -> Int
-gcd' a b
-    | b == 0    = a
-    | otherwise = gcd' b (a `mod` b)
-
- -

-The algorithm is very simple. First, it checks if the second number is 0. If it -is, then the result is the first number. If it isn't, then the result is the -greatest common divisor of the second number and the remainder of dividing the -first number with the second one. For instance, if we want to know what the -greatest common divisor of 8 and 3 is, we just follow the algorithm outlined. -Because 3 isn't 0, we have to find the greatest common divisor of 3 and 2 -(if we divide 8 by 3, the remainder is 2). Next, we find the greatest -common divisor of 3 and 2. 2 still isn't 0, so now we have 2 and 1. The -second number isn't 0, so we run the algorithm again for 1 and 0, as -dividing 2 by 1 gives us a remainder of 0. And finally, because the second number -is now 0, the final result is 1. Let's see if our code agrees: -

- -
-ghci> gcd' 8 3
-1
-
- -

-It does. Very good! Now, we want to equip our result with a context, and the -context will be a monoid value that acts as a log. Like before, we'll use a list -of strings as our monoid. So the type of our new gcd' -function should be: -

- -
-gcd' :: Int -> Int -> Writer [String] Int
-
- -

-All that's left now is to equip our function with log values. Here's the code: -

- -
-import Control.Monad.Writer
-
-gcd' :: Int -> Int -> Writer [String] Int
-gcd' a b
-    | b == 0 = do
-        tell ["Finished with " ++ show a]
-        return a
-    | otherwise = do
-        tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)]
-        gcd' b (a `mod` b)
-
- -

-This function takes two normal Int values and returns -a Writer [String] Int, that is, an Int that has a log context. In the case where b is 0, instead of just giving -a as the result, we use a do expression -to put together a Writer value as a result. First we -use tell to report that we're finished and then we -use return to present a as -the result of the do expression. Instead of this -do expression, we could have also written this: -

- -
-Writer (a, ["Finished with " ++ show a])
-
- -

-However, I think the do expression is easier to read. Next, -we have the case when b isn't 0. In this case, -we log that we're using mod to figure out the -remainder of dividing a and b. Then, the second line of the do expression -just recursively calls gcd'. Remember, gcd' now ultimately returns a Writer value, -so it's perfectly valid that gcd' b (a `mod` b) is a -line in a do expression. -

- -

-While it may be kind of useful to trace the execution of this new gcd' by hand to see how the logs get appended, I think it's -more insightful to just look at the big picture and view these as values with a -context and from that gain insight as to what the final result will be. -

- -

-Let's try our new gcd' out. Its result is a -Writer [String] Int value and if we unwrap that from -its newtype, we get a tuple. The first part of the -tuple is the result. Let's see if it's okay: -

- -
-ghci> fst $ runWriter (gcd' 8 3)
-1
-
- -

-Good! Now what about the log? Because the log is a list of strings, let's use -mapM_ putStrLn to print those strings to the screen: -

- -
-ghci> mapM_ putStrLn $ snd $ runWriter (gcd' 8 3)
-8 mod 3 = 2
-3 mod 2 = 1
-2 mod 1 = 0
-Finished with 1
-
- -

-I think it's awesome how we were able to change our ordinary algorithm to one -that reports what it does as it goes along just by changing normal values to -monadic values and letting the implementation of >>= -for Writer take care of the logs for us. We can add a -logging mechanism to pretty much any function. We just replace normal values -with Writer values where we want and change normal function -application to >>= (or do expressions if it increases readability). -

- -

Inefficient list construction

- -

-When using the Writer monad, you have to be careful -which monoid to use, because using lists can sometimes turn out to be very -slow. That's because lists use ++ for mappend -and using ++ to add something to the end of a list is -slow if that list is really long. -

- -

-In our gcd' function, the logging is fast because the list -appending ends up looking like this: -

- -
-a ++ (b ++ (c ++ (d ++ (e ++ f))))
-
- -

-Lists are a data structure that's constructed from left to right, and this is -efficient because we first fully construct the left part of a list and only -then add a longer list on the right. But if we're not careful, using the -Writer monad can produce list appending that looks like this: -

- -
-((((a ++ b) ++ c) ++ d) ++ e) ++ f
-
- -

-This associates to the left instead of to the right. This is inefficient -because every time it wants to add the right part to the left part, it has -to construct the left part all the way from the beginning! -

- -

-The following function works like gcd', only it logs stuff in -reverse. First it produces the log for the rest of the procedure and then adds -the current step to the end of the log. -

- -
-import Control.Monad.Writer
-
-gcdReverse :: Int -> Int -> Writer [String] Int
-gcdReverse a b
-    | b == 0 = do
-        tell ["Finished with " ++ show a]
-        return a
-    | otherwise = do
-        result <- gcdReverse b (a `mod` b)
-        tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)]
-        return result
-
- -

-It does the recursion first, and binds its result value to result. -Then it adds the current step to the log, but the current step goes at the end -of the log that was produced by the recursion. Finally, it presents the -result of the recursion as the final result. Here it is in action: -

- -
-ghci> mapM_ putStrLn $ snd $ runWriter (gcdReverse 8 3)
-Finished with 1
-2 mod 1 = 0
-3 mod 2 = 1
-8 mod 3 = 2
-
- -

-It's inefficient because it ends up associating the use of ++ to -the left instead of to the right. -

- -

Difference lists

- -cactuses - -

-Because lists can sometimes be inefficient when repeatedly appended in this -manner, it's best to use a data structure that always supports efficient -appending. One such data structure is the difference list. A difference list -is similar to a list, only instead of being a normal list, it's a function that -takes a list and prepends another list to it. The difference list equivalent of -a list like [1,2,3] would be the function \xs -> [1,2,3] ++ xs. -A normal empty list is [], whereas an empty difference list -is the function \xs -> [] ++ xs. -

- -

-The cool thing about difference lists is that they support efficient appending. -When we append two normal lists with ++, it has to -walk all the way to the end of the list on the left of ++ and then stick the other one there. But what if -we take the difference list approach and represent our lists as functions? Well -then, appending two difference lists can be done like so: -

- -
-f `append` g = \xs -> f (g xs)
-
- -

-Remember, f and g are -functions that take lists and prepend something to them. So, for instance, if -f is the function ("dog"++) (just another -way of writing \xs -> "dog" ++ xs) and g the function ("meat"++), then -f `append` g makes a new function that's equivalent -to the following: -

- -
-\xs -> "dog" ++ ("meat" ++ xs)
-
- -

-We've appended two difference lists just by making a new function that first -applies one difference list some list and then the other. -

- -

-Let's make a newtype wrapper for difference lists so -that we can easily give them monoid instances: -

- -
-newtype DiffList a = DiffList { getDiffList :: [a] -> [a] }
-
- -

-The type that we wrap is [a] -> [a] because a -difference list is just a function that takes a list and returns another. -Converting normal lists to difference lists and vice versa is easy: -

- -
-toDiffList :: [a] -> DiffList a
-toDiffList xs = DiffList (xs++)
-
-fromDiffList :: DiffList a -> [a]
-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: -

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

-Notice how for lists, mempty is just the -id function and mappend is -actually just function composition. Let's see if this works: -

- -
-ghci> fromDiffList (toDiffList [1,2,3,4] `mappend` toDiffList [1,2,3])
-[1,2,3,4,1,2,3]
-
- -

-Tip-top! Now we can increase the efficiency of our gcdReverse function by making it use difference lists instead of normal -lists: -

- - -
-import Control.Monad.Writer
-
-gcd' :: Int -> Int -> Writer (DiffList String) Int
-gcd' a b
-    | b == 0 = do
-        tell (toDiffList ["Finished with " ++ show a])
-        return a
-    | otherwise = do
-        result <- gcd' b (a `mod` b)
-        tell (toDiffList [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)])
-        return result
-
- -

-We only had to change the type of the monoid from [String] -to DiffList String and then when using -tell, convert our normal lists into difference lists -with toDiffList. Let's see if the log gets assembled -properly: -

- -
-ghci> mapM_ putStrLn . fromDiffList . snd . runWriter $ gcdReverse 110 34
-Finished with 2
-8 mod 2 = 0
-34 mod 8 = 2
-110 mod 34 = 8
-
- -

-We do gcdReverse 110 34, then use runWriter -to unwrap it from the newtype, then apply snd to that to just get the log, then apply fromDiffList to convert it to a normal list and then -finally print its entries to the screen. -

- -

Comparing Performance

- -

-To get a feel for just how much difference lists may improve your performance, -consider this function that just counts down from some number to zero, but -produces its log in reverse, like gcdReverse, so that the numbers -in the log will actually be counted up: -

- -
-finalCountDown :: Int -> Writer (DiffList String) ()
-finalCountDown 0 = do
-    tell (toDiffList ["0"])
-finalCountDown x = do
-    finalCountDown (x-1)
-    tell (toDiffList [show x])
-
- -

-If we give it 0, it just logs it. For any other number, it -first counts down its predecessor to 0 and then appends -that number to the log. So if we apply finalCountDown to -100, the string "100" will come last in the -log. -

- -

-Anyway, if you load this function in GHCi and apply it to a big number, -like 500000, you'll see that it quickly starts counting from -0 onwards: - -

-ghci> mapM_ putStrLn . fromDiffList . snd . runWriter $ finalCountDown 500000
-0
-1
-2
-...
-
- -

-However, if we change it to use normal lists instead of difference lists, like so: -

- -
-finalCountDown :: Int -> Writer [String] ()
-finalCountDown 0 = do
-    tell ["0"]
-finalCountDown x = do
-    finalCountDown (x-1)
-    tell [show x]
-
- -

-And then tell GHCi to start counting: -

- -
-ghci> mapM_ putStrLn . snd . runWriter $ finalCountDown 500000
-
- -

-We'll see that the counting is really slow. -

- -

-Of course, this is not the proper and scientific way to test how fast our -programs are, but we were able to see that in this case, using difference lists -starts producing results right away whereas normal lists take forever. -

- -

-Oh, by the way, the song Final Countdown by Europe is now stuck in your head. -Enjoy! -

- - -

Reader? Ugh, not this joke again.

- -bang you're dead - -

-In the chapter about -applicatives, we saw -that the function type, (->) r is an instance of -Functor. Mapping a function f over a function g will make a -function that takes the same thing as g, applies -g to it and then applies f -to that result. So basically, we're making a new function that's like g, only before returning its result, f gets applied to that result as well. For instance: -

- -
-ghci> let f = (*5)
-ghci> let g = (+3)
-ghci> (fmap f g) 8
-55
-
- -

-We've also seen that functions are applicative functors. They allow us to -operate on the eventual results of functions as if we already had their results. -Here's an example: -

- -
-ghci> let f = (+) <$> (*2) <*> (+10)
-ghci> f 3
-19
-
- -

-The expression (+) <$> (*2) <*> (+10) -makes a function that takes a number, gives that number to (*2) and (+10) and then adds -together the results. For instance, if we apply this function to 3, it applies both (*2) and -(+10) to 3, giving -6 and 13. Then, it calls -(+) with 6 and -13 and the result is 19. -

- -

-Not only is the function type (->) r a functor -and an applicative functor, but it's also a monad. Just like other monadic -values that we've met so far, a function can also be considered a value with -a context. The context for functions is that that value is not present yet and -that we have to apply that function to something in order to get its result -value. -

- -

-Because we're already acquainted with how functions work as functors and -applicative functors, let's dive right in and see what their -Monad instance looks like. It's located in -Control.Monad.Instances and it goes a little -something like this: -

- -
-instance Monad ((->) r) where
-    return x = \_ -> x
-    h >>= f = \w -> f (h w) w
-
- -

-We've already seen how pure is implemented for -functions, and return is pretty much the same thing -as pure. It takes a value and puts it in a minimal -context that always has that value as its result. And the only way to make a -function that always has a certain value as its result is to make it completely -ignore its parameter. -

- -

-The implementation for >>= seems a bit cryptic, -but it's really not all that. When we use >>= -to feed a monadic value to a function, the result is always a monadic value. So -in this case, when we feed a function to another function, the result is a -function as well. That's why the result starts off as a lambda. All of the -implementations of >>= so far always somehow -isolated the result from the monadic value and then applied the function -f to that result. The same thing happens here. To get -the result from a function, we have to apply it to something, which is why we do -(h w) here to get the result from the function and -then we apply f to that. f -returns a monadic value, which is a function in our case, so we apply it to -w as well. -

- -

-If you don't understand how >>= works at this point, don't -worry, because with examples we'll see how this is a really simple monad. Here's -a do expression that utilizes this monad: -

- -
-import Control.Monad.Instances
-
-addStuff :: Int -> Int
-addStuff = do
-    a <- (*2)
-    b <- (+10)
-    return (a+b)
-
- -

-This is the same thing as the applicative expression that we wrote earlier, only -now it relies on functions being monads. A do -expression always results in a monadic value and this one is no different. The -result of this monadic value is a function. What happens here is that it takes a -number and then (*2) gets applied to that number and -the result becomes a. (+10) is applied to the same number that -(*2) got applied to and the result becomes b. return, like in other monads, -doesn't have any other effect but to make a monadic value that presents some -result. This presents a+b as the result of this -function. If we test it out, we get the same result as before: -

- -
-ghci> addStuff 3
-19
-
- -

-Both (*2) and (+10) get -applied to the number 3 in this case. return (a+b) does as well, but it ignores it and always -presents a+b as the result. For this reason, the -function monad is also called the reader monad. All the functions read from a -common source. To illustrate this even better, we can rewrite -addStuff like so: -

- -
-addStuff :: Int -> Int
-addStuff x = let
-    a = (*2) x
-    b = (+10) x
-    in a+b
-
- -

-We see that the reader monad allows us to treat functions as values with a -context. We can act as if we already know what the functions will return. It -does this by gluing functions together into one function and then giving that -function's parameter to all of the functions that it was glued from. So if we -have a lot of functions that are all just missing one parameter and they'd -eventually be applied to the same thing, we can use the reader monad to sort of -extract their future results and the >>= -implementation will make sure that it all works out. -

- - -

Tasteful stateful computations

-don't jest with texas - -

-Haskell is a pure language and because of that, our programs are made of -functions that can't change any global state or variables, -they can only do some computations and return them results. This restriction -actually makes it easier to think about our programs, as it frees us from -worrying what every variable's value is at some point in time. However, some -problems are inherently stateful in that they rely on some state that changes -over time. While such problems aren't a problem for Haskell, they can be a bit -tedious to model sometimes. That's why Haskell features a thing called the state -monad, which makes dealing with stateful problems a breeze while still keeping -everything nice and pure. -

- -

-When we were dealing with random numbers, we dealt with functions that took a -random generator as a parameter and returned a random number and a new random -generator. If we wanted to generate several random numbers, we always had to use -the random generator that a previous function returned along with its result. -When making a function that takes a StdGen and tosses -a coin three times based on that generator, we had to do this: -

- -
-threeCoins :: StdGen -> (Bool, Bool, Bool)
-threeCoins gen =
-    let (firstCoin, newGen) = random gen
-        (secondCoin, newGen') = random newGen
-        (thirdCoin, newGen'') = random newGen'
-    in  (firstCoin, secondCoin, thirdCoin)
-
- -

-It took a generator gen and then random gen returned a Bool value -along with a new generator. To throw the second coin, we used the new generator, -and so on. In most other languages, we wouldn't have to return a new generator -along with a random number. We could just modify the existing one! But since -Haskell is pure, we can't do that, so we had to take some state, make a result -from it and a new state and then use that new state to generate new results. -

- -

-You'd think that to avoid manually dealing with stateful computations in this -way, we'd have to give up the purity of Haskell. Well, we don't have to, since -there exist a special little monad called the state monad which handles all this -state business for us and without giving up any of the purity that makes Haskell -programming so cool. -

- -

-So, to help us understand this concept of stateful computations better, let's go -ahead and give them a type. We'll say that a stateful computation is a function -that takes some state and returns a value along with some new state. That -function would have the following type: -

- -
-s -> (a,s)
-
- -

-s is the type of the state and a the result of the stateful computations. -

- -
-Assignment in most other languages could be thought of as a stateful -computation. For instance, when we do x = 5 in an -imperative language, it will usually assign the value 5 to the variable x and it will -also have the value 5 as an expression. If you look -at that functionally, you could look at it as a function that takes a state -(that is, all the variables that have been assigned previously) and returns a -result (in this case 5) and a new state, which would -be all the previous variable mappings plus the newly assigned variable. -
- -

-This stateful computation, a function that takes a state -and returns a result and a new state, can be thought of as a value with a -context as well. The actual value is the result, whereas the context is that we -have to provide some initial state to actually get that result and that apart -from getting a result we also get a new state. -

- -

Stacks and stones

- -

-Say we want to model operating a stack. You have a stack of things one on top -of another and you can either push stuff on top of that stack or you can take stuff -off the top of the stack. When you're putting an item on top of the stack we say -that you're pushing it to the stack and when you're taking stuff off the -top we say that you're popping it. If you want something that's at the bottom -of the stack, you have to pop everything that's above it. -

- -

-We'll use a list to represent our stack and the head of the list will be the top -of the stack. To help us with our task, we'll make two functions: pop and push. pop will take a stack, pop one item and return that item as -the result and also return a new stack, without that item. push will take an item and a stack and then push that item -onto the stack. It will return () as its result, -along with a new stack. Here goes: -

- -
-type Stack = [Int]
-
-pop :: Stack -> (Int,Stack)
-pop (x:xs) = (x,xs)
-
-push :: Int -> Stack -> ((),Stack)
-push a xs = ((),a:xs)
-
- -

-We used () as the result when pushing to the stack -because pushing an item onto the stack doesn't have any important result value, -its main job is to change the stack. Notice how we just apply the first parameter of push, we get a stateful -computation. pop is already a stateful computation -because of its type. -

- -

-Let's write a small piece of code to simulate a stack using these functions. -We'll take a stack, push 3 to it and then pop two -items, just for kicks. Here it is: -

- -
-stackManip :: Stack -> (Int, Stack)
-stackManip stack = let
-    ((),newStack1) = push 3 stack
-    (a ,newStack2) = pop newStack1
-    in pop newStack2
-
- -

-We take a stack and then we do push 3 stack, which results in a tuple. The first part of -the tuple is a () and the second is a new stack and -we call it newStack1. -Then, we pop a number from newStack1, which results -in a number a (which is the 3) that we pushed and a new stack which we call -newStack2. Then, we pop a number off -newStack2 and we get a number that's b and a newStack3. We return a -tuple with that number and that stack. Let's try it out: -

- -
-ghci> stackManip [5,8,2,1]
-(5,[8,2,1])
-
- -

-Cool, the result is 5 and the new stack is -[8,2,1]. Notice how stackManip -is itself a stateful computation. We've taken a bunch of stateful computations -and we've sort of glued them together. Hmm, sounds familiar. -

-

-The above code for -stackManip is kind of tedious since we're manually -giving the state to every stateful computation and storing it and then giving it -to the next one. Wouldn't it be cooler if, instead of giving the stack manually -to each function, we could write something like this: -

- -
-stackManip = do
-    push 3
-    a <- pop
-    pop
-
- -

-Well, using the state monad will allow us to do exactly this. With it, we will -be able to take stateful computations like these and use them without having to manage -the state manually. -

- -

The State monad

- -

-The Control.Monad.State module provides a newtype that wraps stateful computations. Here's its definition: -

- -
-newtype State s a = State { runState :: s -> (a,s) }
-
- -

-A State s a is a stateful computation that -manipulates a state of type s and has a result of -type a. -

- -

-Now that we've seen what stateful computations are about and how they can even be -thought of as values with contexts, let's check out their Monad instance: -

- -
-instance Monad (State s) where
-    return x = State $ \s -> (x,s)
-    (State h) >>= f = State $ \s -> let (a, newState) = h s
-                                        (State g) = f a
-                                    in  g newState
-
- -

-Let's take a gander at return first. Our aim -with return is to take a value and make a stateful -computation that always has that value as its result. That's why we just make a -lambda \s -> (x,s). We always present x as the -result of the stateful computation and the state is kept unchanged, because -return has to put a value in a minimal context. So -return will make a stateful computation that presents -a certain value as the result and keeps the state unchanged. -

- -im a cop - -

-What about >>=? Well, the result of feeding a -stateful computation to a function with >>= has to be a -stateful computation, right? So we start off with the State -newtype wrapper and then we type out a lambda. This -lambda will be our new stateful computation. But what goes on in it? Well, we -somehow have to extract the result value from the first stateful computation. -Because we're in a stateful computation right now, we can give the stateful -computation h our current state s, which results in a pair of result and a new state: -(a, newState). Every time so far when we were -implementing >>=, once we had the extracted -the result from the monadic value, we applied the function -f to it to get the new monadic value. In -Writer, after doing that and getting the new monadic -value, we still had to make sure that the context was taken care of by -mappending the old monoid value with the new one. -Here, we do f a and we get a new stateful computation -g. Now that we have a new stateful computation and a -new state (goes by the name of newState) we just -apply that stateful computation g to the newState. The result is a tuple of final result and final -state! -

- -

-So with >>=, we kind of glue two stateful -computations together, only the second one is hidden inside a function that -takes the previous one's result. Because pop and -push are already stateful computations, it's easy to -wrap them into a State wrapper. Watch: -

- -
-import Control.Monad.State
-
-pop :: State Stack Int
-pop = State $ \(x:xs) -> (x,xs)
-
-push :: Int -> State Stack ()
-push a = State $ \xs -> ((),a:xs)
-
- -

-pop is already a stateful computation and -push takes an Int and -returns a stateful computation. Now we can rewrite our previous example of -pushing 3 onto the stack and then popping two numbers -off like this: -

- -
-import Control.Monad.State
-
-stackManip :: State Stack Int
-stackManip = do
-    push 3
-    a <- pop
-    pop
-
- -

-See how we've glued a push and two pops into one stateful computation? When we -unwrap it from its newtype wrapper we get a function -to which we can provide some initial state: -

- -
-ghci> runState stackManip [5,8,2,1]
-(5,[8,2,1])
-
- -

-We didn't have to bind the second pop to a because we didn't use that a -at all. So we could have written it like this: -

- -
-stackManip :: State Stack Int
-stackManip = do
-    push 3
-    pop
-    pop
-
- -

-Pretty cool. But what if we want to do this: pop one number off the stack and -then if that number is 5 we just put it back onto the -stack and stop but if it isn't 5, we push 3 and 8 back on? Well, here's -the code: -

- -
-stackStuff :: State Stack ()
-stackStuff = do
-    a <- pop
-    if a == 5
-        then push 5
-        else do
-            push 3
-            push 8
-
- -

-This is quite straightforward. Let's run it with an initial stack. -

- -
-ghci> runState stackStuff [9,0,2,1,0]
-((),[8,3,0,2,1,0])
-
- -

-Remember, do expressions result in monadic values and -with the State monad, a single do expression is also a stateful function. Because -stackManip and stackStuff -are ordinary stateful computations, we can glue them together to produce further -stateful computations. -

- -
-moreStack :: State Stack ()
-moreStack = do
-    a <- stackManip
-    if a == 100
-        then stackStuff
-        else return ()
-
- -

-If the result of stackManip on the current stack -is 100, we run stackStuff, -otherwise we do nothing. return () just keeps the -state as it is and does nothing. -

- -

-The Control.Monad.State module provides a type class -that's called MonadState and it features two pretty -useful functions, namely get and put. For State, the get function is implemented like this: -

- -
-get = State $ \s -> (s,s)
-
- -

-So it just takes the current state and presents it as the result. The -put function takes some state and makes a stateful -function that replaces the current state with it: -

- -
-put newState = State $ \s -> ((),newState)
-
- -

-So with these, we can see what the current stack is or we can replace it with a -whole other stack. Like so: -

- -
-stackyStack :: State Stack ()
-stackyStack = do
-    stackNow <- get
-    if stackNow == [1,2,3]
-        then put [8,3,1]
-        else put [9,2,1]
-
- -

-It's worth examining what the type of >>= would -be if it only worked for State values: -

- -
-(>>=) :: State s a -> (a -> State s b) -> State s b
-
- -

-See how the type of the state s stays the same but -the type of the result can change from a to -b? This means that we can glue together several -stateful computations whose results are of different types but the type of the -state has to stay the same. Now why is that? Well, for instance, for Maybe, >>= has this type: -

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

-It makes sense that the monad itself, Maybe, doesn't -change. It wouldn't make sense to use >>= -between two different monads. Well, for the state monad, the monad is actually State s, so if that s was -different, we'd be using >>= between two -different monads. -

- -

Randomness and the state monad

- -

-At the beginning of this section, we saw how generating numbers can sometimes be -awkward because every random function takes a generator and returns a random -number along with a new generator, which must then be used instead of the old -one if we want to generate another random number. The state monad makes dealing -with this a lot easier. -

- -

-The random function from System.Random -has the following type: -

- -
-random :: (RandomGen g, Random a) => g -> (a, g)
-
- -

-Meaning it takes a random generator and produces a random number along with a -new generator. We can see that it's a stateful computation, so we can wrap it in -the State newtype -constructor and then use it as a monadic value so that passing of the state gets -handled for us: -

- -
-import System.Random
-import Control.Monad.State
-
-randomSt :: (RandomGen g, Random a) => State g a
-randomSt = State random
-
- -

-So now if we want to throw three coins (True is -tails, False is heads) we just do the following: -

- -
-import System.Random
-import Control.Monad.State
-
-threeCoins :: State StdGen (Bool,Bool,Bool)
-threeCoins = do
-    a <- randomSt
-    b <- randomSt
-    c <- randomSt
-    return (a,b,c)
-
- -

-threeCoins is now a stateful computation and after -taking an initial random generator, it passes it to the first randomSt, which produces a number and a new generator, -which gets passed to the next one and so on. We use return -(a,b,c) to present (a,b,c) as the result -without changing the most recent generator. Let's give this a go: -

- -
-ghci> runState threeCoins (mkStdGen 33)
-((True,False,True),680029187 2103410263)
-
- -

-Nice. Doing these sort of things that require some state to be kept in between -steps just became much less of a hassle! -

- - -

Error error on the wall

- -

-We know by now that Maybe is used to add a -context of possible failure to values. A value can be a -Just something or a Nothing. -However useful it may be, when we have a Nothing, all -we know is that there was some sort of failure, but there's no way to cram -some more info in there telling us what kind of failure it was or why it failed. -

- -

-The Either e a type on the other hand, allows us to -incorporate a context of possible failure to our values while also being able to -attach values to the failure, so that they can describe what went wrong or -provide some other useful info regarding the failure. An Either e a value can either be a Right value, signifying the right answer and a success, or -it can be a Left value, signifying failure. For -instance: -

- - -
-ghci> :t Right 4
-Right 4 :: (Num t) => Either a t
-ghci> :t Left "out of cheese error"
-Left "out of cheese error" :: Either [Char] b
-
- -

-This is pretty much just an enhanced Maybe, -so it makes sense for it to be a monad, because it can also be viewed as a value -with an added context of possible failure, only now there's a value attached -when there's an error as well. -

- -

-Its Monad instance is similar to that of Maybe and it can be found in Control.Monad.Error: -

- -
-instance (Error e) => Monad (Either e) where
-    return x = Right x
-    Right x >>= f = f x
-    Left err >>= f = Left err
-    fail msg = Left (strMsg msg)
-
- -

-return, as always, takes a value and puts it in a -default minimal context. It wraps our value in the Right -constructor because we're using Right to represent a -successful computation where a result is present. This is a lot like return for Maybe. -

- -

-The >>= examines two possible cases: a -Left and a Right. In the -case of a Right, the function f is applied to the value inside it, similar to how in the -case of a Just, the function is just applied to its -contents. In the case of an error, the Left value is -kept, along with its contents, which describe the failure. -

- -

-The Monad instance for Either -e makes an -additional requirement, and that is that the type of the value contained in a -Left, the one that's indexed by the e -type parameter, has to be an instance of the Error -type class. The Error type class is for types whose -values can act like error messages. It defines the strMsg function, which takes -an error in the form of a string and returns such a value. A good example of an -Error instance is, well, the String type! In the case of String, the strMsg -function just returns the string that it got: -

- -
-ghci> :t strMsg
-strMsg :: (Error a) => String -> a
-ghci> strMsg "boom!" :: String
-"boom!"
-
- -

-But since we usually use String to describe the error -when using Either, we don't have to worry about this -too much. When a pattern match fails in do notation, -a Left value is used to signify this failure. -

- -

-Anyway, here are a few examples of usage: -

- -
-ghci> Left "boom" >>= \x -> return (x+1)
-Left "boom"
-ghci> Right 100 >>= \x -> Left "no way!"
-Left "no way!"
-
- -

-When we use >>= to feed a Left value to a function, -the function is ignored and an identical Left value -is returned. When we feed a Right value to a function, -the function gets applied to what's on the inside, but in this case that -function produced a Left value anyway! -

- -

-When we try to feed a Right value to a function that -also succeeds, we're tripped up by a peculiar type error! Hmmm. -

- -
-ghci> Right 3 >>= \x -> return (x + 100)
-
-<interactive>:1:0:
-    Ambiguous type variable `a' in the constraints:
-      `Error a' arising from a use of `it' at <interactive>:1:0-33
-      `Show a' arising from a use of `print' at <interactive>:1:0-33
-    Probable fix: add a type signature that fixes these type variable(s)
-
- -

-Haskell says that it doesn't know which type to choose for the -e part of our Either e a -typed value, even though we're just printing the Right part. -This is due to the Error e constraint on the -Monad instance. So if you get type errors like this -one when using Either as a monad, just add an -explicit type signature: -

- -
-ghci> Right 3 >>= \x -> return (x + 100) :: Either String Int
-Right 103
-
- -

-Alright, now it works! -

- -

-Other than this little hangup, using this monad is very similar to using -Maybe as a monad. In the previous chapter, we used -the monadic aspects of Maybe to simulate birds -landing on the balancing pole of a tightrope walker. As an exercise, you can -rewrite that with the error monad so that when the -tightrope walker slips and falls, we remember how many birds were on each side of -the pole when he fell. -

- - -

Some useful monadic functions

- - -

-In this section, we're going to explore a few functions that either operate on -monadic values or return monadic values as their results (or both!). Such -functions are usually referred to as monadic functions. While some of them will -be brand new, others will be monadic counterparts of functions that we already -know, like filter and foldl. -Let's see what they are then! -

- -

liftM and friends

- -im a cop too - -

-When we started our journey to the top of Monad Mountain, we first looked -at functors, which are for things that can be mapped over. Then, we learned -about improved functors called applicative functors, which allowed us to apply -normal functions between several applicative values as well as to take a normal -value and put it in some default context. Finally, we introduced -monads as improved applicative functors, which added the ability for these -values with context to somehow be fed into normal functions. -

- -

-So every monad is an applicative functor and every applicative functor is a -functor. The Applicative type class has a class -constraint such that our type has to be an instance of Functor before we can make it an instance of Applicative. But even though Monad should have the same -constraint for Applicative, as every monad is an -applicative functor, it doesn't, because the Monad -type class was introduced to Haskell way before Applicative. -

- -

-But even though every monad is a functor, we don't have to rely on it having a -Functor instance because of the liftM function. liftM takes a -function and a monadic value and maps it over the monadic value. So it's pretty -much the same thing as fmap! This is -liftM's type: -

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

-And this is the type of fmap: -

- -
-fmap :: (Functor f) => (a -> b) -> f a -> f b
-
- -

-If the Functor and Monad -instances for a type obey the functor and monad laws, these two amount to the -same thing (and all the monads that we've met so far obey both). This is kind of -like pure and return do -the same thing, only one has an Applicative class -constraint whereas the other has a Monad one. Let's -try liftM out: -

- -
-ghci> liftM (*3) (Just 8)
-Just 24
-ghci> fmap (*3) (Just 8)
-Just 24
-ghci> runWriter $ liftM not $ Writer (True, "chickpeas")
-(False,"chickpeas")
-ghci> runWriter $ fmap not $ Writer (True, "chickpeas")
-(False,"chickpeas")
-ghci> runState (liftM (+100) pop) [1,2,3,4]
-(101,[2,3,4])
-ghci> runState (fmap (+100) pop) [1,2,3,4]
-(101,[2,3,4])
-
- -

-We already know quite well how fmap works with Maybe values. And liftM does the -same thing. For Writer values, the function is mapped -over the first component of the tuple, which is the result. Doing fmap or liftM over a stateful -computation results in another stateful computation, only its eventual result is modified -by the supplied function. Had we not mapped (+100) over -pop in this case before running it, it would have -returned (1,[2,3,4]). -

- -

-This is how liftM is implemented: -

- -
-liftM :: (Monad m) => (a -> b) -> m a -> m b
-liftM f m = m >>= (\x -> return (f x))
-
- -

-Or with do notation: -

- -
-liftM :: (Monad m) => (a -> b) -> m a -> m b
-liftM f m = do
-    x <- m
-    return (f x)
-
- -

-We feed the monadic value m into the function and -then we apply the function f to its result before -putting it back into a default context. Because of the monad laws, this is -guaranteed not to change the context, only the result that the monadic value -presents. We see that liftM is implemented without -referencing the Functor type class at all. This means -that we can implement fmap (or liftM, whatever you want to call it) just by using the -goodies that monads offer us. Because of this, we can conclude that monads are -stronger than just regular old functors. -

- -

-The Applicative type class allows us to apply -functions between values with contexts as if they were normal values. Like this: -

- -
-ghci> (+) <$> Just 3 <*> Just 5
-Just 8
-ghci> (+) <$> Just 3 <*> Nothing
-Nothing
-
- -

-Using this applicative style makes things pretty easy. <$> is just fmap and -<*> is a function from the -Applicative type class that has the following type: -

- -
-(<*>) :: (Applicative f) => f (a -> b) -> f a -> f b
-
- -

-So it's kind of like fmap, only the function itself -is in a context. We have to somehow extract it from the context and map it over -the f a value and then assemble the context back -together. Because all functions are curried in Haskell by default, we can use -the combination of <$> and <*> to apply functions that take several parameters -between applicative values. -

- -

-Anyway, it turns out that just like fmap, <*> can also be implemented by using only what the -Monad type class give us. The ap function is basically <*>, only it has a -Monad constraint instead of an Applicative one. Here's its definition: -

- -
-ap :: (Monad m) => m (a -> b) -> m a -> m b
-ap mf m = do
-    f <- mf
-    x <- m
-    return (f x)
-
- -

-mf is a monadic value whose result is a function. -Because the function is in a context as well as the value, we get the function -from the context and call it f, then get the value -and call that x and then finally apply the function -to the value and present that as a result. Here's a quick demonstration: -

- - -
-ghci> Just (+3) <*> Just 4
-Just 7
-ghci> Just (+3) `ap` Just 4
-Just 7
-ghci> [(+1),(+2),(+3)] <*> [10,11]
-[11,12,12,13,13,14]
-ghci> [(+1),(+2),(+3)] `ap` [10,11]
-[11,12,12,13,13,14]
-
- -

-Now we see that monads are stronger than applicatives as well, because we can -use the functions from Monad to implement the ones -for Applicative. In fact, many times when a type is -found to be a monad, people first write up a Monad -instance and then make an Applicative instance by just -saying that pure is return -and <*> is ap. -Similarly, if you already have a Monad instance for -something, you can give it a Functor instance just -saying that fmap is liftM. -

- -

-The liftA2 function is a convenience function for -applying a function between two applicative values. It's defined simply like -so: -

- -
-liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
-liftA2 f x y = f <$> x <*> y
-
- -

-The liftM2 function does the same thing, only it has -a Monad constraint. There also exist liftM3 and liftM4 and -liftM5. -

- -

-We saw how monads are stronger than applicatives and functors and how even -though all monads are functors and applicative functors, they don't necessarily -have Functor and Applicative -instances, so we examined the monadic equivalents of the functions that functors -and applicative functors use. -

- -

The join function

- -

-Here's some food for thought: if the result of one monadic value is another -monadic value i.e. if one monadic value is nested inside the other, can you -flatten them to just a single normal monadic value? Like, if we have -Just (Just 9), can we make that into Just 9? It turns out that any nested monadic value can be -flattened and that this is actually a property unique to monads. For this, the -join function exists. Its type is this: -

- -
-join :: (Monad m) => m (m a) -> m a
-
- -

-So it takes a monadic value within a monadic value and gives us just a monadic -value, so it sort of flattens it. Here it is with some Maybe values: -

- -
-ghci> join (Just (Just 9))
-Just 9
-ghci> join (Just Nothing)
-Nothing
-ghci> join Nothing
-Nothing
-
- -

-The first line has a successful computation as a result of a successful -computation, so they're both just joined into one big successful computation. -The second line features a Nothing as a result of a -Just value. Whenever we were dealing with Maybe values before and we wanted to combine several of -them into one, be it with <*> or >>=, they all had to be Just values -for the result to be a Just value. If there was any -failure along the way, the result was a failure and the same thing happens here. -In the third line, we try to flatten what is from the onset a failure, so the -result is a failure as well. -

- - -

-Flattening lists is pretty intuitive: -

- -
-ghci> join [[1,2,3],[4,5,6]]
-[1,2,3,4,5,6]
-
- -

-As you can see, for lists, join is just -concat. To flatten a Writer value -whose result is a Writer value itself, we have to -mappend the monoid value. -

- -
-ghci> runWriter $ join (Writer (Writer (1,"aaa"),"bbb"))
-(1,"bbbaaa")
-
- -

-The outer monoid value -"bbb" comes first and then to it "aaa" is appended. Intuitively speaking, when you want to -examine what the result of a Writer value is, you -have to write its monoid value to the log first and only then can you examine -what it has inside. -

- -

-Flattening Either values is very similar to -flattening Maybe values: -

- -
-ghci> join (Right (Right 9)) :: Either String Int
-Right 9
-ghci> join (Right (Left "error")) :: Either String Int
-Left "error"
-ghci> join (Left "error") :: Either String Int
-Left "error"
-
- - -

-If we apply join to a stateful computation whose -result is a stateful computation, the result is a stateful computation that -first runs the outer stateful computation and then the resulting one. Watch: -

- -
-ghci> runState (join (State $ \s -> (push 10,1:2:s))) [0,0,0]
-((),[10,1,2,0,0,0])
-
- -

-The lambda here takes a state and puts 2 and -1 onto the stack and presents push 10 as its result. So when this whole thing is -flattened with join and then run, it first puts -2 and 1 onto the stack and -then push 10 gets carried out, pushing a 10 onto the top. -

- -

-The implementation for join is as follows: -

- -
-join :: (Monad m) => m (m a) -> m a
-join mm = do
-    m <- mm
-    m
-
- -

-Because the result of mm is a monadic value, we -get that result and then just put it on a line of its own because it's a monadic -value. The trick here is that when we do m <- mm, -the context of the monad in which we -are in gets taken care of. That's why, for instance, Maybe values result in Just -values only if the outer and inner values are both Just values. Here's what this would look like if the mm value was set in advance to Just (Just 8): -

- -
-joinedMaybes :: Maybe Int
-joinedMaybes = do
-    m <- Just (Just 8)
-    m
-
- -im a cop too as well also - -

-Perhaps the most interesting thing about join is -that for every monad, feeding a monadic value to a function with >>= is the same thing as just mapping that function -over the value and then using join to flatten the -resulting nested monadic value! In other words, m >>= -f is always the same thing as join (fmap f m)! -It makes sense when you think about it. With >>=, we're always thinking about -how to feed a monadic value to a function that takes a normal value but returns -a monadic value. If we just map that function over the monadic value, we have a -monadic value inside a monadic value. For instance, say we have Just 9 and the function \x -> Just -(x+1). If we map this function over Just 9, -we're left with Just (Just 10). -

- -

-The fact that m >>= f always equals -join (fmap f m) is very useful if we're making our -own Monad instance for some type because it's often -easier to figure out how we would flatten a nested monadic value than figuring -out how to implement >>=. -

- -

filterM

- -

-The filter function is pretty much the bread of -Haskell programming (map being the butter). It takes -a predicate and a list to filter out and then returns a new list where only the -elements that satisfy the predicate are kept. Its type is this: -

- -
-filter :: (a -> Bool) -> [a] -> [a]
-
- -

-The predicate takes an element of the list and returns a Bool value. Now, what if the Bool value that it returned -was actually a monadic value? Whoa! That is, what if it came with a context? -Could that work? For instance, what if every True or a False value that the -predicate produced also had an accompanying monoid value, like ["Accepted the number 5"] or ["3 is too -small"]? That sounds like it could work. If that were the case, we'd -expect the resulting list to also come with a log of all the log values that -were produced along the way. So if the Bool that the -predicate returned came with a context, we'd expect the final resulting list to have -some context attached as well, otherwise the context that each Bool came with would be lost. -

- -

-The filterM function from Control.Monad -does just what we want! Its type is this: -

- -
-filterM :: (Monad m) => (a -> m Bool) -> [a] -> m [a]
-
- -

-The predicate returns a monadic value whose result is a Bool, but because it's a monadic value, its context can be -anything from a possible failure to non-determinism and more! To ensure that the -context is reflected in the final result, the result is also a monadic value. -

- -

-Let's take a list and only keep those values that are smaller than 4. To -start, we'll just use the regular filter function: -

- -
-ghci> filter (\x -> x < 4) [9,1,5,2,10,3]
-[1,2,3]
-
- -

-That's pretty easy. Now, let's make a predicate that, aside from presenting a -True or False result, also -provides a log of what it did. Of course, we'll be using the Writer monad for this: -

- -
-keepSmall :: Int -> Writer [String] Bool
-keepSmall x
-    | x < 4 = do
-        tell ["Keeping " ++ show x]
-        return True
-    | otherwise = do
-        tell [show x ++ " is too large, throwing it away"]
-        return False
-
- -

-Instead of just and returning a Bool, this function -returns a Writer [String] Bool. It's a monadic -predicate. Sounds fancy, doesn't it? If the number is smaller than 4 we report that we're keeping it and then return True. -

- -

-Now, let's give it to filterM along with a list. -Because the predicate returns a -Writer value, the resulting list will also -be a Writer value. -

- -
-ghci> fst $ runWriter $ filterM keepSmall [9,1,5,2,10,3]
-[1,2,3]
-
- -

-Examining the result of the resulting Writer value, -we see that everything is in order. Now, let's print the log and see what we -got: -

- -
-ghci> mapM_ putStrLn $ snd $ runWriter $ filterM keepSmall [9,1,5,2,10,3]
-9 is too large, throwing it away
-Keeping 1
-5 is too large, throwing it away
-Keeping 2
-10 is too large, throwing it away
-Keeping 3
-
- -

-Awesome. So just by providing a monadic predicate to filterM, -we were able to filter a list while taking advantage of the monadic context that -we used. -

- -

-A very cool Haskell trick is using filterM to get the -powerset of a list (if we think of them as sets for now). The powerset of some -set is a set of all subsets of that set. So if we have a set like [1,2,3], its powerset would include the following sets: -

- -
-[1,2,3]
-[1,2]
-[1,3]
-[1]
-[2,3]
-[2]
-[3]
-[]
-
- -

-In other words, getting a powerset is like getting all the combinations of -keeping and throwing out elements from a set. [2,3] -is like the original set, only we excluded the number 1. -

- -

-To make a function that returns a powerset of some list, we're going to rely on -non-determinism. We take the list [1,2,3] and then -look at the first element, which is 1 and we ask -ourselves: should we keep it or drop it? Well, we'd like to do both actually. So -we are going to filter a list and we'll use a predicate that non-deterministically -both keeps and drops every element from the list. Here's our powerset function: -

- -
-powerset :: [a] -> [[a]]
-powerset xs = filterM (\x -> [True, False]) xs
-
- -

-Wait, that's it? Yup. We choose to drop and keep every element, regardless of -what that element is. We have a non-deterministic predicate, so the resulting -list will also be a non-deterministic value and will thus be a list of lists. Let's -give this a go: -

- -
-ghci> powerset [1,2,3]
-[[1,2,3],[1,2],[1,3],[1],[2,3],[2],[3],[]]
-
- -

-This takes a bit of thinking to wrap your head around, but if you just consider -lists as non-deterministic values that don't know what to be so they just decide -to be everything at once, it's a bit easier. -

- -

foldM

- -

-The monadic counterpart to foldl is foldM. If you remember your folds from the folds section, you know that foldl takes a binary -function, a starting accumulator and a list to fold up and then folds it from the left -into a single value by using the binary function. foldM does the same -thing, except it takes a binary function that produces a monadic value and folds -the list up with that. Unsurprisingly, the resulting value is also monadic. The -type of foldl is this: -

- -
-foldl :: (a -> b -> a) -> a -> [b] -> a
-
- -

-Whereas foldM has the following type: -

- -
-foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
-
- -

-The value that the binary function returns is monadic and so the result of the -whole fold is monadic as well. Let's sum a list of numbers with a -fold: -

- -
-ghci> foldl (\acc x -> acc + x) 0 [2,8,3,1]
-14
-
- -

-The starting accumulator is 0 and then 2 gets added to the accumulator, resulting in a new -accumulator that has a value of 2. 8 gets added to this accumulator resulting in an -accumulator of 10 and so on and when we reach the -end, the final accumulator is the result. -

- -

-Now what if we wanted to sum a list of numbers but with the added condition that -if any number is greater than 9 in the list, the -whole thing fails? It would make sense to use a binary function that checks if -the current number is greater than 9 and if it is, -fails, and if it isn't, continues on its merry way. Because of this added -possibility of failure, let's make our binary function return a Maybe accumulator instead of a normal one. Here's the -binary function: -

- -
-binSmalls :: Int -> Int -> Maybe Int
-binSmalls acc x
-    | x > 9     = Nothing
-    | otherwise = Just (acc + x)
-
- -

-Because our binary function is now a monadic function, we can't use it with the -normal foldl, but we have to use foldM. Here goes: -

- -
-ghci> foldM binSmalls 0 [2,8,3,1]
-Just 14
-ghci> foldM binSmalls 0 [2,11,3,1]
-Nothing
-
- -

-Excellent! Because one number in the list was greater than 9, the whole thing resulted in a Nothing. Folding with a binary function that returns a -Writer value is cool as well because then you log -whatever you want as your fold goes along its way. -

- -

Making a safe RPN calculator

- -i've found yellow! - -

-When we were solving the problem of implementing an RPN calculator, -we noted that it worked fine as long as the input that it got made sense. -But if something went wrong, it caused our whole program to crash. Now that we -know how to take some code that we have and make it monadic, let's take our RPN -calculator and add error handling to it by taking advantage of the -Maybe monad. -

- -

-We implemented our RPN calculator by taking a string like "1 3 + 2 *", breaking it up into words to get something -like ["1","3","+","2","*"] and then folding over that -list by starting out with an empty stack and then using a binary folding -function that adds numbers to the stack or manipulates numbers on the top of the -stack to add them together and divide them and such. -

- -

-This was the main body of our function: -

- -
-import Data.List
-
-solveRPN :: String -> Double
-solveRPN = head . foldl foldingFunction [] . words
-
- -

-We made the expression into a list of strings, folded over it with our folding -function and then when we were left with just one item in the stack, we returned -that item as the answer. This was the folding function: -

- -
-foldingFunction :: [Double] -> String -> [Double]
-foldingFunction (x:y:ys) "*" = (x * y):ys
-foldingFunction (x:y:ys) "+" = (x + y):ys
-foldingFunction (x:y:ys) "-" = (y - x):ys
-foldingFunction xs numberString = read numberString:xs
-
- -

-The accumulator of the fold was a stack, which we represented with a list of -Double values. As the folding function went over the RPN expression, if -the current item was an operator, it took two items off the top of the stack, -applied the operator between them and then put the result back on the stack. If -the current item was a string that represented a number, it converted that -string into an actual number and returned a new stack that was like the old one, -except with that number pushed to the top. -

- -

-Let's first make our folding function capable of graceful failure. Its type is -going to change from what it is now to this: -

- -
-foldingFunction :: [Double] -> String -> Maybe [Double]
-
- -

-So it will either return Just a new stack or it will -fail with Nothing. -

-

The reads function is like -read, only it returns a list with a single element in -case of a successful read. If it fails to read something, then it returns an -empty list. Apart from returning the value that it read, it also returns the -part of the string that it didn't consume. We're going to say that it always has -to consume the full input to work and make it into a readMaybe function -for convenience. Here it is: -

- -
-readMaybe :: (Read a) => String -> Maybe a
-readMaybe st = case reads st of [(x,"")] -> Just x
-                                _ -> Nothing
-
- -

-Testing it out: -

- -
-ghci> readMaybe "1" :: Maybe Int
-Just 1
-ghci> readMaybe "GO TO HELL" :: Maybe Int
-Nothing
-
- -

-Okay, it seems to work. So, let's make our folding function into a monadic -function that can fail: -

- -
-foldingFunction :: [Double] -> String -> Maybe [Double]
-foldingFunction (x:y:ys) "*" = return ((x * y):ys)
-foldingFunction (x:y:ys) "+" = return ((x + y):ys)
-foldingFunction (x:y:ys) "-" = return ((y - x):ys)
-foldingFunction xs numberString = liftM (:xs) (readMaybe numberString)
-
- -

-The first three cases are like the old ones, except the new stack gets -wrapped in a Just (we used return here to do this, but we could have written -Just just as well). In the last case, we do -readMaybe numberString and then we map (:xs) over it. So if the stack xs -is [1.0,2.0] and readMaybe -numberString results in a Just 3.0, the result -is Just [3.0,1.0,2.0]. If readMaybe numberString results in a Nothing then the result is Nothing. -Let's try out the folding function by itself: -

- -
-ghci> foldingFunction [3,2] "*"
-Just [6.0]
-ghci> foldingFunction [3,2] "-"
-Just [-1.0]
-ghci> foldingFunction [] "*"
-Nothing
-ghci> foldingFunction [] "1"
-Just [1.0]
-ghci> foldingFunction [] "1 wawawawa"
-Nothing
-
- -

-It looks like it's working! And now it's time for the new and improved -solveRPN. Here it is ladies and gents! -

- -
-import Data.List
-
-solveRPN :: String -> Maybe Double
-solveRPN st = do
-    [result] <- foldM foldingFunction [] (words st)
-    return result
-
- -

-Just like before, we take the string and make it into a list of words. Then, -we do a fold, starting with the empty stack, only instead of doing a normal -foldl, we do a foldM. The -result of that foldM should be a Maybe value that contains a list (that's our final stack) -and that list should have only one value. We use a do -expression to get that value and we call it result. In -case the foldM returns a Nothing, the whole thing -will be a Nothing, because that's how Maybe works. Also notice that we pattern match in the -do expression, so if the list has more than one value -or none at all, the pattern match fails and a Nothing -is produced. In the last line we just do return result to present -the result of the RPN calculation as the result of the final Maybe value. -

- -

-Let's give it a shot: -

- -
-ghci> solveRPN "1 2 * 4 +"
-Just 6.0
-ghci> solveRPN "1 2 * 4 + 5 *"
-Just 30.0
-ghci> solveRPN "1 2 * 4"
-Nothing
-ghci> solveRPN "1 8 wharglbllargh"
-Nothing
-
- -

-The first failure happens because the final stack isn't a list with one element -in it and so the pattern matching in the do -expression fails. The second failure happens because readMaybe -returns a Nothing. -

- -

Composing monadic functions

- -

-When we were learning about the monad laws, we said that the -<=< function is just like composition, only -instead of working for normal functions like a -> b, it -works for monadic functions like a -> m b. For -instance: -

- -
-ghci> let f = (+1) . (*100)
-ghci> f 4
-401
-ghci> let g = (\x -> return (x+1)) <=< (\x -> return (x*100))
-ghci> Just 4 >>= g
-Just 401
-
- - -

-In this example we first composed two normal functions, applied the -resulting function to 4 and then we composed two -monadic functions and fed Just 4 to the resulting -function with >>=. -

- -

-If we have a bunch of functions in a list, we can compose them one all into one -big function by just using id as the starting -accumulator and the . function as the binary -function. Here's an example: -

- -
-ghci> let f = foldr (.) id [(+1),(*100),(+1)]
-ghci> f 1
-201
-
- -

-The function f takes a number and then adds -1 to it, multiplies the result by -100 and then adds 1 to -that. Anyway, we can compose monadic functions in the same way, only instead -normal composition we use <=< and instead of -id we use return. We don't -have to use a foldM over a foldr or anything because the <=< -function makes sure that composition happens in a monadic fashion. -

- -

-When we were getting to know the list monad in the previous chapter, we used -it to figure out if a knight can go from one position on a -chessboard to another in exactly three moves. We had a function -called moveKnight which took the knight's position on -the board and returned all the possible moves that he can make next. Then, to -generate all the possible positions that he can have after taking three moves, -we made the following function: -

- -
-in3 start = return start >>= moveKnight >>= moveKnight >>= moveKnight
-
- -

-And to check if he can go from start to -end in three moves, we did the following: -

- -
-canReachIn3 :: KnightPos -> KnightPos -> Bool
-canReachIn3 start end = end `elem` in3 start
-
- -

-Using monadic function composition, we can make a function like in3, only instead of generating all the positions that the -knight can have after making three moves, we can do it for an arbitrary number -of moves. If you look at in3, we see that we -used moveKnight three times and each time we used ->>= to feed it all the possible previous -positions. So now, let's make it more general. Here's how to do it: -

- -
-import Data.List
-
-inMany :: Int -> KnightPos -> [KnightPos]
-inMany x start = return start >>= foldr (<=<) return (replicate x moveKnight)
-
- -

-First we use replicate to make a list that contains -x copies of the function moveKnight. Then, -we monadically compose all those functions into one, which gives us a function -that takes a starting position and non-deterministically moves the knight -x times. Then, we just make the starting position -into a singleton list with return and feed it to the -function. -

- -

-Now, we can change our canReachIn3 function to be -more general as well: -

- -
-canReachIn :: Int -> KnightPos -> KnightPos -> Bool
-canReachIn x start end = end `elem` inMany x start
-
- - -

Making monads

-kewl - -

-In this section, we're going to look at an example of how a type gets made, -identified as a monad and then given the appropriate -Monad instance. We don't usually set out to make -a monad with the sole purpose of making a monad. Instead, we usually make a type -that whose purpose is to model an aspect of some problem and then later on if we -see that the type represents a value with a context and can act like a monad, we -give it a Monad instance. -

- -

-As we've seen, lists are used to represent non-deterministic values. A list like -[3,5,9] can be viewed as a single non-deterministic -value that just can't decide what it's going to be. When we feed a list into -a function with >>=, it just makes all the -possible choices of taking an element from the list and applying the function to it -and then presents those results in a list as well. -

- -

-If we look at the list [3,5,9] as the numbers -3, 5 and -9 occurring at once, we might notice that there's no -info regarding the probability that each of those numbers occurs. What if we -wanted to model a non-deterministic value like [3,5,9], -but we wanted to express that 3 has a 50% chance of -happening and 5 and 9 both -have a 25% chance of happening? Let's try and make this happen! -

- -

-Let's say that every item in the list comes with another value, a probability -of it happening. It might make sense to present this like this then: -

- -
-[(3,0.5),(5,0.25),(9,0.25)]
-
- -

-In mathematics, probabilities aren't usually expressed in percentages, but -rather in real numbers between a 0 and 1. A 0 means that there's no chance in -hell for something to happen and a 1 means that it's happening for sure. Floating -point numbers can get real messy real fast because they tend to lose -precision, so Haskell offers us a data type for rational numbers that doesn't lose -precision. That type is called Rational and it lives -in Data.Ratio. To make a Rational, -we write it as if it were a fraction. The numerator and the denominator are -separated by a %. Here are a few examples: -

- - -
-ghci> 1%4
-1 % 4
-ghci> 1%2 + 1%2
-1 % 1
-ghci> 1%3 + 5%4
-19 % 12
-
- -

-The first line is just one quarter. In the second line we add two halves to get -a whole and in the third line we add one third with five quarters and get -nineteen twelfths. So let's throw out our floating points and use -Rational for our probabilities: -

- -
-ghci> [(3,1%2),(5,1%4),(9,1%4)]
-[(3,1 % 2),(5,1 % 4),(9,1 % 4)]
-
- -

-Okay, so 3 has a one out of two chance of happening while -5 and 9 will happen one -time out of four. Pretty neat. -

- -

-We took lists and we added some extra context to them, so this represents values -with contexts too. Before we go any further, let's wrap this into a -newtype because something tells me we'll be making -some instances. -

- -
-import Data.Ratio
-
-newtype Prob a = Prob { getProb :: [(a,Rational)] } deriving Show
-
- -

-Alright. Is this a functor? Well, the list is a functor, so this should probably -be a functor as well, because we just added some stuff to the list. When we map -a function over a list, we apply it to each element. Here, we'll apply it to -each element as well, only we'll leave the probabilities as they are. Let's make -an instance: -

- -
-instance Functor Prob where
-    fmap f (Prob xs) = Prob $ map (\(x,p) -> (f x,p)) xs
-
- -

-We unwrap it from the newtype with pattern matching, -apply the function f to the values while keeping the -probabilities as they are and then wrap it back up. Let's see if it works: -

- -
-ghci> fmap negate (Prob [(3,1%2),(5,1%4),(9,1%4)])
-Prob {getProb = [(-3,1 % 2),(-5,1 % 4),(-9,1 % 4)]}
-
- -

-Another thing to note is that the probabilities should always add up to -1. If those are all the things that can happen, it -doesn't make sense for the sum of their probabilities to be anything other than -1. A coin that lands tails 75% of the time and -heads 50% of the time seems like it could only work in some other strange -universe. -

- -

-Now the big question, is this a monad? Given how the list is a monad, this -looks like it should be a monad as well. First, let's think about -return. How does it work for lists? It takes a value -and puts it in a singleton list. What about here? Well, since it's supposed to -be a default minimal context, it should also make a singleton list. What about -the probability? Well, return x is supposed to make a -monadic value that always presents x as its result, -so it doesn't make sense for the probability to be 0. -If it always has to present it as its result, the probability should be -1! -

- -

-What about >>=? Seems kind of tricky, so let's -make use of the fact that m >>= f always equals -join (fmap f m) for monads and think about how we -would flatten a probability list of probability lists. As an example, let's -consider this list where there's a 25% chance that exactly one of -'a' or 'b' will happen. Both -'a' and 'b' are equally -likely to occur. Also, there's a 75% chance that -exactly one of 'c' or 'd' -will happen. 'c' and 'd' -are also equally likely to happen. Here's a picture of a probability list that -models this scenario: -

- -probs - -

-What are the chances for each of these letters to occur? If we were to draw this -as just four boxes, each with a probability, what would those probabilities be? -To find out, all we have to do is multiply each probability with all of -probabilities that it contains. 'a' would occur one -time out of eight, as would 'b', because if we -multiply one half by one quarter we get one eighth. 'c' would happen three times out of eight because -three quarters multiplied by one half is three eighths. 'd' would also happen three times out of eight. If we sum -all the probabilities, they still add up to one. -

- -

-Here's this situation expressed as a probability list: -

- -
-thisSituation :: Prob (Prob Char)
-thisSituation = Prob
-    [( Prob [('a',1%2),('b',1%2)] , 1%4 )
-    ,( Prob [('c',1%2),('d',1%2)] , 3%4)
-    ]
-
- -

-Notice that its type is Prob (Prob Char). So now that -we've figure out how to flatten a nested probability list, all we have to do is -write the code for this and then we can write >>= simply as -join (fmap f m) and we have ourselves a monad! So -here's flatten, which we'll use because the name join -is already taken: -

- -
-flatten :: Prob (Prob a) -> Prob a
-flatten (Prob xs) = Prob $ concat $ map multAll xs
-    where multAll (Prob innerxs,p) = map (\(x,r) -> (x,p*r)) innerxs
-
- -

-The function multAll takes a tuple of probability -list and a probability p that comes with it and then -multiplies every inner probability with p, returning -a list of pairs of items and probabilities. We map multAll over -each pair in our nested probability list and then we just flatten the resulting -nested list. -

- -

-Now we have all that we need, we can write a Monad -instance! -

- -
-instance Monad Prob where
-    return x = Prob [(x,1%1)]
-    m >>= f = flatten (fmap f m)
-    fail _ = Prob []
-
- -ride em cowboy - -

-Because we already did all the hard work, the instance is very simple. We also -defined the fail function, which is the same as it is -for lists, so if there's a pattern match failure in a do -expression, a failure occurs within the context of a probability list. -

- -

-It's also important to check if the monad laws hold for the monad that we just -made. The first one says that return x >>= f -should be equal to f x. A rigorous proof would be -rather tedious, but we can see that if we put a value in a default context -with return and then fmap -a function over that and flatten the resulting probability list, every -probability that results from the function would be multiplied by the 1%1 probability that we made with return, so it wouldn't affect the context. The reasoning -for m >>= return being equal to just -m is similar. The third law states that -f <=< (g <=< h) should be the same as -(f <=< g) <=< h. This one holds as well, -because it holds for the list monad which forms the basis of the probability -monad and because multiplication is associative. 1%2 * (1%3 -* 1%5) is equal to (1%2 * 1%3) * 1%5. -

- -

-Now that we have a monad, what can we do with it? Well, it can help us do -calculations with probabilities. We can treat probabilistic events as values -with contexts and the probability monad will make sure that those probabilities -get reflected in the probabilities of the final result. -

- -

-Say we have two normal coins and one loaded coin that gets tails an astounding -nine times out of ten and heads only one time out of ten. If we throw all the -coins at once, what are the odds of all of them landing tails? First, let's make -probability values for a normal coin flip and for a loaded one: -

- -
-data Coin = Heads | Tails deriving (Show, Eq)
-
-coin :: Prob Coin
-coin = Prob [(Heads,1%2),(Tails,1%2)]
-
-loadedCoin :: Prob Coin
-loadedCoin = Prob [(Heads,1%10),(Tails,9%10)]
-
- -

-And finally, the coin throwing action: -

- -
-import Data.List (all)
-
-flipThree :: Prob Bool
-flipThree = do
-    a <- coin
-    b <- coin
-    c <- loadedCoin
-    return (all (==Tails) [a,b,c])
-
- -

-Giving it a go, we see that the odds of all three landing tails are not that -good, despite cheating with our loaded coin: -

- -
-ghci> getProb flipThree
-[(False,1 % 40),(False,9 % 40),(False,1 % 40),(False,9 % 40),
- (False,1 % 40),(False,9 % 40),(False,1 % 40),(True,9 % 40)]
-
- -

-All three of them will land tails nine times out of forty, which is less than -25%. We see that our monad doesn't know how to join all of the -False outcomes where all coins don't land tails into -one outcome. That's not a big problem, since writing a function to put all the -same outcomes into one outcome is pretty easy and is left as an exercise to the -reader (you!) -

- -

-In this section, we went from having a question (what if lists also carried -information about probability?) to making a type, recognizing a monad and -finally making an instance and doing something with it. I think that's quite -fetching! By now, we should have a pretty good grasp on monads and what they're -about. -

- -
- - - - -
- - - - diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html deleted file mode 100644 index bacd081..0000000 --- a/docs/functionally-solving-problems.html +++ /dev/null @@ -1,325 +0,0 @@ - - - -Functionally Solving Problems - Learn You a Haskell for Great Good! - - - - - - - - - - - -
-
- -

Functionally Solving Problems

-

In this chapter, we'll take a look at a few interesting problems and how to think functionally to solve them as elegantly as possible. We probably won't be introducing any new concepts, we'll just be flexing our newly acquired Haskell muscles and practicing our coding skills. Each section will present a different problem. First we'll describe the problem, then we'll try and find out what the best (or least bad) way of solving it is.

-

Reverse Polish notation calculator

-

Usually when we write mathematical expressions in school, we write them in an infix manner. For instance, we write 10 - (4 + 3) * 2. +, * and - are infix operators, just like the infix functions we met in Haskell (+, `elem`, etc.). This makes it handy because we, as humans, can parse it easily in our minds by looking at such an expression. The downside to it is that we have to use parentheses to denote precedence.

-

Reverse Polish notation is another way of writing down mathematical expressions. Initially it looks a bit weird, but it's actually pretty easy to understand and use because there's no need for parentheses and it's very easy to punch into a calculator. While most modern calculators use infix notation, some people still swear by RPN calculators. This is what the previous infix expression looks like in RPN: 10 4 3 + 2 * -. How do we calculate what the result of that is? Well, think of a stack. You go over the expression from left to right. Every time a number is encountered, push it onto the stack. When we encounter an operator, take the two numbers that are on top of the stack (we also say that we pop them), use the operator and those two and then push the resulting number back onto the stack. When you reach the end of the expression, you should be left with a single number if the expression was well-formed and that number represents the result.

-this expression -

Let's go over the expression 10 4 3 + 2 * - together! First we push 10 onto the stack and the stack is now 10. The next item is 4, so we push it to the stack as well. The stack is now 10, 4. We do the same with 3 and the stack is now 10, 4, 3. And now, we encounter an operator, namely +! We pop the two top numbers from the stack (so now the stack is just 10), add those numbers together and push that result to the stack. The stack is now 10, 7. We push 2 to the stack, the stack for now is 10, 7, 2. We've encountered an operator again, so let's pop 7 and 2 off the stack, multiply them and push that result to the stack. Multiplying 7 and 2 produces a 14, so the stack we have now is 10, 14. Finally, there's a -. We pop 10 and 14 from the stack, subtract 14 from 10 and push that back. The number on the stack is now -4 and because there are no more numbers or operators in our expression, that's our result!

-

Now that we know how we'd calculate any RPN expression by hand, let's think about how we could make a Haskell function that takes as its parameter a string that contains an RPN expression, like "10 4 3 + 2 * -" and gives us back its result.

-

What would the type of that function be? We want it to take a string as a parameter and produce a number as its result. So it will probably be something like solveRPN :: (Num a) => String -> a.

-
Pro. tip: it really helps to first think what the type declaration of a function should be before concerning ourselves with the implementation and then write it down. In Haskell, a function's type declaration tells us a whole lot about the function, due to the very strong type system.
-HA HA HA -

Cool. When implementing a solution to a problem in Haskell, it's also good to think back on how you did it by hand and maybe try to see if you can gain any insight from that. Here we see that we treated every number or operator that was separated by a space as a single item. So it might help us if we start by breaking a string like "10 4 3 + 2 * -" into a list of items like ["10","4","3","+","2","*","-"].

-

Next up, what did we do with that list of items in our head? We went over it from left to right and kept a stack as we did that. Does the previous sentence remind you of anything? Remember, in the section about folds, we said that pretty much any function where you traverse a list from left to right or right to left one element by element and build up (accumulate) some result (whether it's a number, a list, a stack, whatever) can be implemented with a fold.

-

In this case, we're going to use a left fold, because we go over the list from left to right. The accumulator value will be our stack and hence, the result from the fold will also be a stack, only as we've seen, it will only have one item.

-

One more thing to think about is, well, how are we going to represent the stack? I propose we use a list. Also I propose that we keep the top of our stack at the head of the list. That's because adding to the head (beginning) of a list is much faster than adding to the end of it. So if we have a stack of, say, 10, 4, 3, we'll represent that as the list [3,4,10].

-

Now we have enough information to roughly sketch our function. It's going to take a string, like, "10 4 3 + 2 * -" and break it down into a list of items by using words to get ["10","4","3","+","2","*","-"]. Next, we'll do a left fold over that list and end up with a stack that has a single item, so [-4]. We take that single item out of the list and that's our final result!

-

So here's a sketch of that function:

-
-import Data.List
-
-solveRPN :: (Num a) => String -> a
-solveRPN expression = head (foldl foldingFunction [] (words expression))
-    where   foldingFunction stack item = ...
-
-

We take the expression and turn it into a list of items. Then we fold over that list of items with the folding function. Mind the [], which represents the starting accumulator. The accumulator is our stack, so [] represents an empty stack, which is what we start with. After getting the final stack with a single item, we call head on that list to get the item out and then we apply read.

-

So all that's left now is to implement a folding function that will take a stack, like [4,10], and an item, like "3" and return a new stack [3,4,10]. If the stack is [4,10] and the item "*", then it will have to return [40]. But before that, let's turn our function into point-free style because it has a lot of parentheses that are kind of freaking me out:

-
-import Data.List
-
-solveRPN :: (Num a) => String -> a
-solveRPN = head . foldl foldingFunction [] . words
-    where   foldingFunction stack item = ...
-
-

Ah, there we go. Much better. So, the folding function will take a stack and an item and return a new stack. We'll use pattern matching to get the top items of a stack and to pattern match against operators like "*" and "-".

-
-solveRPN :: (Num a, Read a) => String -> a
-solveRPN = head . foldl foldingFunction [] . words
-    where   foldingFunction (x:y:ys) "*" = (x * y):ys
-            foldingFunction (x:y:ys) "+" = (x + y):ys
-            foldingFunction (x:y:ys) "-" = (y - x):ys
-            foldingFunction xs numberString = read numberString:xs
-
-

We laid this out as four patterns. The patterns will be tried from top to bottom. First the folding function will see if the current item is "*". If it is, then it will take a list like [3,4,9,3] and call its first two elements x and y respectively. So in this case, x would be 3 and y would be 4. ys would be [9,3]. It will return a list that's just like ys, only it has x and y multiplied as its head. So with this we pop the two topmost numbers off the stack, multiply them and push the result back onto the stack. If the item is not "*", the pattern matching will fall through and "+" will be checked, and so on.

-

If the item is none of the operators, then we assume it's a string that represents a number. If it's a number, we just call read on that string to get a number from it and return the previous stack but with that number pushed to the top.

-

And that's it! Also noticed that we added an extra class constraint of Read a to the function declaration, because we call read on our string to get the number. So this declaration means that the result can be of any type that's part of the Num and Read typeclasses (like Int, Float, etc.).

-

For the list of items ["2","3","+"], our function will start folding from the left. The initial stack will be []. It will call the folding function with [] as the stack (accumulator) and "2" as the item. Because that item is not an operator, it will be read and the added to the beginning of []. So the new stack is now [2] and the folding function will be called with [2] as the stack and ["3"] as the item, producing a new stack of [3,2]. Then, it's called for the third time with [3,2] as the stack and "+" as the item. This causes these two numbers to be popped off the stack, added together and pushed back. The final stack is [5], which is the number that we return.

-

Let's play around with our function:

-
-ghci> solveRPN "10 4 3 + 2 * -"
--4
-ghci> solveRPN "2 3 +"
-5
-ghci> solveRPN "90 34 12 33 55 66 + * - +"
--3947
-ghci> solveRPN "90 34 12 33 55 66 + * - + -"
-4037
-ghci> solveRPN "90 34 12 33 55 66 + * - + -"
-4037
-ghci> solveRPN "90 3 -"
-87
-
-

Cool, it works! One nice thing about this function is that it can be easily modified to support various other operators. They don't even have to be binary operators. For instance, we can make an operator "log" that just pops one number off the stack and pushes back its logarithm. We can also make ternary operators that pop three numbers off the stack and push back a result or operators like "sum" which pop off all the numbers and push back their sum.

-

Let's modify our function to take a few more operators. For simplicity's sake, we'll change its type declaration so that it returns a number of type Float.

-
-import Data.List
-
-solveRPN :: String -> Float
-solveRPN = head . foldl foldingFunction [] . words
-    where   foldingFunction (x:y:ys) "*" = (x * y):ys
-            foldingFunction (x:y:ys) "+" = (x + y):ys
-            foldingFunction (x:y:ys) "-" = (y - x):ys
-            foldingFunction (x:y:ys) "/" = (y / x):ys
-            foldingFunction (x:y:ys) "^" = (y ** x):ys
-            foldingFunction (x:xs) "ln" = log x:xs
-            foldingFunction xs "sum" = [sum xs]
-            foldingFunction xs numberString = read numberString:xs
-
-

Wow, great! / is division of course and ** is floating point exponentiation. With the logarithm operator, we just pattern match against a single element and the rest of the stack because we only need one element to perform its natural logarithm. With the sum operator, we just return a stack that has only one element, which is the sum of the stack so far.

-
-ghci> solveRPN "2.7 ln"
-0.9932518
-ghci> solveRPN "10 10 10 10 sum 4 /"
-10.0
-ghci> solveRPN "10 10 10 10 10 sum 4 /"
-12.5
-ghci> solveRPN "10 2 ^"
-100.0
-
-

Notice that we can include floating point numbers in our expression because read knows how to read them.

-
-ghci> solveRPN "43.2425 0.5 ^"
-6.575903
-
-

I think that making a function that can calculate arbitrary floating point RPN expressions and has the option to be easily extended in 10 lines is pretty awesome.

-

One thing to note about this function is that it's not really fault-tolerant. When given input that doesn't make sense, it will just crash everything. We'll make a fault-tolerant version of this with a type declaration of solveRPN :: String -> Maybe Float once we get to know monads (they're not scary, trust me!). We could make one right now, but it would be a bit tedious because it would involve a lot of checking for Nothing on every step. If you're feeling up to the challenge though, you can go ahead and try it! Hint: you can use reads to see if a read was successful or not.

-

Heathrow to London

-

Our next problem is this: your plane has just landed in England and you rent a car. You have a meeting really soon and you have to get from Heathrow Airport to London as fast as you can (but safely!).

-

There are two main roads going from Heathrow to London and there's a number of regional roads crossing them. It takes you a fixed amount of time to travel from one crossroads to another. It's up to you to find the optimal path to take so that you get to London as fast as you can! You start on the left side and can either cross to the other main road or go forward.

-Heathrow - London -

As you can see in the picture, the shortest path from Heathrow to London in this case is to start on main road B, cross over, go forward on A, cross over again and then go forward twice on B. If we take this path, it takes us 75 minutes. Had we chosen any other path, it would take more than that.

-

Our job is to make a program that takes input that represents a road system and print out what the shortest path across it is. Here's what the input would look like for this case:

-
-50
-10
-30
-5
-90
-20
-40
-2
-25
-10
-8
-0
-
-

To mentally parse the input file, read it in threes and mentally split the road system into sections. Each section is composed of a road A, road B and a crossing road. To have it neatly fit into threes, we say that there's a last crossing section that takes 0 minutes to drive over. That's because we don't care where we arrive in London, as long as we're in London.

-

Just like we did when solving the RPN calculator problem, we're going to solve this problem in three steps:

- -

In the RPN calculator section, we first figured out that when calculating an expression by hand, we'd keep a sort of stack in our minds and then go over the expression one item at a time. We decided to use a list of strings to represent our expression. Finally, we used a left fold to walk over the list of strings while keeping a stack to produce a solution.

-

Okay, so how would we figure out the shortest path from Heathrow to London by hand? Well, we can just sort of look at the whole picture and try to guess what the shortest path is and hopefully we'll make a guess that's right. That solution works for very small inputs, but what if we have a road that has 10,000 sections? Yikes! We also won't be able to say for certain that our solution is the optimal one, we can just sort of say that we're pretty sure.

-

That's not a good solution then. Here's a simplified picture of our road system:

-roads -

Alright, can you figure out what the shortest path to the first crossroads (the first blue dot on A, marked A1) on road A is? That's pretty trivial. We just see if it's shorter to go directly forward on A or if it's shorter to go forward on B and then cross over. Obviously, it's cheaper to go forward via B and then cross over because that takes 40 minutes, whereas going directly via A takes 50 minutes. What about crossroads B1? Same thing. We see that it's a lot cheaper to just go directly via B (incurring a cost of 10 minutes), because going via A and then crossing over would take us a whole 80 minutes!

-

Now we know what the cheapest path to A1 is (go via B and then cross over, so we'll say that's B, C with a cost of 40) and we know what the cheapest path to B1 is (go directly via B, so that's just B, going at 10). Does this knowledge help us at all if we want to know the cheapest path to the next crossroads on both main roads? Gee golly, it sure does!

-

Let's see what the shortest path to A2 would be. To get to A2, we'll either go directly to A2 from A1 or we'll go forward from B1 and then cross over (remember, we can only move forward or cross to the other side). And because we know the cost to A1 and B1, we can easily figure out what the best path to A2 is. It costs 40 to get to A1 and then 5 to get from A1 to A2, so that's B, C, A for a cost of 45. It costs only 10 to get to B1, but then it would take an additional 110 minutes to go to B2 and then cross over! So obviously, the cheapest path to A2 is B, C, A. In the same way, the cheapest way to B2 is to go forward from A1 and then cross over.

-
Maybe you're asking yourself: but what about getting to A2 by first crossing over at B1 and then going on forward? Well, we already covered crossing from B1 to A1 when we were looking for the best way to A1, so we don't have to take that into account in the next step as well.
-

Now that we have the best path to A2 and B2, we can repeat this indefinitely until we reach the end. Once we've gotten the best paths for A4 and B4, the one that's cheaper is the optimal path!

-

So in essence, for the second section, we just repeat the step we did at first, only we take into account what the previous best paths on A and B. We could say that we also took into account the best paths on A and on B in the first step, only they were both empty paths with a cost of 0.

-

Here's a summary. To get the best path from Heathrow to London, we do this: first we see what the best path to the next crossroads on main road A is. The two options are going directly forward or starting at the opposite road, going forward and then crossing over. We remember the cost and the path. We use the same method to see what the best path to the next crossroads on main road B is and remember that. Then, we see if the path to the next crossroads on A is cheaper if we go from the previous A crossroads or if we go from the previous B crossroads and then cross over. We remember the cheaper path and then we do the same for the crossroads opposite of it. We do this for every section until we reach the end. Once we've reached the end, the cheapest of the two paths that we have is our optimal path!

-

So in essence, we keep one shortest path on the A road and one shortest path on the B road and when we reach the end, the shorter of those two is our path. We now know how to figure out the shortest path by hand. If you had enough time, paper and pencils, you could figure out the shortest path through a road system with any number of sections.

-

Next step! How do we represent this road system with Haskell's data types? One way is to think of the starting points and crossroads as nodes of a graph that point to other crossroads. If we imagine that the starting points actually point to each other with a road that has a length of one, we see that every crossroads (or node) points to the node on the other side and also to the next one on its side. Except for the last nodes, they just point to the other side.

-
-data Node = Node Road Road | EndNode Road
-data Road = Road Int Node
-
-

A node is either a normal node and has information about the road that leads to the other main road and the road that leads to the next node or an end node, which only has information about the road to the other main road. A road keeps information about how long it is and which node it points to. For instance, the first part of the road on the A main road would be Road 50 a1 where a1 would be a node Node x y, where x and y are roads that point to B1 and A2.

-

Another way would be to use Maybe for the road parts that point forward. Each node has a road part that point to the opposite road, but only those nodes that aren't the end ones have road parts that point forward.

-
-data Node = Node Road (Maybe Road)
-data Road = Road Int Node
-
-

This is an alright way to represent the road system in Haskell and we could certainly solve this problem with it, but maybe we could come up with something simpler? If we think back to our solution by hand, we always just checked the lengths of three road parts at once: the road part on the A road, its opposite part on the B road and part C, which touches those two parts and connects them. When we were looking for the shortest path to A1 and B1, we only had to deal with the lengths of the first three parts, which have lengths of 50, 10 and 30. We'll call that one section. So the road system that we use for this example can be easily represented as four sections: 50, 10, 30, 5, 90, 20, 40, 2, 25, and 10, 8, 0.

-

It's always good to keep our data types as simple as possible, although not any simpler!

-
-data Section = Section { getA :: Int, getB :: Int, getC :: Int } deriving (Show)
-type RoadSystem = [Section]
-
-

This is pretty much perfect! It's as simple as it goes and I have a feeling it'll work perfectly for implementing our solution. Section is a simple algebraic data type that holds three integers for the lengths of its three road parts. We introduce a type synonym as well, saying that RoadSystem is a list of sections. -

We could also use a triple of (Int, Int, Int) to represent a road section. Using tuples instead of making your own algebraic data types is good for some small localized stuff, but it's usually better to make a new type for things like this. It gives the type system more information about what's what. We can use (Int, Int, Int) to represent a road section or a vector in 3D space and we can operate on those two, but that allows us to mix them up. If we use Section and Vector data types, then we can't accidentally add a vector to a section of a road system.
-

Our road system from Heathrow to London can now be represented like this:

-
-heathrowToLondon :: RoadSystem
-heathrowToLondon = [Section 50 10 30, Section 5 90 20, Section 40 2 25, Section 10 8 0]
-
-

All we need to do now is to implement the solution that we came up with previously in Haskell. What should the type declaration for a function that calculates a shortest path for any given road system be? It should take a road system as a parameter and return a path. We'll represent a path as a list as well. Let's introduce a Label type that's just an enumeration of either A, B or C. We'll also make a type synonym: Path.

-
-data Label = A | B | C deriving (Show)
-type Path = [(Label, Int)]
-
-

Our function, we'll call it optimalPath should thus have a type declaration of optimalPath :: RoadSystem -> Path. If called with the road system heathrowToLondon, it should return the following path:

-
-[(B,10),(C,30),(A,5),(C,20),(B,2),(B,8)]
-
-

We're going to have to walk over the list with the sections from left to right and keep the optimal path on A and optimal path on B as we go along. We'll accumulate the best path as we walk over the list, left to right. What does that sound like? Ding, ding, ding! That's right, A LEFT FOLD!

-

When doing the solution by hand, there was a step that we repeated over and over again. It involved checking the optimal paths on A and B so far and the current section to produce the new optimal paths on A and B. For instance, at the beginning the optimal paths were [] and [] for A and B respectively. We examined the section Section 50 10 30 and concluded that the new optimal path to A1 is [(B,10),(C,30)] and the optimal path to B1 is [(B,10)]. If you look at this step as a function, it takes a pair of paths and a section and produces a new pair of paths. The type is (Path, Path) -> Section -> (Path, Path). Let's go ahead and implement this function, because it's bound to be useful.

-
Hint: it will be useful because (Path, Path) -> Section -> (Path, Path) can be used as the binary function for a left fold, which has to have a type of a -> b -> a
-
-roadStep :: (Path, Path) -> Section -> (Path, Path)
-roadStep (pathA, pathB) (Section a b c) =
-    let priceA = sum $ map snd pathA
-        priceB = sum $ map snd pathB
-        forwardPriceToA = priceA + a
-        crossPriceToA = priceB + b + c
-        forwardPriceToB = priceB + b
-        crossPriceToB = priceA + a + c
-        newPathToA = if forwardPriceToA <= crossPriceToA
-                        then (A,a):pathA
-                        else (C,c):(B,b):pathB
-        newPathToB = if forwardPriceToB <= crossPriceToB
-                        then (B,b):pathB
-                        else (C,c):(A,a):pathA
-    in  (newPathToA, newPathToB)
-
-this is you -

What's going on here? First, calculate the optimal price on road A based on the best so far on A and we do the same for B. We do sum $ map snd pathA, so if pathA is something like [(A,100),(C,20)], priceA becomes 120. forwardPriceToA is the price that we would pay if we went to the next crossroads on A if we went there directly from the previous crossroads on A. It equals the best price to our previous A, plus the length of the A part of the current section. crossPriceToA is the price that we would pay if we went to the next A by going forward from the previous B and then crossing over. It's the best price to the previous B so far plus the B length of the section plus the C length of the section. We determine forwardPriceToB and crossPriceToB in the same manner.

-

Now that we know what the best way to A and B is, we just need to make the new paths to A and B based on that. If it's cheaper to go to A by just going forwards, we set newPathToA to be (A,a):pathA. Basically we prepend the Label A and the section length a to the optimal path path on A so far. Basically, we say that the best path to the next A crossroads is the path to the previous A crossroads and then one section forward via A. Remember, A is just a label, whereas a has a type of Int. Why do we prepend instead of doing pathA ++ [(A,a)]? Well, adding an element to the beginning of a list (also known as consing) is much faster than adding it to the end. This means that the path will be the wrong way around once we fold over a list with this function, but it's easy to reverse the list later. If it's cheaper to get to the next A crossroads by going forward from road B and then crossing over, then newPathToA is the old path to B that then goes forward and crosses to A. We do the same thing for newPathToB, only everything's mirrored.

-

Finally, we return newPathToA and newPathToB in a pair.

-

Let's run this function on the first section of heathrowToLondon. Because it's the first section, the best paths on A and B parameter will be a pair of empty lists.

-
-ghci> roadStep ([], []) (head heathrowToLondon)
-([(C,30),(B,10)],[(B,10)])
-
-

Remember, the paths are reversed, so read them from right to left. From this we can read that the best path to the next A is to start on B and then cross over to A and that the best path to the next B is to just go directly forward from the starting point at B.

-
Optimization tip: when we do priceA = sum $ map snd pathA, we're calculating the price from the path on every step. We wouldn't have to do that if we implemented roadStep as a (Path, Path, Int, Int) -> Section -> (Path, Path, Int, Int) function where the integers represent the best price on A and B.
-

Now that we have a function that takes a pair of paths and a section and produces a new optimal path, we can just easily do a left fold over a list of sections. roadStep is called with ([],[]) and the first section and returns a pair of optimal paths to that section. Then, it's called with that pair of paths and the next section and so on. When we've walked over all the sections, we're left with a pair of optimal paths and the shorter of them is our answer. With this in mind, we can implement optimalPath.

-
-optimalPath :: RoadSystem -> Path
-optimalPath roadSystem =
-    let (bestAPath, bestBPath) = foldl roadStep ([],[]) roadSystem
-    in  if sum (map snd bestAPath) <= sum (map snd bestBPath)
-            then reverse bestAPath
-            else reverse bestBPath
-
-

We left fold over roadSystem (remember, it's a list of sections) with the starting accumulator being a pair of empty paths. The result of that fold is a pair of paths, so we pattern match on the pair to get the paths themselves. Then, we check which one of these was cheaper and return it. Before returning it, we also reverse it, because the optimal paths so far were reversed due to us choosing consing over appending.

-

Let's test this!

-
-ghci> optimalPath heathrowToLondon
-[(B,10),(C,30),(A,5),(C,20),(B,2),(B,8),(C,0)]
-
-

This is the result that we were supposed to get! Awesome! It differs from our expected result a bit because there's a step (C,0) at the end, which means that we cross over to the other road once we're in London, but because that crossing doesn't cost anything, this is still the correct result.

-

We have the function that finds an optimal path based on, now we just have to read a textual representation of a road system from the standard input, convert it into a type of RoadSystem, run that through our optimalPath function and print the path.

-

First off, let's make a function that takes a list and splits it into groups of the same size. We'll call it groupsOf. For a parameter of [1..10], groupsOf 3 should return [[1,2,3],[4,5,6],[7,8,9],[10]].

-
-groupsOf :: Int -> [a] -> [[a]]
-groupsOf 0 _ = undefined
-groupsOf _ [] = []
-groupsOf n xs = take n xs : groupsOf n (drop n xs)
-
-

A standard recursive function. For an xs of [1..10] and an n of 3, this equals [1,2,3] : groupsOf 3 [4,5,6,7,8,9,10]. When the recursion is done, we get our list in groups of three. And here's our main function, which reads from the standard input, makes a RoadSystem out of it and prints out the shortest path:

-
-import Data.List
-
-main = do
-    contents <- getContents
-    let threes = groupsOf 3 (map read $ lines contents)
-        roadSystem = map (\[a,b,c] -> Section a b c) threes
-        path = optimalPath roadSystem
-        pathString = concat $ map (show . fst) path
-        pathPrice = sum $ map snd path
-    putStrLn $ "The best path to take is: " ++ pathString
-    putStrLn $ "The price is: " ++ show pathPrice
-
-

First, we get all the contents from the standard input. Then, we call lines with our contents to convert something like "50\n10\n30\n... to ["50","10","30".. and then we map read to that to convert it to a list of numbers. We call groupsOf 3 on it so that we turn it to a list of lists of length 3. We map the lambda (\[a,b,c] -> Section a b c) over that list of lists. As you can see, the lambda just takes a list of length 3 and turns it into a section. So roadSystem is now our system of roads and it even has the correct type, namely RoadSystem (or [Section]). We call optimalPath with that and then get the path and the price in a nice textual representation and print it out.

-

We save the following text

-
-50
-10
-30
-5
-90
-20
-40
-2
-25
-10
-8
-0
-
-

in a file called paths.txt and then feed it to our program.

-
-$ cat paths.txt | runhaskell heathrow.hs
-The best path to take is: BCACBBC
-The price is: 75
-
-

Works like a charm! You can use your knowledge of the Data.Random module to generate a much longer system of roads, which you can then feed to what we just wrote. If you get stack overflows, try using foldl' instead of foldl, because foldl' is strict.

- -
- - - - -
- - - - diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html deleted file mode 100644 index 3eb5997..0000000 --- a/docs/functors-applicative-functors-and-monoids.html +++ /dev/null @@ -1,2164 +0,0 @@ - - - -Functors, Applicative Functors and Monoids - Learn You a Haskell for Great Good! - - - - - - - - - - - -
-
- -

Functors, Applicative Functors and Monoids

-

Haskell's combination of purity, higher order functions, parameterized algebraic data types, and typeclasses allows us to implement polymorphism on a much higher level than possible in other languages. We don't have to think about types belonging to a big hierarchy of types. Instead, we think about what the types can act like and then connect them with the appropriate typeclasses. An Int can act like a lot of things. It can act like an equatable thing, like an ordered thing, like an enumerable thing, etc.

-

Typeclasses are open, which means that we can define our own data type, think about what it can act like and connect it with the typeclasses that define its behaviors. Because of that and because of Haskell's great type system that allows us to know a lot about a function just by knowing its type declaration, we can define typeclasses that define behavior that's very general and abstract. We've met typeclasses that define operations for seeing if two things are equal or comparing two things by some ordering. Those are very abstract and elegant behaviors, but we just don't think of them as anything very special because we've been dealing with them for most of our lives. We recently met functors, which are basically things that can be mapped over. That's an example of a useful and yet still pretty abstract property that typeclasses can describe. In this chapter, we'll take a closer look at functors, along with slightly stronger and more useful versions of functors called applicative functors. We'll also take a look at monoids, which are sort of like socks.

-

Functors redux

-frogs dont even need money -

We've already talked about functors in their own little section. If you haven't read it yet, you should probably give it a glance right now, or maybe later when you have more time. Or you can just pretend you read it.

-

Still, here's a quick refresher: Functors are things that can be mapped over, like lists, Maybes, trees, and such. In Haskell, they're described by the typeclass Functor, which has only one typeclass method, namely fmap, which has a type of fmap :: (a -> b) -> f a -> f b. It says: give me a function that takes an a and returns a b and a box with an a (or several of them) inside it and I'll give you a box with a b (or several of them) inside it. It kind of applies the function to the element inside the box.

-
A word of advice. Many times the box analogy is used to help you get some intuition for how functors work, and later, we'll probably use the same analogy for applicative functors and monads. It's an okay analogy that helps people understand functors at first, just don't take it too literally, because for some functors the box analogy has to be stretched really thin to still hold some truth. A more correct term for what a functor is would be computational context. The context might be that the computation can have a value or it might have failed (Maybe and Either a) or that there might be more values (lists), stuff like that.
-

If we want to make a type constructor an instance of Functor, it has to have a kind of * -> *, which means that it has to take exactly one concrete type as a type parameter. For example, Maybe can be made an instance because it takes one type parameter to produce a concrete type, like Maybe Int or Maybe String. If a type constructor takes two parameters, like Either, we have to partially apply the type constructor until it only takes one type parameter. So we can't write instance Functor Either where, but we can write instance Functor (Either a) where and then if we imagine that fmap is only for Either a, it would have a type declaration of fmap :: (b -> c) -> Either a b -> Either a c. As you can see, the Either a part is fixed, because Either a takes only one type parameter, whereas just Either takes two so fmap :: (b -> c) -> Either b -> Either c wouldn't really make sense.

-

We've learned by now how a lot of types (well, type constructors really) are instances of Functor, like [], Maybe, Either a and a Tree type that we made on our own. We saw how we can map functions over them for great good. In this section, we'll take a look at two more instances of functor, namely IO and (->) r.

-

If some value has a type of, say, IO String, that means that it's an I/O action that, when performed, will go out into the real world and get some string for us, which it will yield as a result. We can use <- in do syntax to bind that result to a name. We mentioned that I/O actions are like boxes with little feet that go out and fetch some value from the outside world for us. We can inspect what they fetched, but after inspecting, we have to wrap the value back in IO. By thinking about this box with little feet analogy, we can see how IO acts like a functor.

-

-Let's see how IO is an instance of Functor. When we fmap a function over an I/O action, we want to get back an I/O action that does the same thing, but has our function applied over its result value.

-
-instance Functor IO where
-    fmap f action = do
-        result <- action
-        return (f result)
-
-

-The result of mapping something over an I/O action will be an I/O action, so right off the bat we use do syntax to glue two actions and make a new one. In the implementation for fmap, we make a new I/O action that first performs the original I/O action and calls its result result. Then, we do return (f result). return is, as you know, a function that makes an I/O action that doesn't do anything but only presents something as its result. The action that a do block produces will always have the result value of its last action. That's why we use return to make an I/O action that doesn't really do anything, it just presents f result as the result of the new I/O action. -

We can play around with it to gain some intuition. It's pretty simple really. Check out this piece of code:

-
-main = do line <- getLine
-          let line' = reverse line
-          putStrLn $ "You said " ++ line' ++ " backwards!"
-          putStrLn $ "Yes, you really said" ++ line' ++ " backwards!"
-
-

The user is prompted for a line and we give it back to the user, only reversed. Here's how to rewrite this by using fmap:

-
-main = do line <- fmap reverse getLine
-          putStrLn $ "You said " ++ line ++ " backwards!"
-          putStrLn $ "Yes, you really said" ++ line ++ " backwards!"
-
-w00ooOoooOO -

Just like when we fmap reverse over Just "blah" to get Just "halb", we can fmap reverse over getLine. getLine is an I/O action that has a type of IO String and mapping reverse over it gives us an I/O action that will go out into the real world and get a line and then apply reverse to its result. Like we can apply a function to something that's inside a Maybe box, we can apply a function to what's inside an IO box, only it has to go out into the real world to get something. Then when we bind it to a name by using <-, the name will reflect the result that already has reverse applied to it.

-

The I/O action fmap (++"!") getLine behaves just like getLine, only that its result always has "!" appended to it!

-

If we look at what fmap's type would be if it were limited to IO, it would be fmap :: (a -> b) -> IO a -> IO b. fmap takes a function and an I/O action and returns a new I/O action that's like the old one, except that the function is applied to its contained result.

-

If you ever find yourself binding the result of an I/O action to a name, only to apply a function to that and call that something else, consider using fmap, because it looks prettier. If you want to apply multiple transformations to some data inside a functor, you can declare your own function at the top level, make a lambda function or ideally, use function composition:

-
-import Data.Char
-import Data.List
-
-main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine
-          putStrLn line
-
-
-$ runhaskell fmapping_io.hs
-hello there
-E-R-E-H-T- -O-L-L-E-H
-
-

As you probably know, intersperse '-' . reverse . map toUpper is a function that takes a string, maps toUpper over it, the applies reverse to that result and then applies intersperse '-' to that result. It's like writing (\xs -> intersperse '-' (reverse (map toUpper xs))), only prettier.

-

Another instance of Functor that we've been dealing with all along but didn't know was a Functor is (->) r. You're probably slightly confused now, since what the heck does (->) r mean? The function type r -> a can be rewritten as (->) r a, much like we can write 2 + 3 as (+) 2 3. When we look at it as (->) r a, we can see (->) in a slightly different light, because we see that it's just a type constructor that takes two type parameters, just like Either. But remember, we said that a type constructor has to take exactly one type parameter so that it can be made an instance of Functor. That's why we can't make (->) an instance of Functor, but if we partially apply it to (->) r, it doesn't pose any problems. If the syntax allowed for type constructors to be partially applied with sections (like we can partially apply + by doing (2+), which is the same as (+) 2), you could write (->) r as (r ->). How are functions functors? Well, let's take a look at the implementation, which lies in Control.Monad.Instances

-
We usually mark functions that take anything and return anything as a -> b. r -> a is the same thing, we just used different letters for the type variables.
-
-instance Functor ((->) r) where
-    fmap f g = (\x -> f (g x))
-
-

If the syntax allowed for it, it could have been written as

-
-instance Functor (r ->) where
-    fmap f g = (\x -> f (g x))
-
-

But it doesn't, so we have to write it in the former fashion.

-

First of all, let's think about fmap's type. It's fmap :: (a -> b) -> f a -> f b. Now what we'll do is mentally replace all the f's, which are the role that our functor instance plays, with (->) r's. We'll do that to see how fmap should behave for this particular instance. We get fmap :: (a -> b) -> ((->) r a) -> ((->) r b). Now what we can do is write the (->) r a and (-> r b) types as infix r -> a and r -> b, like we normally do with functions. What we get now is fmap :: (a -> b) -> (r -> a) -> (r -> b).

-

Hmmm OK. Mapping one function over a function has to produce a function, just like mapping a function over a Maybe has to produce a Maybe and mapping a function over a list has to produce a list. What does the type fmap :: (a -> b) -> (r -> a) -> (r -> b) for this instance tell us? Well, we see that it takes a function from a to b and a function from r to a and returns a function from r to b. Does this remind you of anything? Yes! Function composition! We pipe the output of r -> a into the input of a -> b to get a function r -> b, which is exactly what function composition is about. If you look at how the instance is defined above, you'll see that it's just function composition. Another way to write this instance would be:

-
-instance Functor ((->) r) where
-    fmap = (.)
-
-

This makes the revelation that using fmap over functions is just composition sort of obvious. Do :m + Control.Monad.Instances, since that's where the instance is defined and then try playing with mapping over functions.

-
-ghci> :t fmap (*3) (+100)
-fmap (*3) (+100) :: (Num a) => a -> a
-ghci> fmap (*3) (+100) 1
-303
-ghci> (*3) `fmap` (+100) $ 1
-303
-ghci> (*3) . (+100) $ 1
-303
-ghci> fmap (show . (*3)) (*100) 1
-"300"
-
-

We can call fmap as an infix function so that the resemblance to . is clear. In the second input line, we're mapping (*3) over (+100), which results in a function that will take an input, call (+100) on that and then call (*3) on that result. We call that function with 1.

-

How does the box analogy hold here? Well, if you stretch it, it holds. When we use fmap (+3) over Just 3, it's easy to imagine the Maybe as a box that has some contents on which we apply the function (+3). But what about when we're doing fmap (*3) (+100)? Well, you can think of the function (+100) as a box that contains its eventual result. Sort of like how an I/O action can be thought of as a box that will go out into the real world and fetch some result. Using fmap (*3) on (+100) will create another function that acts like (+100), only before producing a result, (*3) will be applied to that result. Now we can see how fmap acts just like . for functions.

-

The fact that fmap is function composition when used on functions isn't so terribly useful right now, but at least it's very interesting. It also bends our minds a bit and let us see how things that act more like computations than boxes (IO and (->) r) can be functors. The function being mapped over a computation results in the same computation but the result of that computation is modified with the function.

-lifting a function is easier than lifting a million pounds -

Before we go on to the rules that fmap should follow, let's think about the type of fmap once more. Its type is fmap :: (a -> b) -> f a -> f b. We're missing the class constraint (Functor f) =>, but we left it out here for brevity, because we're talking about functors anyway so we know what the f stands for. When we first learned about curried functions, we said that all Haskell functions actually take one parameter. A function a -> b -> c actually takes just one parameter of type a and then returns a function b -> c, which takes one parameter and returns a c. That's how if we call a function with too few parameters (i.e. partially apply it), we get back a function that takes the number of parameters that we left out (if we're thinking about functions as taking several parameters again). So a -> b -> c can be written as a -> (b -> c), to make the currying more apparent.

-

In the same vein, if we write fmap :: (a -> b) -> (f a -> f b), we can think of fmap not as a function that takes one function and a functor and returns a functor, but as a function that takes a function and returns a new function that's just like the old one, only it takes a functor as a parameter and returns a functor as the result. It takes an a -> b function and returns a function f a -> f b. This is called lifting a function. Let's play around with that idea by using GHCI's :t command:

-
-ghci> :t fmap (*2)
-fmap (*2) :: (Num a, Functor f) => f a -> f a
-ghci> :t fmap (replicate 3)
-fmap (replicate 3) :: (Functor f) => f a -> f [a]
-
-

The expression fmap (*2) is a function that takes a functor f over numbers and returns a functor over numbers. That functor can be a list, a Maybe , an Either String, whatever. The expression fmap (replicate 3) will take a functor over any type and return a functor over a list of elements of that type.

-
When we say a functor over numbers, you can think of that as a functor that has numbers in it. The former is a bit fancier and more technically correct, but the latter is usually easier to get.
-

This is even more apparent if we partially apply, say, fmap (++"!") and then bind it to a name in GHCI.

-

You can think of fmap as either a function that takes a function and a functor and then maps that function over the functor, or you can think of it as a function that takes a function and lifts that function so that it operates on functors. Both views are correct and in Haskell, equivalent.

-

The type fmap (replicate 3) :: (Functor f) => f a -> f [a] means that the function will work on any functor. What exactly it will do depends on which functor we use it on. If we use fmap (replicate 3) on a list, the list's implementation for fmap will be chosen, which is just map. If we use it on a Maybe a, it'll apply replicate 3 to the value inside the Just, or if it's Nothing, then it stays Nothing.

-
-ghci> fmap (replicate 3) [1,2,3,4]
-[[1,1,1],[2,2,2],[3,3,3],[4,4,4]]
-ghci> fmap (replicate 3) (Just 4)
-Just [4,4,4]
-ghci> fmap (replicate 3) (Right "blah")
-Right ["blah","blah","blah"]
-ghci> fmap (replicate 3) Nothing
-Nothing
-ghci> fmap (replicate 3) (Left "foo")
-Left "foo"
-
-

Next up, we're going to look at the functor laws. In order for something to be a functor, it should satisfy some laws. All functors are expected to exhibit certain kinds of functor-like properties and behaviors. They should reliably behave as things that can be mapped over. Calling fmap on a functor should just map a function over the functor, nothing more. This behavior is described in the functor laws. There are two of them that all instances of Functor should abide by. They aren't enforced by Haskell automatically, so you have to test them out yourself.

-

The first functor law states that if we map the id function over a functor, the functor that we get back should be the same as the original functor. If we write that a bit more formally, it means that fmap id = id. So essentially, this says that if we do fmap id over a functor, it should be the same as just calling id on the functor. Remember, id is the identity function, which just returns its parameter unmodified. It can also be written as \x -> x. If we view the functor as something that can be mapped over, the fmap id = id law seems kind of trivial or obvious.

-

Let's see if this law holds for a few values of functors.

-
-ghci> fmap id (Just 3)
-Just 3
-ghci> id (Just 3)
-Just 3
-ghci> fmap id [1..5]
-[1,2,3,4,5]
-ghci> id [1..5]
-[1,2,3,4,5]
-ghci> fmap id []
-[]
-ghci> fmap id Nothing
-Nothing
-
-

If we look at the implementation of fmap for, say, Maybe, we can figure out why the first functor law holds.

-
-instance Functor Maybe where
-    fmap f (Just x) = Just (f x)
-    fmap f Nothing = Nothing
-
-

We imagine that id plays the role of the f parameter in the implementation. We see that if wee fmap id over Just x, the result will be Just (id x), and because id just returns its parameter, we can deduce that Just (id x) equals Just x. So now we know that if we map id over a Maybe value with a Just value constructor, we get that same value back.

-

Seeing that mapping id over a Nothing value returns the same value is trivial. So from these two equations in the implementation for fmap, we see that the law fmap id = id holds.

-justice is blind, but so is my dog -

The second law says that composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other one. Formally written, that means that fmap (f . g) = fmap f . fmap g. Or to write it in another way, for any functor F, the following should hold: fmap (f . g) F = fmap f (fmap g F).

-

If we can show that some type obeys both functor laws, we can rely on it having the same fundamental behaviors as other functors when it comes to mapping. We can know that when we use fmap on it, there won't be anything other than mapping going on behind the scenes and that it will act like a thing that can be mapped over, i.e. a functor. You figure out how the second law holds for some type by looking at the implementation of fmap for that type and then using the method that we used to check if Maybe obeys the first law.

-

If you want, we can check out how the second functor law holds for Maybe. If we do fmap (f . g) over Nothing, we get Nothing, because doing a fmap with any function over Nothing returns Nothing. If we do fmap f (fmap g Nothing), we get Nothing, for the same reason. OK, seeing how the second law holds for Maybe if it's a Nothing value is pretty easy, almost trivial.

How about if it's a Just something value? Well, if we do fmap (f . g) (Just x), we see from the implementation that it's implemented as Just ((f . g) x), which is, of course, Just (f (g x)). If we do fmap f (fmap g (Just x)), we see from the implementation that fmap g (Just x) is Just (g x). Ergo, fmap f (fmap g (Just x)) equals fmap f (Just (g x)) and from the implementation we see that this equals Just (f (g x)).

-

If you're a bit confused by this proof, don't worry. Be sure that you understand how function composition works. Many times, you can intuitively see how these laws hold because the types act like containers or functions. You can also just try them on a bunch of different values of a type and be able to say with some certainty that a type does indeed obey the laws.

-

Let's take a look at a pathological example of a type constructor being an instance of the Functor typeclass but not really being a functor, because it doesn't satisfy the laws. Let's say that we have a type:

-
-data CMaybe a = CNothing | CJust Int a deriving (Show)
-
-

The C here stands for counter. It's a data type that looks much like Maybe a, only the Just part holds two fields instead of one. The first field in the CJust value constructor will always have a type of Int, and it will be some sort of counter and the second field is of type a, which comes from the type parameter and its type will, of course, depend on the concrete type that we choose for CMaybe a. Let's play with our new type to get some intuition for it.

-
-ghci> CNothing
-CNothing
-ghci> CJust 0 "haha"
-CJust 0 "haha"
-ghci> :t CNothing
-CNothing :: CMaybe a
-ghci> :t CJust 0 "haha"
-CJust 0 "haha" :: CMaybe [Char]
-ghci> CJust 100 [1,2,3]
-CJust 100 [1,2,3]
-
-

If we use the CNothing constructor, there are no fields, and if we use the CJust constructor, the first field is an integer and the second field can be any type. Let's make this an instance of Functor so that every time we use fmap, the function gets applied to the second field, whereas the first field gets increased by 1.

-
-instance Functor CMaybe where
-    fmap f CNothing = CNothing
-    fmap f (CJust counter x) = CJust (counter+1) (f x)
-
-

This is kind of like the instance implementation for Maybe, except that when we do fmap over a value that doesn't represent an empty box (a CJust value), we don't just apply the function to the contents, we also increase the counter by 1. Everything seems cool so far, we can even play with this a bit:

-
-ghci> fmap (++"ha") (CJust 0 "ho")
-CJust 1 "hoha"
-ghci> fmap (++"he") (fmap (++"ha") (CJust 0 "ho"))
-CJust 2 "hohahe"
-ghci> fmap (++"blah") CNothing
-CNothing
-
-

Does this obey the functor laws? In order to see that something doesn't obey a law, it's enough to find just one counter-example.

-
-ghci> fmap id (CJust 0 "haha")
-CJust 1 "haha"
-ghci> id (CJust 0 "haha")
-CJust 0 "haha"
-
-

Ah! We know that the first functor law states that if we map id over a functor, it should be the same as just calling id with the same functor, but as we've seen from this example, this is not true for our CMaybe functor. Even though it's part of the Functor typeclass, it doesn't obey the functor laws and is therefore not a functor. If someone used our CMaybe type as a functor, they would expect it to obey the functor laws like a good functor. But CMaybe fails at being a functor even though it pretends to be one, so using it as a functor might lead to some faulty code. When we use a functor, it shouldn't matter if we first compose a few functions and then map them over the functor or if we just map each function over a functor in succession. But with CMaybe, it matters, because it keeps track of how many times it's been mapped over. Not cool! If we wanted CMaybe to obey the functor laws, we'd have to make it so that the Int field stays the same when we use fmap.

-

At first, the functor laws might seem a bit confusing and unnecessary, but then we see that if we know that a type obeys both laws, we can make certain assumptions about how it will act. If a type obeys the functor laws, we know that calling fmap on a value of that type will only map the function over it, nothing more. This leads to code that is more abstract and extensible, because we can use laws to reason about behaviors that any functor should have and make functions that operate reliably on any functor.

-

All the Functor instances in the standard library obey these laws, but you can check for yourself if you don't believe me. And the next time you make a type an instance of Functor, take a minute to make sure that it obeys the functor laws. Once you've dealt with enough functors, you kind of intuitively see the properties and behaviors that they have in common and it's not hard to intuitively see if a type obeys the functor laws. But even without the intuition, you can always just go over the implementation line by line and see if the laws hold or try to find a counter-example.

-

We can also look at functors as things that output values in a context. For instance, Just 3 outputs the value 3 in the context that it might or not output any values at all. [1,2,3] outputs three values—1, 2, and 3, the context is that there may be multiple values or no values. The function (+3) will output a value, depending on which parameter it is given.

-

If you think of functors as things that output values, you can think of mapping over functors as attaching a transformation to the output of the functor that changes the value. When we do fmap (+3) [1,2,3], we attach the transformation (+3) to the output of [1,2,3], so whenever we look at a number that the list outputs, (+3) will be applied to it. Another example is mapping over functions. When we do fmap (+3) (*3), we attach the transformation (+3) to the eventual output of (*3). Looking at it this way gives us some intuition as to why using fmap on functions is just composition (fmap (+3) (*3) equals (+3) . (*3), which equals \x -> ((x*3)+3)), because we take a function like (*3) then we attach the transformation (+3) to its output. The result is still a function, only when we give it a number, it will be multiplied by three and then it will go through the attached transformation where it will be added to three. This is what happens with composition.

-

Applicative functors

-disregard this analogy -

In this section, we'll take a look at applicative functors, which are beefed up functors, represented in Haskell by the Applicative typeclass, found in the Control.Applicative module.

-

As you know, functions in Haskell are curried by default, which means that a function that seems to take several parameters actually takes just one parameter and returns a function that takes the next parameter and so on. If a function is of type a -> b -> c, we usually say that it takes two parameters and returns a c, but actually it takes an a and returns a function b -> c. That's why we can call a function as f x y or as (f x) y. This mechanism is what enables us to partially apply functions by just calling them with too few parameters, which results in functions that we can then pass on to other functions.

-

So far, when we were mapping functions over functors, we usually mapped functions that take only one parameter. But what happens when we map a function like *, which takes two parameters, over a functor? Let's take a look at a couple of concrete examples of this. If we have Just 3 and we do fmap (*) (Just 3), what do we get? From the instance implementation of Maybe for Functor, we know that if it's a Just something value, it will apply the function to the something inside the Just. Therefore, doing fmap (*) (Just 3) results in Just ((*) 3), which can also be written as Just (* 3) if we use sections. Interesting! We get a function wrapped in a Just!

-
-ghci> :t fmap (++) (Just "hey")
-fmap (++) (Just "hey") :: Maybe ([Char] -> [Char])
-ghci> :t fmap compare (Just 'a')
-fmap compare (Just 'a') :: Maybe (Char -> Ordering)
-ghci> :t fmap compare "A LIST OF CHARS"
-fmap compare "A LIST OF CHARS" :: [Char -> Ordering]
-ghci> :t fmap (\x y z -> x + y / z) [3,4,5,6]
-fmap (\x y z -> x + y / z) [3,4,5,6] :: (Fractional a) => [a -> a -> a]
-
-

If we map compare, which has a type of (Ord a) => a -> a -> Ordering over a list of characters, we get a list of functions of type Char -> Ordering, because the function compare gets partially applied with the characters in the list. It's not a list of (Ord a) => a -> Ordering function, because the first a that got applied was a Char and so the second a has to decide to be of type Char.

-

We see how by mapping "multi-parameter" functions over functors, we get functors that contain functions inside them. So now what can we do with them? Well for one, we can map functions that take these functions as parameters over them, because whatever is inside a functor will be given to the function that we're mapping over it as a parameter.

-
-ghci> let a = fmap (*) [1,2,3,4]
-ghci> :t a
-a :: [Integer -> Integer]
-ghci> fmap (\f -> f 9) a
-[9,18,27,36]
-
-

But what if we have a functor value of Just (3 *) and a functor value of Just 5 and we want to take out the function from Just (3 *) and map it over Just 5? With normal functors, we're out of luck, because all they support is just mapping normal functions over existing functors. Even when we mapped \f -> f 9 over a functor that contained functions inside it, we were just mapping a normal function over it. But we can't map a function that's inside a functor over another functor with what fmap offers us. We could pattern-match against the Just constructor to get the function out of it and then map it over Just 5, but we're looking for a more general and abstract way of doing that, which works across functors.

-

Meet the Applicative typeclass. It lies in the Control.Applicative module and it defines two methods, pure and <*>. It doesn't provide a default implementation for any of them, so we have to define them both if we want something to be an applicative functor. The class is defined like so:

-
-class (Functor f) => Applicative f where
-    pure :: a -> f a
-    (<*>) :: f (a -> b) -> f a -> f b
-
-

This simple three line class definition tells us a lot! Let's start at the first line. It starts the definition of the Applicative class and it also introduces a class constraint. It says that if we want to make a type constructor part of the Applicative typeclass, it has to be in Functor first. That's why if we know that if a type constructor is part of the Applicative typeclass, it's also in Functor, so we can use fmap on it.

-

The first method it defines is called pure. Its type declaration is pure :: a -> f a. f plays the role of our applicative functor instance here. Because Haskell has a very good type system and because everything a function can do is take some parameters and return some value, we can tell a lot from a type declaration and this is no exception. pure should take a value of any type and return an applicative functor with that value inside it. When we say inside it, we're using the box analogy again, even though we've seen that it doesn't always stand up to scrutiny. But the a -> f a type declaration is still pretty descriptive. We take a value and we wrap it in an applicative functor that has that value as the result inside it.

-

A better way of thinking about pure would be to say that it takes a value and puts it in some sort of default (or pure) context—a minimal context that still yields that value.

-

The <*> function is really interesting. It has a type declaration of f (a -> b) -> f a -> f b. Does this remind you of anything? Of course, fmap :: (a -> b) -> f a -> f b. It's a sort of beefed up fmap. Whereas fmap takes a function and a functor and applies the function inside the functor, <*> takes a functor that has a function in it and another functor and sort of extracts that function from the first functor and then maps it over the second one. When I say extract, I actually sort of mean run and then extract, maybe even sequence. We'll see why soon. -

Let's take a look at the Applicative instance implementation for Maybe.

-
-instance Applicative Maybe where
-    pure = Just
-    Nothing <*> _ = Nothing
-    (Just f) <*> something = fmap f something
-
-

Again, from the class definition we see that the f that plays the role of the applicative functor should take one concrete type as a parameter, so we write instance Applicative Maybe where instead of writing instance Applicative (Maybe a) where.

-

First off, pure. We said earlier that it's supposed to take something and wrap it in an applicative functor. We wrote pure = Just, because value constructors like Just are normal functions. We could have also written pure x = Just x.

-

Next up, we have the definition for <*>. We can't extract a function out of a Nothing, because it has no function inside it. So we say that if we try to extract a function from a Nothing, the result is a Nothing. If you look at the class definition for Applicative, you'll see that there's a Functor class constraint, which means that we can assume that both of <*>'s parameters are functors. If the first parameter is not a Nothing, but a Just with some function inside it, we say that we then want to map that function over the second parameter. This also takes care of the case where the second parameter is Nothing, because doing fmap with any function over a Nothing will return a Nothing.

-

So for Maybe, <*> extracts the function from the left value if it's a Just and maps it over the right value. If any of the parameters is Nothing, Nothing is the result.

-

OK cool great. Let's give this a whirl.

-
-ghci> Just (+3) <*> Just 9
-Just 12
-ghci> pure (+3) <*> Just 10
-Just 13
-ghci> pure (+3) <*> Just 9
-Just 12
-ghci> Just (++"hahah") <*> Nothing
-Nothing
-ghci> Nothing <*> Just "woot"
-Nothing
-
-

We see how doing pure (+3) and Just (+3) is the same in this case. Use pure if you're dealing with Maybe values in an applicative context (i.e. using them with <*>), otherwise stick to Just. The first four input lines demonstrate how the function is extracted and then mapped, but in this case, they could have been achieved by just mapping unwrapped functions over functors. The last line is interesting, because we try to extract a function from a Nothing and then map it over something, which of course results in a Nothing.

-

With normal functors, you can just map a function over a functor and then you can't get the result out in any general way, even if the result is a partially applied function. Applicative functors, on the other hand, allow you to operate on several functors with a single function. Check out this piece of code:

-
-ghci> pure (+) <*> Just 3 <*> Just 5
-Just 8
-ghci> pure (+) <*> Just 3 <*> Nothing
-Nothing
-ghci> pure (+) <*> Nothing <*> Just 5
-Nothing
-
-whale -

What's going on here? Let's take a look, step by step. <*> is left-associative, which means that pure (+) <*> Just 3 <*> Just 5 is the same as (pure (+) <*> Just 3) <*> Just 5. First, the + function is put in a functor, which is in this case a Maybe value that contains the function. So at first, we have pure (+), which is Just (+). Next, Just (+) <*> Just 3 happens. The result of this is Just (3+). This is because of partial application. Only applying 3 to the + function results in a function that takes one parameter and adds 3 to it. Finally, Just (3+) <*> Just 5 is carried out, which results in a Just 8.

-

Isn't this awesome?! Applicative functors and the applicative style of doing pure f <*> x <*> y <*> ... allow us to take a function that expects parameters that aren't necessarily wrapped in functors and use that function to operate on several values that are in functor contexts. The function can take as many parameters as we want, because it's always partially applied step by step between occurrences of <*>.

-

This becomes even more handy and apparent if we consider the fact that pure f <*> x equals fmap f x. This is one of the applicative laws. We'll take a closer look at them later, but for now, we can sort of intuitively see that this is so. Think about it, it makes sense. Like we said before, pure puts a value in a default context. If we just put a function in a default context and then extract and apply it to a value inside another applicative functor, we did the same as just mapping that function over that applicative functor. Instead of writing pure f <*> x <*> y <*> ..., we can write fmap f x <*> y <*> .... This is why Control.Applicative exports a function called <$>, which is just fmap as an infix operator. Here's how it's defined:

-
-(<$>) :: (Functor f) => (a -> b) -> f a -> f b
-f <$> x = fmap f x
-
-
Yo! Quick reminder: type variables are independent of parameter names or other value names. The f in the function declaration here is a type variable with a class constraint saying that any type constructor that replaces f should be in the Functor typeclass. The f in the function body denotes a function that we map over x. The fact that we used f to represent both of those doesn't mean that they somehow represent the same thing.
-

By using <$>, the applicative style really shines, because now if we want to apply a function f between three applicative functors, we can write f <$> x <*> y <*> z. If the parameters weren't applicative functors but normal values, we'd write f x y z.

-

Let's take a closer look at how this works. We have a value of Just "johntra" and a value of Just "volta" and we want to join them into one String inside a Maybe functor. We do this:

-
-ghci> (++) <$> Just "johntra" <*> Just "volta"
-Just "johntravolta"
-
-

Before we see how this happens, compare the above line with this:

-
-ghci> (++) "johntra" "volta"
-"johntravolta"
-
-

Awesome! To use a normal function on applicative functors, just sprinkle some <$> and <*> about and the function will operate on applicatives and return an applicative. How cool is that?

-

Anyway, when we do (++) <$> Just "johntra" <*> Just "volta", first (++), which has a type of (++) :: [a] -> [a] -> [a] gets mapped over Just "johntra", resulting in a value that's the same as Just ("johntra"++) and has a type of Maybe ([Char] -> [Char]). Notice how the first parameter of (++) got eaten up and how the as turned into Chars. And now Just ("johntra"++) <*> Just "volta" happens, which takes the function out of the Just and maps it over Just "volta", resulting in Just "johntravolta". Had any of the two values been Nothing, the result would have also been Nothing.

-

So far, we've only used Maybe in our examples and you might be thinking that applicative functors are all about Maybe. There are loads of other instances of Applicative, so let's go and meet them!

-

Lists (actually the list type constructor, []) are applicative functors. What a surprise! Here's how [] is an instance of Applicative:

-
-instance Applicative [] where
-    pure x = [x]
-    fs <*> xs = [f x | f <- fs, x <- xs]
-
-

Earlier, we said that pure takes a value and puts it in a default context. Or in other words, a minimal context that still yields that value. The minimal context for lists would be the empty list, [], but the empty list represents the lack of a value, so it can't hold in itself the value that we used pure on. That's why pure takes a value and puts it in a singleton list. Similarly, the minimal context for the Maybe applicative functor would be a Nothing, but it represents the lack of a value instead of a value, so pure is implemented as Just in the instance implementation for Maybe.

-
-ghci> pure "Hey" :: [String]
-["Hey"]
-ghci> pure "Hey" :: Maybe String
-Just "Hey"
-
-

What about <*>? If we look at what <*>'s type would be if it were limited only to lists, we get (<*>) :: [a -> b] -> [a] -> [b]. It's implemented with a list comprehension. <*> has to somehow extract the function out of its left parameter and then map it over the right parameter. But the thing here is that the left list can have zero functions, one function, or several functions inside it. The right list can also hold several values. That's why we use a list comprehension to draw from both lists. We apply every possible function from the left list to every possible value from the right list. The resulting list has every possible combination of applying a function from the left list to a value in the right one.

-
-ghci> [(*0),(+100),(^2)] <*> [1,2,3]
-[0,0,0,101,102,103,1,4,9]
-
-

The left list has three functions and the right list has three values, so the resulting list will have nine elements. Every function in the left list is applied to every function in the right one. If we have a list of functions that take two parameters, we can apply those functions between two lists.

-
-ghci> [(+),(*)] <*> [1,2] <*> [3,4]
-[4,5,5,6,3,4,6,8]
-
-

Because <*> is left-associative, [(+),(*)] <*> [1,2] happens first, resulting in a list that's the same as [(1+),(2+),(1*),(2*)], because every function on the left gets applied to every value on the right. Then, [(1+),(2+),(1*),(2*)] <*> [3,4] happens, which produces the final result.

-

Using the applicative style with lists is fun! Watch:

-
-ghci> (++) <$> ["ha","heh","hmm"] <*> ["?","!","."]
-["ha?","ha!","ha.","heh?","heh!","heh.","hmm?","hmm!","hmm."]
-
-

Again, see how we used a normal function that takes two strings between two applicative functors of strings just by inserting the appropriate applicative operators.

-

You can view lists as non-deterministic computations. A value like 100 or "what" can be viewed as a deterministic computation that has only one result, whereas a list like [1,2,3] can be viewed as a computation that can't decide on which result it wants to have, so it presents us with all of the possible results. So when you do something like (+) <$> [1,2,3] <*> [4,5,6], you can think of it as adding together two non-deterministic computations with +, only to produce another non-deterministic computation that's even less sure about its result.

-

Using the applicative style on lists is often a good replacement for list comprehensions. In the second chapter, we wanted to see all the possible products of [2,5,10] and [8,10,11], so we did this:

-
-ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
-[16,20,22,40,50,55,80,100,110]
-
-

We're just drawing from two lists and applying a function between every combination of elements. This can be done in the applicative style as well:

-
-ghci> (*) <$> [2,5,10] <*> [8,10,11]
-[16,20,22,40,50,55,80,100,110]
-
-

This seems clearer to me, because it's easier to see that we're just calling * between two non-deterministic computations. If we wanted all possible products of those two lists that are more than 50, we'd just do:

-
-ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]
-[55,80,100,110]
-
-

It's easy to see how pure f <*> xs equals fmap f xs with lists. pure f is just [f] and [f] <*> xs will apply every function in the left list to every value in the right one, but there's just one function in the left list, so it's like mapping.

-

Another instance of Applicative that we've already encountered is IO. This is how the instance is implemented:

-
-instance Applicative IO where
-    pure = return
-    a <*> b = do
-        f <- a
-        x <- b
-        return (f x)
-
-ahahahah! -

Since pure is all about putting a value in a minimal context that still holds it as its result, it makes sense that pure is just return, because return does exactly that; it makes an I/O action that doesn't do anything, it just yields some value as its result, but it doesn't really do any I/O operations like printing to the terminal or reading from a file.

-

If <*> were specialized for IO it would have a type of (<*>) :: IO (a -> b) -> IO a -> IO b. It would take an I/O action that yields a function as its result and another I/O action and create a new I/O action from those two that, when performed, first performs the first one to get the function and then performs the second one to get the value and then it would yield that function applied to the value as its result. We used do syntax to implement it here. Remember, do syntax is about taking several I/O actions and gluing them into one, which is exactly what we do here.

-

With Maybe and [], we could think of <*> as simply extracting a function from its left parameter and then sort of applying it over the right one. With IO, extracting is still in the game, but now we also have a notion of sequencing, because we're taking two I/O actions and we're sequencing, or gluing, them into one. We have to extract the function from the first I/O action, but to extract a result from an I/O action, it has to be performed.

-

Consider this:

-
-myAction :: IO String
-myAction = do
-    a <- getLine
-    b <- getLine
-    return $ a ++ b
-
-

This is an I/O action that will prompt the user for two lines and yield as its result those two lines concatenated. We achieved it by gluing together two getLine I/O actions and a return, because we wanted our new glued I/O action to hold the result of a ++ b. Another way of writing this would be to use the applicative style.

-
-myAction :: IO String
-myAction = (++) <$> getLine <*> getLine
-
-

What we were doing before was making an I/O action that applied a function between the results of two other I/O actions, and this is the same thing. Remember, getLine is an I/O action with the type getLine :: IO String. When we use <*> between two applicative functors, the result is an applicative functor, so this all makes sense.

-

If we regress to the box analogy, we can imagine getLine as a box that will go out into the real world and fetch us a string. Doing (++) <$> getLine <*> getLine makes a new, bigger box that sends those two boxes out to fetch lines from the terminal and then presents the concatenation of those two lines as its result.

-

The type of the expression (++) <$> getLine <*> getLine is IO String, which means that this expression is a completely normal I/O action like any other, which also holds a result value inside it, just like other I/O actions. That's why we can do stuff like:

-
-main = do
-    a <- (++) <$> getLine <*> getLine
-    putStrLn $ "The two lines concatenated turn out to be: " ++ a
-
-

If you ever find yourself binding some I/O actions to names and then calling some function on them and presenting that as the result by using return, consider using the applicative style because it's arguably a bit more concise and terse.

-

Another instance of Applicative is (->) r, so functions. They are rarely used with the applicative style outside of code golf, but they're still interesting as applicatives, so let's take a look at how the function instance is implemented.

-
If you're confused about what (->) r means, check out the previous section where we explain how (->) r is a functor.
-
-instance Applicative ((->) r) where
-    pure x = (\_ -> x)
-    f <*> g = \x -> f x (g x)
-
-

When we wrap a value into an applicative functor with pure, the result it yields always has to be that value. A minimal default context that still yields that value as a result. That's why in the function instance implementation, pure takes a value and creates a function that ignores its parameter and always returns that value. If we look at the type for pure, but specialized for the (->) r instance, it's pure :: a -> (r -> a).

-
-ghci> (pure 3) "blah"
-3
-
-

Because of currying, function application is left-associative, so we can omit the parentheses.

-
-ghci> pure 3 "blah"
-3
-
-

The instance implementation for <*> is a bit cryptic, so it's best if we just take a look at how to use functions as applicative functors in the applicative style.

-
-ghci> :t (+) <$> (+3) <*> (*100)
-(+) <$> (+3) <*> (*100) :: (Num a) => a -> a
-ghci> (+) <$> (+3) <*> (*100) $ 5
-508
-
-

Calling <*> with two applicative functors results in an applicative functor, so if we use it on two functions, we get back a function. So what goes on here? When we do (+) <$> (+3) <*> (*100), we're making a function that will use + on the results of (+3) and (*100) and return that. To demonstrate on a real example, when we did (+) <$> (+3) <*> (*100) $ 5, the 5 first got applied to (+3) and (*100), resulting in 8 and 500. Then, + gets called with 8 and 500, resulting in 508.

-
-ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
-[8.0,10.0,2.5]
-
-SLAP -

Same here. We create a function that will call the function \x y z -> [x,y,z] with the eventual results from (+3), (*2) and (/2). The 5 gets fed to each of the three functions and then \x y z -> [x, y, z] gets called with those results.

-

You can think of functions as boxes that contain their eventual results, so doing k <$> f <*> g creates a function that will call k with the eventual results from f and g. When we do something like (+) <$> Just 3 <*> Just 5, we're using + on values that might or might not be there, which also results in a value that might or might not be there. When we do (+) <$> (+10) <*> (+5), we're using + on the future return values of (+10) and (+5) and the result is also something that will produce a value only when called with a parameter.

-

We don't often use functions as applicatives, but this is still really interesting. It's not very important that you get how the (->) r instance for Applicative works, so don't despair if you're not getting this right now. Try playing with the applicative style and functions to build up an intuition for functions as applicatives.

-

An instance of Applicative that we haven't encountered yet is ZipList, and it lives in Control.Applicative.

-

It turns out there are actually more ways for lists to be applicative functors. One way is the one we already covered, which says that calling <*> with a list of functions and a list of values results in a list which has all the possible combinations of applying functions from the left list to the values in the right list. If we do [(+3),(*2)] <*> [1,2], (+3) will be applied to both 1 and 2 and (*2) will also be applied to both 1 and 2, resulting in a list that has four elements, namely [4,5,2,4].

-

However, [(+3),(*2)] <*> [1,2] could also work in such a way that the first function in the left list gets applied to the first value in the right one, the second function gets applied to the second value, and so on. That would result in a list with two values, namely [4,4]. You could look at it as [1 + 3, 2 * 2].

-

Because one type can't have two instances for the same typeclass, the ZipList a type was introduced, which has one constructor ZipList that has just one field, and that field is a list. Here's the instance:

-
-instance Applicative ZipList where
-        pure x = ZipList (repeat x)
-        ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
-
-

<*> does just what we said. It applies the first function to the first value, the second function to the second value, etc. This is done with zipWith (\f x -> f x) fs xs. Because of how zipWith works, the resulting list will be as long as the shorter of the two lists.

-

pure is also interesting here. It takes a value and puts it in a list that just has that value repeating indefinitely. pure "haha" results in ZipList (["haha","haha","haha".... This might be a bit confusing since we said that pure should put a value in a minimal context that still yields that value. And you might be thinking that an infinite list of something is hardly minimal. But it makes sense with zip lists, because it has to produce the value on every position. This also satisfies the law that pure f <*> xs should equal fmap f xs. If pure 3 just returned ZipList [3], pure (*2) <*> ZipList [1,5,10] would result in ZipList [2], because the resulting list of two zipped lists has the length of the shorter of the two. If we zip a finite list with an infinite list, the length of the resulting list will always be equal to the length of the finite list.

-

So how do zip lists work in an applicative style? Let's see. Oh, the ZipList a type doesn't have a Show instance, so we have to use the getZipList function to extract a raw list out of a zip list.

-
-ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100]
-[101,102,103]
-ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..]
-[101,102,103]
-ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2]
-[5,3,3,4]
-ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat"
-[('d','c','r'),('o','a','a'),('g','t','t')]
-
-
The (,,) function is the same as \x y z -> (x,y,z). Also, the (,) function is the same as \x y -> (x,y).
-

Aside from zipWith, the standard library has functions such as zipWith3, zipWith4, all the way up to 7. zipWith takes a function that takes two parameters and zips two lists with it. zipWith3 takes a function that takes three parameters and zips three lists with it, and so on. By using zip lists with an applicative style, we don't have to have a separate zip function for each number of lists that we want to zip together. We just use the applicative style to zip together an arbitrary amount of lists with a function, and that's pretty cool.

-

Control.Applicative defines a function that's called liftA2, which has a type of liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c . It's defined like this:

-
-liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c
-liftA2 f a b = f <$> a <*> b
-
-

Nothing special, it just applies a function between two applicatives, hiding the applicative style that we've become familiar with. The reason we're looking at it is because it clearly showcases why applicative functors are more powerful than just ordinary functors. With ordinary functors, we can just map functions over one functor. But with applicative functors, we can apply a function between several functors. It's also interesting to look at this function's type as (a -> b -> c) -> (f a -> f b -> f c). When we look at it like this, we can say that liftA2 takes a normal binary function and promotes it to a function that operates on two functors.

-

Here's an interesting concept: we can take two applicative functors and combine them into one applicative functor that has inside it the results of those two applicative functors in a list. For instance, we have Just 3 and Just 4. Let's assume that the second one has a singleton list inside it, because that's really easy to achieve:

-
-ghci> fmap (\x -> [x]) (Just 4)
-Just [4]
-
-

OK, so let's say we have Just 3 and Just [4]. How do we get Just [3,4]? Easy.

-
-ghci> liftA2 (:) (Just 3) (Just [4])
-Just [3,4]
-ghci> (:) <$> Just 3 <*> Just [4]
-Just [3,4]
-
-

Remember, : is a function that takes an element and a list and returns a new list with that element at the beginning. Now that we have Just [3,4], could we combine that with Just 2 to produce Just [2,3,4]? Of course we could. It seems that we can combine any amount of applicatives into one applicative that has a list of the results of those applicatives inside it. Let's try implementing a function that takes a list of applicatives and returns an applicative that has a list as its result value. We'll call it sequenceA.

-
-sequenceA :: (Applicative f) => [f a] -> f [a]
-sequenceA [] = pure []
-sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
-
-

Ah, recursion! First, we look at the type. It will transform a list of applicatives into an applicative with a list. From that, we can lay some groundwork for an edge condition. If we want to turn an empty list into an applicative with a list of results, well, we just put an empty list in a default context. Now comes the recursion. If we have a list with a head and a tail (remember, x is an applicative and xs is a list of them), we call sequenceA on the tail, which results in an applicative with a list. Then, we just prepend the value inside the applicative x into that applicative with a list, and that's it!

-

So if we do sequenceA [Just 1, Just 2], that's (:) <$> Just 1 <*> sequenceA [Just 2] . That equals (:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA []). Ah! We know that sequenceA [] ends up as being Just [], so this expression is now (:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just []), which is (:) <$> Just 1 <*> Just [2], which is Just [1,2]!

-

Another way to implement sequenceA is with a fold. Remember, pretty much any function where we go over a list element by element and accumulate a result along the way can be implemented with a fold.

-
-sequenceA :: (Applicative f) => [f a] -> f [a]
-sequenceA = foldr (liftA2 (:)) (pure [])
-
-

We approach the list from the right and start off with an accumulator value of pure []. We do liftA2 (:) between the accumulator and the last element of the list, which results in an applicative that has a singleton in it. Then we do liftA2 (:) with the now last element and the current accumulator and so on, until we're left with just the accumulator, which holds a list of the results of all the applicatives.

-

Let's give our function a whirl on some applicatives.

-
-ghci> sequenceA [Just 3, Just 2, Just 1]
-Just [3,2,1]
-ghci> sequenceA [Just 3, Nothing, Just 1]
-Nothing
-ghci> sequenceA [(+3),(+2),(+1)] 3
-[6,5,4]
-ghci> sequenceA [[1,2,3],[4,5,6]]
-[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
-ghci> sequenceA [[1,2,3],[4,5,6],[3,4,4],[]]
-[]
-
-

Ah! Pretty cool. When used on Maybe values, sequenceA creates a Maybe value with all the results inside it as a list. If one of the values was Nothing, then the result is also a Nothing. This is cool when you have a list of Maybe values and you're interested in the values only if none of them is a Nothing.

-

When used with functions, sequenceA takes a list of functions and returns a function that returns a list. In our example, we made a function that took a number as a parameter and applied it to each function in the list and then returned a list of results. sequenceA [(+3),(+2),(+1)] 3 will call (+3) with 3, (+2) with 3 and (+1) with 3 and present all those results as a list.

-

Doing (+) <$> (+3) <*> (*2) will create a function that takes a parameter, feeds it to both (+3) and (*2) and then calls + with those two results. In the same vein, it makes sense that sequenceA [(+3),(*2)] makes a function that takes a parameter and feeds it to all of the functions in the list. Instead of calling + with the results of the functions, a combination of : and pure [] is used to gather those results in a list, which is the result of that function.

-

Using sequenceA is cool when we have a list of functions and we want to feed the same input to all of them and then view the list of results. For instance, we have a number and we're wondering whether it satisfies all of the predicates in a list. One way to do that would be like so:

-
-ghci> map (\f -> f 7) [(>4),(<10),odd]
-[True,True,True]
-ghci> and $ map (\f -> f 7) [(>4),(<10),odd]
-True
-
-

Remember, and takes a list of booleans and returns True if they're all True. Another way to achieve the same thing would be with sequenceA:

-
-ghci> sequenceA [(>4),(<10),odd] 7
-[True,True,True]
-ghci> and $ sequenceA [(>4),(<10),odd] 7
-True
-
-

sequenceA [(>4),(<10),odd] creates a function that will take a number and feed it to all of the predicates in [(>4),(<10),odd] and return a list of booleans. It turns a list with the type (Num a) => [a -> Bool] into a function with the type (Num a) => a -> [Bool]. Pretty neat, huh?

-

Because lists are homogenous, all the functions in the list have to be functions of the same type, of course. You can't have a list like [ord, (+3)], because ord takes a character and returns a number, whereas (+3) takes a number and returns a number.

-

When used with [], sequenceA takes a list of lists and returns a list of lists. Hmm, interesting. It actually creates lists that have all possible combinations of their elements. For illustration, here's the above done with sequenceA and then done with a list comprehension:

-
-ghci> sequenceA [[1,2,3],[4,5,6]]
-[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
-ghci> [[x,y] | x <- [1,2,3], y <- [4,5,6]]
-[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
-ghci> sequenceA [[1,2],[3,4]]
-[[1,3],[1,4],[2,3],[2,4]]
-ghci> [[x,y] | x <- [1,2], y <- [3,4]]
-[[1,3],[1,4],[2,3],[2,4]]
-ghci> sequenceA [[1,2],[3,4],[5,6]]
-[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
-ghci> [[x,y,z] | x <- [1,2], y <- [3,4], z <- [5,6]]
-[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
-
-

This might be a bit hard to grasp, but if you play with it for a while, you'll see how it works. Let's say that we're doing sequenceA [[1,2],[3,4]]. To see how this happens, let's use the sequenceA (x:xs) = (:) <$> x <*> sequenceA xs definition of sequenceA and the edge condition sequenceA [] = pure []. You don't have to follow this evaluation, but it might help you if have trouble imagining how sequenceA works on lists of lists, because it can be a bit mind-bending.

- -

Doing (+) <$> [1,2] <*> [4,5,6]results in a non-deterministic computation x + y where x takes on every value from [1,2] and y takes on every value from [4,5,6]. We represent that as a list which holds all of the possible results. Similarly, when we do sequence [[1,2],[3,4],[5,6],[7,8]], the result is a non-deterministic computation [x,y,z,w], where x takes on every value from [1,2], y takes on every value from [3,4] and so on. To represent the result of that non-deterministic computation, we use a list, where each element in the list is one possible list. That's why the result is a list of lists.

-

When used with I/O actions, sequenceA is the same thing as sequence! It takes a list of I/O actions and returns an I/O action that will perform each of those actions and have as its result a list of the results of those I/O actions. That's because to turn an [IO a] value into an IO [a] value, to make an I/O action that yields a list of results when performed, all those I/O actions have to be sequenced so that they're then performed one after the other when evaluation is forced. You can't get the result of an I/O action without performing it.

-
-ghci> sequenceA [getLine, getLine, getLine]
-heyh
-ho
-woo
-["heyh","ho","woo"]
-
-

Like normal functors, applicative functors come with a few laws. The most important one is the one that we already mentioned, namely that pure f <*> x = fmap f x holds. As an exercise, you can prove this law for some of the applicative functors that we've met in this chapter.The other functor laws are:

- -

We won't go over them in detail right now because that would take up a lot of pages and it would probably be kind of boring, but if you're up to the task, you can take a closer look at them and see if they hold for some of the instances.

-

In conclusion, applicative functors aren't just interesting, they're also useful, because they allow us to combine different computations, such as I/O computations, non-deterministic computations, computations that might have failed, etc. by using the applicative style. Just by using <$> and <*> we can use normal functions to uniformly operate on any number of applicative functors and take advantage of the semantics of each one.

-

The newtype keyword

- -why_ so serious? - -

-So far, we've learned how to make our own algebraic data types by using the -data keyword. We've also learned how to give existing types -synonyms with the type keyword. In this section, we'll be taking a look -at how to make new types out of existing data types by using the -newtype keyword and why we'd want to do that in the first place. -

- -

-In the previous section, we saw that there are actually more ways for the list -type to be an applicative functor. One way is to have <*> -take every function out of the list that is its left parameter and apply it to -every value in the list that is on the right, resulting in every possible -combination of applying a function from the left list to a value in the right -list. -

- -
-ghci> [(+1),(*100),(*5)] <*> [1,2,3]
-[2,3,4,100,200,300,5,10,15]
-
- -

-The second way is to take the first function on the left side of -<*> and apply it to the first value on the -right, then take the second function from the list on the left side and apply it -to the second value on the right, and so on. Ultimately, it's kind of like -zipping the two lists together. But lists are already an instance of -Applicative, so how did we also make lists an instance -of Applicative in this second way? If you remember, -we said that the ZipList a type was introduced for -this reason, which has one value constructor, ZipList, -that has just one field. We put the list that we're wrapping in that field. -Then, ZipList was made an instance of Applicative, -so that when we want to use lists as applicatives in the zipping manner, we just -wrap them with the ZipList constructor and then once -we're done, unwrap them with getZipList: -

- -
-ghci> getZipList $ ZipList [(+1),(*100),(*5)] <*> ZipList [1,2,3]
-[2,200,15]
-
- -

-So, what does this have to do with this newtype keyword? Well, think -about how we might write the data declaration for our ZipList a type. One way would be to do it like so: -

- -
-data ZipList a = ZipList [a]
-
- -

-A type that has just one value constructor and that value constructor has -just one field that is a list of things. We might also want to use record syntax -so that we automatically get a function that extracts a list from a ZipList: -

- -
-data ZipList a = ZipList { getZipList :: [a] }
-
- -

-This looks fine and would actually work pretty well. We had two ways of making -an existing type an instance of a type class, so we used the data -keyword to just wrap that type into another type and made the other type an -instance in the second way. -

- -

-The newtype keyword in Haskell is made exactly for -these cases when we want to just take one type and wrap it in something to -present it as another type. In the actual libraries, ZipList -a is defined like this: -

- -
-newtype ZipList a = ZipList { getZipList :: [a] }
-
- -

-Instead of the data keyword, the newtype keyword is used. Now why is that? Well for one, -newtype is faster. If you use the data keyword -to wrap a type, there's some overhead to all that wrapping and unwrapping when -your program is running. But if you use newtype, Haskell knows that -you're just using it to wrap an existing type into a new type (hence the name), -because you want it to be the same internally but have a different type. With -that in mind, Haskell can get rid of the wrapping and unwrapping once it -resolves which value is of what type. -

- -

-So why not just use newtype all the time instead of data then? -Well, when you make a new type from an existing type by using the -newtype keyword, you can only have one value constructor and that value -constructor can only have one field. But with data, you can make data -types that have several value constructors and each constructor can have zero or -more fields: -

- -
-data Profession = Fighter | Archer | Accountant
-
-data Race = Human | Elf | Orc | Goblin
-
-data PlayerCharacter = PlayerCharacter Race Profession
-
- -

-When using newtype, you're restricted to just one constructor with one -field. -

- -

-We can also use the deriving keyword with newtype just like we -would with data. We can derive instances for -Eq, -Ord, -Enum, -Bounded, -Show and -Read. -If we derive the instance for a type class, the type that we're wrapping -has to be in that type class to begin with. It makes sense, because -newtype just wraps an existing type. So now if we do the following, we -can print and equate values of our new type: -

- -
-newtype CharList = CharList { getCharList :: [Char] } deriving (Eq, Show)
-
- -

-Let's give that a go: -

- -
-ghci> CharList "this will be shown!"
-CharList {getCharList = "this will be shown!"}
-ghci> CharList "benny" == CharList "benny"
-True
-ghci> CharList "benny" == CharList "oysters"
-False
-
- -

-In this particular newtype, the value constructor has the following type: -

- -
-CharList :: [Char] -> CharList
-
- -

-It takes a [Char] value, such as "my sharona" -and returns a CharList value. From the above examples where we used -the CharList value constructor, we see that really is -the case. Conversely, the getCharList function, which -was generated for us because we used record syntax in our newtype, has -this type: -

- -
-getCharList :: CharList -> [Char]
-
- -

-It takes a CharList value and converts it to a -[Char] value. You can think of this as wrapping -and unwrapping, but you can also think of it as converting values from one type -to the other. -

- -

Using newtype to make type class instances

- -

-Many times, we want to make our types instances of certain type classes, but the -type parameters just don't match up for what we want to do. It's easy to make -Maybe an instance of Functor, because -the Functor type class is defined like this: -

- -
-class Functor f where
-    fmap :: (a -> b) -> f a -> f b
-
- -

So we just start out with:

- -
-instance Functor Maybe where
-
- -

-And then implement fmap. All the type parameters add -up because the Maybe takes the place of f -in the definition of the Functor type class and so if -we look at fmap like it only worked on -Maybe, it ends up behaving like: -

- -
-fmap :: (a -> b) -> Maybe a -> Maybe b
-
- -wow, very evil - -

-Isn't that just peachy? Now what if we wanted to make the tuple an instance of -Functor in such a way that when we fmap -a function over a tuple, it gets applied to the first component of the tuple? -That way, doing fmap (+3) (1,1) would result in (4,1). -It turns out that writing the instance for that is kind of hard. With Maybe, we just say instance Functor -Maybe where because only type constructors that take exactly one -parameter can be made an instance of Functor. But it -seems like there's -no way to do something like that with (a,b) so that -the type parameter a ends up being the one that -changes when we use fmap. To get around this, we -can newtype our tuple in such a way that the second type parameter -represents the type of the first component in the tuple: -

- -
-newtype Pair b a = Pair { getPair :: (a,b) }
-
- -

-And now, we can make it an instance of Functor so -that the function is mapped over the first component: -

- -
-instance Functor (Pair c) where
-    fmap f (Pair (x,y)) = Pair (f x, y)
-
- -

-As you can see, we can pattern match on types defined with newtype. We -pattern match to get the underlying tuple, then we apply the function f -to the first component in the tuple and then we use the Pair -value constructor to convert the tuple back to our Pair b a. -If we imagine what the type fmap would be if it only -worked on our new pairs, it would be: -

- -
-fmap :: (a -> b) -> Pair c a -> Pair c b
-
- -

-Again, we said instance Functor (Pair c) where and so -Pair c took the place of the f -in the type class definition for Functor: -

- -
-class Functor f where
-    fmap :: (a -> b) -> f a -> f b
-
- -

-So now, if we convert a tuple into a Pair b a, we can -use fmap over it and the function will be mapped over -the first component: -

- -
-ghci> getPair $ fmap (*100) (Pair (2,3))
-(200,3)
-ghci> getPair $ fmap reverse (Pair ("london calling", 3))
-("gnillac nodnol",3)
-
- -

On newtype laziness

- -

-We mentioned that newtype is usually faster than data. The -only thing that can be done with newtype is turning an existing type -into a new type, so internally, Haskell can represent the values of types -defined with newtype just like the original ones, only it has to keep in -mind that their types are now distinct. This fact means that not only is -newtype faster, it's also lazier. Let's take a look at what this means. -

- -

-Like we've said before, Haskell is lazy by default, which means that only -when we try to actually print the results of our functions will any computation -take place. Furthermore, only those computations that are necessary for our -function to tell us the result will get carried out. The undefined -value in Haskell represents an erroneous computation. If we try to evaluate it -(that is, force Haskell to actually compute it) by printing it to -the terminal, Haskell will throw a hissy fit (technically referred to as an -exception): -

- -
-ghci> undefined
-*** Exception: Prelude.undefined
-
- -

-However, if we make a list that has some undefined -values in it but request only the head of the list, which is not undefined, -everything will go smoothly because Haskell doesn't really need to evaluate any -other elements in a list if we only want to see what the first element is: -

- -
-ghci> head [3,4,5,undefined,2,undefined]
-3
-
- -

-Now consider the following type: -

- -
-data CoolBool = CoolBool { getCoolBool :: Bool }
-
- -

-It's your run-of-the-mill algebraic data type that was defined with the -data keyword. It has one value constructor, which has one field whose -type is Bool. Let's make a function that pattern -matches on a CoolBool and returns the value -"hello" regardless of whether the Bool inside -the CoolBool was True or -False: -

- -
-helloMe :: CoolBool -> String
-helloMe (CoolBool _) = "hello"
-
- -

-Instead of applying this function to a normal CoolBool, -let's throw it a curveball and apply it to undefined! -

- -
-ghci> helloMe undefined
-"*** Exception: Prelude.undefined
-
- -

-Yikes! An exception! Now why did this exception happen? Types defined with -the data keyword can have multiple value constructors (even though -CoolBool only has one). So in order to see if the -value given to our function conforms to the (CoolBool _) -pattern, Haskell has to evaluate the value just enough to see which value -constructor was used when we made the value. And when we try to evaluate an -undefined value, even a little, an exception is -thrown. -

- -

-Instead of using the data keyword for CoolBool, -let's try using newtype: -

- -
-newtype CoolBool = CoolBool { getCoolBool :: Bool }
-
- -

-We don't have to change our helloMe function, because -the pattern matching syntax is the same if you use newtype or -data to define your type. Let's do the same thing here and apply -helloMe to an undefined -value: -

- -
-ghci> helloMe undefined
-"hello"
-
- -top of the morning to ya!!! - -

-It worked! Hmmm, why is that? Well, like we've said, when we use newtype, -Haskell can internally represent the values of the new type in the same way as the original -values. It doesn't have to add another box around them, it just has to be aware -of the values being of different types. And because Haskell knows that types -made with the newtype keyword can only have one constructor, it doesn't -have to evaluate the value passed to the function to make sure that it conforms -to the (CoolBool _) pattern because newtype -types can only have one possible value constructor and one field! -

- -

-This difference in behavior may seem trivial, but it's actually pretty -important because it helps us realize that even though types defined with -data and newtype behave similarly from the programmer's point of -view because they both have value constructors and fields, they are actually two -different mechanisms. Whereas data can be used to make your own types -from scratch, newtype is for making a completely new type out of an -existing type. Pattern matching on newtype values isn't like taking -something out of a box (like it is with data), it's more about making a -direct conversion from one type to another. -

- -

type vs. newtype vs. data

- -

-At this point, you may be a bit confused about what exactly the difference -between type, data and newtype is, so let's refresh our -memory a bit. -

- -

-The type keyword is for making type synonyms. What that means is that -we just give another name to an already existing type so that the type is easier -to refer to. Say we did the following: -

- -
-type IntList = [Int]
-
- -

-All this does is to allow us to refer to the [Int] -type as IntList. They can be used interchangeably. -We don't get an IntList value constructor or anything like that. -Because [Int] and IntList -are only two ways to refer to the same type, it doesn't matter which name we use -in our type annotations: -

- -
-ghci> ([1,2,3] :: IntList) ++ ([1,2,3] :: [Int])
-[1,2,3,1,2,3]
-
- -

-We use type synonyms when we want to make our type signatures more -descriptive by giving types names that tell us something about their purpose in -the context of the functions where they're being used. For instance, when we -used an association list of type [(String,String)] to -represent a phone book, we gave it the type synonym of -PhoneBook so that the type signatures of our -functions were easier to read. -

- -

-The newtype keyword is for taking existing types and wrapping them in -new types, mostly so that it's easier to make them instances of certain type -classes. When we use newtype to wrap an existing type, the type that we -get is separate from the original type. If we make the following newtype: -

- -
-newtype CharList = CharList { getCharList :: [Char] }
-
- -

-We can't use ++ to put together a -CharList and a list of type -[Char]. We can't even use -++ to put together two CharLists, -because ++ works only on lists and the -CharList type isn't a list, even though it could be -said that it contains one. We can, however, convert two CharLists to -lists, ++ them and then convert that back to a CharList. -

- -

-When we use record syntax in our newtype declarations, we get functions -for converting between the new type and the original type: namely the value -constructor of our newtype and the function for extracting the value -in its field. The new type also isn't automatically made an instance of the -type classes that the original type belongs to, so we have to derive or -manually write them. -

- -

-In practice, you can think of newtype declarations as data declarations -that can only have one constructor and one field. If you catch yourself writing -such a data declaration, consider using newtype. -

- -

-The data keyword is for making your own data types and with them, you -can go hog wild. They can have as many constructors and fields as you wish and -can be used to implement any algebraic data type by yourself. Everything from -lists and Maybe-like types to trees. -

- -

-If you just want your type signatures to look cleaner and be more -descriptive, you probably want type synonyms. If you want to take an existing -type and wrap it in a new type in order to make it an instance of a type class, -chances are you're looking for a newtype. And if you want to make -something completely new, odds are good that you're looking for the data -keyword. -

- -

Monoids

- -wow this is pretty much the gayest pirate ship
-ever - -

-Type classes in Haskell are used to present an interface for types that have -some behavior in common. We started out with simple type classes like Eq, which is for types whose values can be equated, and -Ord, which is for things that can be put in an order -and then moved on to more interesting ones, like Functor and Applicative. - -

-When we make a type, we think about which behaviors it supports, i.e. what it can -act like and then based on that we decide which type classes to make it an -instance of. If it makes sense for values of our type to be equated, we make it -an instance of the Eq type class. If we see that our -type is some kind of functor, we make it an instance of -Functor, and so on. -

- -

-Now consider the following: * is a function that -takes two numbers and multiplies them. If we multiply some number with a 1, the result is always equal to that number. It doesn't -matter if we do 1 * x or x * -1, the result is always x. Similarly, ++ is -also a function which takes two things and returns a third. Only instead of -multiplying numbers, it takes two lists and concatenates them. And much like -*, it also has a certain value which doesn't change -the other one when used with ++. That value is the -empty list: []. -

- -
-ghci> 4 * 1
-4
-ghci> 1 * 9
-9
-ghci> [1,2,3] ++ []
-[1,2,3]
-ghci> [] ++ [0.5, 2.5]
-[0.5,2.5]
-
- -

-It seems that both * together with 1 -and ++ along with [] share -some common properties: -

- - - -

-There's another thing that these two operations have in common that may not be -as obvious as our previous observations: when we have three or more values and -we want to use the binary function to reduce them to a single result, the order -in which we apply the binary function to the values doesn't matter. It doesn't -matter if we do (3 * 4) * 5 or 3 -* (4 * 5). Either way, the result is 60. The -same goes for ++: -

- -
-ghci> (3 * 2) * (8 * 5)
-240
-ghci> 3 * (2 * (8 * 5))
-240
-ghci> "la" ++ ("di" ++ "da")
-"ladida"
-ghci> ("la" ++ "di") ++ "da"
-"ladida"
-
- -

-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. 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: -

- -
-class Monoid m where
-    mempty :: m
-    mappend :: m -> m -> m
-    mconcat :: [m] -> m
-    mconcat = foldr mappend mempty
-
- -woof dee do!!! - -

-The Monoid type class is defined in -import Data.Monoid. 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. -

- -

-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. mempty represents the -identity value for a particular monoid. -

- -

-Next up, we have mappend, which, as you've probably -guessed, is the binary function. It takes two values of the same type and -returns a value of that type as well. It's worth noting that the decision to -name -mappend as it's named was kind of unfortunate, -because it implies that we're appending two things in some way. While ++ does take two lists and append one to the other, * doesn't really do any appending, it just multiplies two -numbers together. When we meet other instances of Monoid, we'll see that most of them don't append values -either, so avoid thinking in terms of appending and just think in terms of -mappend being a binary function that takes two monoid -values and returns a third. -

- -

-The last function in this type class definition is mconcat. -It takes a list of monoid values and reduces them to a single value by doing -mappend between the list's elements. It has a default -implementation, which just takes mempty as a starting -value and folds the list from the right with mappend. -Because the default implementation is fine for most instances, we won't concern -ourselves with mconcat too much from now on. When -making a type an instance of Monoid, it suffices to -just implement mempty and mappend. -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. 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. Otherwise, what's the -point? That's why when making instances, we have to make sure they follow these -laws: -

- - - -

-The first two state that mempty has to act as the -identity with respect to mappend and the third says -that mappend has to be associative i.e. that it the -order in which we use mappend to reduce several -monoid values into one doesn't matter. 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: -

- -
-instance Monoid [a] where
-    mempty = []
-    mappend = (++)
-
- -

-Lists are an instance of the Monoid type class -regardless of the type of the elements they hold. -Notice that we wrote instance Monoid [a] and not -instance Monoid [], because Monoid -requires a concrete type for an instance. -

- -

-Giving this a test run, we encounter no surprises: -

- -
-ghci> [1,2,3] `mappend` [4,5,6]
-[1,2,3,4,5,6]
-ghci> ("one" `mappend` "two") `mappend` "tree"
-"onetwotree"
-ghci> "one" `mappend` ("two" `mappend` "tree")
-"onetwotree"
-ghci> "one" `mappend` "two" `mappend` "tree"
-"onetwotree"
-ghci> "pang" `mappend` mempty
-"pang"
-ghci> mconcat [[1,2],[3,6],[9]]
-[1,2,3,6,9]
-ghci> mempty :: [a]
-[]
-
- -smug as hell - -

-Notice that in the last line, we had to write an explicit type annotation, -because if we just did mempty, GHCi wouldn't know -which instance to use, so we had to say we want the list instance. We were able -to use the general type of [a] (as opposed to -specifying [Int] or [String]) -because the empty list can act as if it contains any type. -

- -

-Because mconcat has a default implementation, we get -it for free when we make something an instance of Monoid. -In the case of the list, mconcat turns out to be just -concat. It takes a list of lists and flattens it, -because that's the equivalent of doing ++ between all -the adjacent lists in a list. -

- -

-The monoid laws do indeed hold for the list instance. When we have several lists -and we mappend (or ++) -them together, it doesn't matter which ones we do first, because they're just -joined at the ends anyway. Also, the empty list acts as the identity so all is well. -Notice that monoids don't require that a `mappend` b -be equal to b `mappend` a. In the case of the list, -they clearly aren't: -

- -
-ghci> "one" `mappend` "two"
-"onetwo"
-ghci> "two" `mappend` "one"
-"twoone"
-
- -

-And that's okay. The fact that for multiplication 3 * 5 and -5 * 3 are the same is just a property of -multiplication, but it doesn't hold for all (and indeed, most) monoids. -

- -

Product and Sum

- -

-We already examined one way for numbers to be considered monoids. Just have the -binary function be * and the identity value -1. It turns out that that's not the only way for -numbers to be monoids. Another way is to have the binary function be -+ and the identity value 0: -

- -
-ghci> 0 + 4
-4
-ghci> 5 + 0
-5
-ghci> (1 + 3) + 5
-9
-ghci> 1 + (3 + 5)
-9
-
- -

-The monoid laws hold, because if you add 0 to any number, the result is that -number. And addition is also associative, so we get no problems there. So now -that there are two equally valid ways for numbers to be monoids, which way do -choose? Well, we don't have to. Remember, when there are several ways for some -type to be an instance of the same type class, we can wrap that type in a -newtype and then make the new type an instance of the -type class in a different way. We can have our cake and eat it too. -

- -

-The Data.Monoid module exports two types for this, -namely Product and Sum. -Product is defined like this: -

- -
-newtype Product a =  Product { getProduct :: a }
-    deriving (Eq, Ord, Read, Show, Bounded)
-
- -

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

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

-mempty is just 1 wrapped -in a Product constructor. mappend -pattern matches on the Product constructor, -multiplies the two numbers and then wraps the resulting number back. As you can -see, there's a Num a class constraint. So this means that -Product a is an instance of Monoid for all -a's that are already an instance of Num. -To use Producta a as a monoid, we have to do some -newtype wrapping and unwrapping: -

- -
-ghci> getProduct $ Product 3 `mappend` Product 9
-27
-ghci> getProduct $ Product 3 `mappend` mempty
-3
-ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2
-24
-ghci> getProduct . mconcat . map Product $ [3,4,2]
-24
-
- -

-This is nice as a showcase of the Monoid type class, -but no one in their right mind would use this way of multiplying numbers instead -of just writing 3 * 9 and 3 * 1. -But a bit later, we'll see how these Monoid instances -that may seem trivial at this time can come in handy. -

- -

-Sum is defined like Product and the -instance is similar as well. We use it in the same way: -

- -
-ghci> getSum $ Sum 2 `mappend` Sum 9
-11
-ghci> getSum $ mempty `mappend` Sum 3
-3
-ghci> getSum . mconcat . map Sum $ [1,2,3]
-6
-
- -

Any and All

- -

-Another type which can act like a monoid in two distinct but equally valid ways -is Bool. The first way is to have the or -function || act as the binary function along with -False as the identity value. The way or works -in logic is that if any of its two parameters is True, -it returns True, otherwise it returns -False. So if we use False -as the identity value, it will return False when -or-ed with False and True -when or-ed with True. The Any -newtype constructor is an instance of Monoid -in this fashion. It's defined like this: -

- -
-newtype Any = Any { getAny :: Bool }
-    deriving (Eq, Ord, Read, Show, Bounded)
-
- -

-Its instance looks goes like so: -

- -
-instance Monoid Any where
-        mempty = Any False
-        Any x `mappend` Any y = Any (x || y)
-
- -

-The reason it's called Any is because -x `mappend` y will be True -if any one of those two is True. Even -if three or more Any wrapped Bools -are mappended together, the result will hold -True if any of them are True: -

- -
-ghci> getAny $ Any True `mappend` Any False
-True
-ghci> getAny $ mempty `mappend` Any True
-True
-ghci> getAny . mconcat . map Any $ [False, False, False, True]
-True
-ghci> getAny $ mempty `mappend` mempty
-False
-
- -

-The other way for Bool to be an instance of -Monoid is to kind of do the opposite: have && -be the binary function and then make True -the identity value. Logical and will return True only -if both of its parameters are True. This is the newtype -declaration, nothing fancy: -

- -
-newtype All = All { getAll :: Bool }
-        deriving (Eq, Ord, Read, Show, Bounded)
-
- -

-And this is the instance: -

- -
-instance Monoid All where
-        mempty = All True
-        All x `mappend` All y = All (x && y)
-
- -

-When we mappend values of the -All type, the result will be -True only if all the values -used in the mappend operations are -True: -

- -
-ghci> getAll $ mempty `mappend` All True
-True
-ghci> getAll $ mempty `mappend` All False
-False
-ghci> getAll . mconcat . map All $ [True, True, True]
-True
-ghci> getAll . mconcat . map All $ [True, True, False]
-False
-
- -

-Just like with multiplication and addition, we usually explicitly state the -binary functions instead of wrapping them in newtypes and then using -mappend and mempty. -mconcat seems useful for Any -and All, but usually it's easier to use the -or and and functions, -which take lists of Bools and return -True if any of them are True or -if all of them are True, respectively. -

- -

The Ordering monoid

- -

-Hey, remember the Ordering type? It's used as the -result when comparing things and it can have three values: LT, -EQ and GT, which stand for -less than, equal and greater than respectively: -

- -
-ghci> 1 `compare` 2
-LT
-ghci> 2 `compare` 2
-EQ
-ghci> 3 `compare` 2
-GT
-
- -

-With lists, numbers and boolean values, finding monoids was just a matter of -looking at already existing commonly used functions and seeing if they exhibit -some sort of monoid behavior. 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: -

- -
-instance Monoid Ordering where
-    mempty = EQ
-    LT `mappend` _ = LT
-    EQ `mappend` y = y
-    GT `mappend` _ = GT
-
- -did anyone ORDER pizza?!?! I can't BEAR these puns! - -

-The instance is set up like this: when we mappend two -Ordering values, the one on the left is kept, unless -the value on the left is EQ, in which case the right -one is the result. The identity is EQ. At first, this -may seem kind of arbitrary, but it actually resembles the way we alphabetically -compare words. We compare the first two letters and if they differ, we can -already decide which word would go first in a dictionary. However, if the first two -letters are equal, then we move on to comparing the next pair of letters and -repeat the process. -

- -

-For instance, if we were to alphabetically compare the words -"ox" and "on", we'd first -compare the first two letters of each word, see that they are equal and then -move on to comparing the second letter of each word. We see that 'x' is alphabetically greater than 'n', and so we know how the words compare. To gain some -intuition for EQ being the identity, we can notice -that if we were to cram the same letter in the same position in both words, it -wouldn't change their alphabetical ordering. "oix" is -still alphabetically greater than and "oin". -

- -

-It's important to note that in the Monoid instance -for Ordering, x `mappend` y -doesn't equal y `mappend` x. Because the first -parameter is kept unless it's EQ, LT `mappend` GT will result in LT, whereas GT `mappend` LT will -result in GT: -

- -
-ghci> LT `mappend` GT
-LT
-ghci> GT `mappend` LT
-GT
-ghci> mempty `mappend` LT
-LT
-ghci> mempty `mappend` GT
-GT
-
- -

-OK, so how is this monoid useful? Let's say you were writing a function that -takes two strings, compares their lengths, and returns an Ordering. But if the strings are of the same length, then -instead of returning EQ right away, we want to -compare them alphabetically. One way to write this would be like so: -

- -
-lengthCompare :: String -> String -> Ordering
-lengthCompare x y = let a = length x `compare` length y
-                        b = x `compare` y
-                    in  if a == EQ then b else a
-
- -

-We name the result of comparing the lengths a and the -result of the alphabetical comparison b and then if -it turns out that the lengths were equal, we return their alphabetical ordering. -

- -

-But by employing our understanding of how Ordering is -a monoid, we can rewrite this function in a much simpler manner: -

- -
-import Data.Monoid
-
-lengthCompare :: String -> String -> Ordering
-lengthCompare x y = (length x `compare` length y) `mappend`
-                    (x `compare` y)
-
- -

-We can try this out: -

- -
-ghci> lengthCompare "zen" "ants"
-LT
-ghci> lengthCompare "zen" "ant"
-GT
-
- -

-Remember, when we use mappend, its left parameter is -always kept unless it's EQ, in which case the right -one is kept. That's why we put the comparison that we consider to be the first, -more important criterion as the first parameter. If we wanted to expand this -function to also compare for the number of vowels and set this to be the second -most important criterion for comparison, we'd just modify it like this: -

- -
-import Data.Monoid
-
-lengthCompare :: String -> String -> Ordering
-lengthCompare x y = (length x `compare` length y) `mappend`
-                    (vowels x `compare` vowels y) `mappend`
-                    (x `compare` y)
-    where vowels = length . filter (`elem` "aeiou")
-
- -

-We made a helper function, which takes a string and tells us how many vowels it -has by first filtering it only for letters that are in the string "aeiou" and then applying length -to that. -

- -
-ghci> lengthCompare "zen" "anna"
-LT
-ghci> lengthCompare "zen" "ana"
-LT
-ghci> lengthCompare "zen" "ann"
-GT
-
- -

-Very cool. Here, we see how in the first example the lengths are found to be -different and so LT is returned, because the length -of "zen" is less than the length of -"anna". In the second example, the lengths are the -same, but the second string has more vowels, so LT is -returned again. In the third example, they both have the same length and the -same number of vowels, so they're compared alphabetically and -"zen" wins. -

- -

-The Ordering monoid is very cool because it allows us -to easily compare things by many different criteria and put those criteria in an -order themselves, ranging from the most important to the least. -

- -

Maybe the monoid

- -

-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 mappend in such a way that it uses the -mappend 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 -mappending is Nothing, we -keep the other value. Here's the instance declaration: -

- -
-instance Monoid a => Monoid (Maybe a) where
-    mempty = Nothing
-    Nothing `mappend` m = m
-    m `mappend` Nothing = m
-    Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
-
- -

-Notice the class constraint. It says that Maybe a is -an instance of Monoid only if a is an instance of Monoid. -If we mappend something with a Nothing, -the result is that something. If we mappend two Just values, the contents of the Justs get -mappended 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. -

- -
-ghci> Nothing `mappend` Just "andy"
-Just "andy"
-ghci> Just LT `mappend` Nothing
-Just LT
-ghci> Just (Sum 3) `mappend` 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. -

- -

-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 mappend -are Just values. But if we don't know if the contents -are monoids, we can't use mappend 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: -

- -
-newtype First a = First { getFirst :: Maybe a }
-    deriving (Eq, Ord, Read, Show)
-
- -

-We take a Maybe a and we wrap it with a -newtype. The Monoid instance is as follows: -

- -
-instance Monoid (First a) where
-    mempty = First Nothing
-    First (Just x) `mappend` _ = First (Just x)
-    First Nothing `mappend` x = x
-
- -

-Just like we said. mempty is just a -Nothing wrapped with the First -newtype constructor. If mappend's first -parameter is a Just value, we ignore the second one. -If the first one is a Nothing, then we present the -second parameter as a result, regardless of whether it's a Just -or a Nothing: -

- -
-ghci> getFirst $ First (Just 'a') `mappend` First (Just 'b')
-Just 'a'
-ghci> getFirst $ First Nothing `mappend` First (Just 'b')
-Just 'b'
-ghci> getFirst $ First (Just 'a') `mappend` First Nothing
-Just 'a'
-
- -

-First is useful when we have a bunch of Maybe values -and we just want to know if any of them is a Just. -The mconcat function comes in handy: -

- -
-ghci> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10]
-Just 9
-
- -

-If we want a monoid on Maybe a such that the second -parameter is kept if both parameters of mappend are -Just values, Data.Monoid -provides a Last a type, which works like First a, only the last non-Nothing -value is kept when mappending and using mconcat: -

- -
-ghci> getLast . mconcat . map Last $ [Nothing, Just 9, Just 10]
-Just 10
-ghci> getLast $ Last (Just "one") `mappend` Last (Just "two")
-Just "two"
-
- -

Using monoids to fold data structures

- -

-One of the more interesting ways to put monoids to work is to make them help us -define folds over various data structures. So far, we've only done folds over -lists, but lists aren't the only data structure that can be folded over. We can -define folds over almost any data structure. Trees especially lend themselves well -to folding. -

- -

-Because there are so many data structures that work nicely with folds, the -Foldable type class was introduced. Much like -Functor is for things that can be mapped over, -Foldable is for things that can be folded up! It can -be found in Data.Foldable and because it exports -functions whose names clash with the ones from the Prelude, -it's best imported qualified (and served with basil): -

- -
-import qualified Foldable as F
-
- -

-To save ourselves precious keystrokes, we've chosen to import it qualified as -F. Alright, so what are some of the functions that -this type class defines? Well, among them are foldr, -foldl, foldr1 and foldl1. -Huh? But we already know these functions, what's so new about this? Let's -compare the types of Foldable's foldr and -the foldr from the Prelude -to see how they differ: -

- -
-ghci> :t foldr
-foldr :: (a -> b -> b) -> b -> [a] -> b
-ghci> :t F.foldr
-F.foldr :: (F.Foldable t) => (a -> b -> b) -> b -> t a -> b
-
- -

-Ah! So whereas foldr takes a list and folds it up, -the foldr from -Data.Foldable accepts any type that can be folded up, -not just lists! As expected, both foldr functions do -the same for lists: -

- -
-ghci> foldr (*) 1 [1,2,3]
-6
-ghci> F.foldr (*) 1 [1,2,3]
-6
-
- -

-Okay then, what are some other data structures that support folds? Well, there's -the Maybe we all know and love! -

- -
-ghci> F.foldl (+) 2 (Just 9)
-11
-ghci> F.foldr (||) False (Just True)
-True
-
- -

-But folding over a Maybe value isn't terribly -interesting, because when it comes to folding, it just acts like a list with one -element if it's a Just value and as an empty list if -it's Nothing. So let's examine a data structure -that's a little more complex then. -

- -

-Remember the tree data structure from the Making Our -Own Types and Typeclasses chapter? We defined it like this: -

- -
-data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
-
- -

-We said that a tree is either an empty tree that doesn't hold any values or it's a node that -holds one value and also two other trees. After defining it, we made it an -instance of Functor and with that we gained the -ability to fmap functions over it. Now, we're going -to make it an instance of Foldable so that we get the -ability to fold it up. One way to make a type constructor an instance of -Foldable is to just directly implement foldr for it. -But another, often much easier way, is to implement the foldMap function, -which is also a part of the Foldable type class. The -foldMap function has the following type: -

- -
-foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
-
- -

-Its first parameter is a function that takes a value of the type that -our foldable structure contains (denoted here with a) -and returns a monoid value. Its second parameter is a foldable structure that -contains values of type a. It maps that function over -the foldable structure, thus producing a foldable structure that contains -monoid values. Then, by doing mappend between those -monoid values, it joins them all into a single monoid value. This function -may sound kind of odd at the moment, but we'll see that it's very easy to -implement. What's also cool is that implementing this function is all it takes for -our type to be made an instance of Foldable. So if we -just implement foldMap for some type, we get -foldr and foldl on that -type for free! -

- -

-This is how we make Tree an instance of -Foldable: -

- -
-instance F.Foldable Tree where
-    foldMap f Empty = mempty
-    foldMap f (Node x l r) = F.foldMap f l `mappend`
-                             f x           `mappend`
-                             F.foldMap f r
-
- -find the visual pun or whatever - -

-We think like this: if we are provided with a function that takes an element of -our tree and returns a monoid value, how do we reduce our whole tree down to one -single monoid value? When we were doing fmap over our tree, -we applied the function that we were mapping to a node and then we recursively -mapped the function over the left subtree as well as the right one. Here, we're -tasked with not only mapping a function, but with also joining up the results -into a single monoid value by using mappend. First we -consider the case of the empty tree — a sad and lonely tree that has no -values or subtrees. It doesn't hold any value that we can give to our monoid-making -function, so we just say that if our tree is empty, the monoid value it becomes -is mempty. -

- -

-The case of a non-empty node is a bit more interesting. It contains two -subtrees as well as a value. In this case, we recursively foldMap the -same function f over the left and the right -subtrees. Remember, our foldMap results in a single -monoid value. We also apply our function f to the -value in the node. Now we have three monoid values (two from our subtrees and -one from applying f to the value in the node) and we -just have to bang them together into a single value. For this purpose we use -mappend, and naturally the left subtree comes first, -then the node value and then the right subtree. -

- -

-Notice that we didn't have to provide the function that takes a value and -returns a monoid value. We receive that function as a parameter to foldMap -and all we have to decide is where to apply that function and how to join up -the resulting monoids from it. -

- -

-Now that we have a Foldable instance for our tree -type, we get foldr and foldl for free! -Consider this tree: -

- -
-testTree = Node 5
-            (Node 3
-                (Node 1 Empty Empty)
-                (Node 6 Empty Empty)
-            )
-            (Node 9
-                (Node 8 Empty Empty)
-                (Node 10 Empty Empty)
-            )
-
- -

-It has 5 at its root and then its left node has -3 with 1 on the left and -6 on the right. The root's right node has a 9 -and then an 8 to its left and a 10 on the far right side. With a Foldable instance, -we can do all of the folds that we can do on lists: -

- -
-ghci> F.foldl (+) 0 testTree
-42
-ghci> F.foldl (*) 1 testTree
-64800
-
- -

-And also, foldMap isn't only useful for making new instances of -Foldable; it comes in handy for reducing our -structure to a single monoid value. For instance, if we want to know if any number in our -tree is equal to 3, we can do this: -

- -
-ghci> getAny $ F.foldMap (\x -> Any $ x == 3) testTree
-True
-
- -

-Here, \x -> Any $ x == 3 is a function that takes -a number and returns a monoid value, namely a Bool -wrapped in Any. foldMap -applies this function to every element in our tree and then reduces the -resulting monoids into a single monoid with mappend. -If we do this: -

- -
-ghci> getAny $ F.foldMap (\x -> Any $ x > 15) testTree
-False
-
- -

-All of the nodes in our tree would hold the value Any -False after having the function in the lambda applied to them. But to end -up True, -mappend for Any has to -have at least one True value as a parameter. That's -why the final result is False, which makes sense -because no value in our tree is greater than 15. -

- -

-We can also easily turn our tree into a list by doing a -foldMap with the \x -> [x] -function. By first projecting that function onto our tree, each element becomes -a singleton list. The mappend action that takes place -between all those singleton list results in a single list that holds all of the -elements that are in our tree: -

- -
-ghci> F.foldMap (\x -> [x]) testTree
-[1,3,6,5,8,9,10]
-
- -

-What's cool is that all of these trick aren't limited to trees, they work on any -instance of Foldable. -

- -
- - - - -
- - - - diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html deleted file mode 100644 index f3ef887..0000000 --- a/docs/higher-order-functions.html +++ /dev/null @@ -1,503 +0,0 @@ - - - -Higher Order Functions - Learn You a Haskell for Great Good! - - - - - - - - - - - -
-
-
- -
-

Higher order functions

-sun -

Haskell functions can take functions as parameters and return functions as return values. A function that does either of those is called a higher order function. Higher order functions aren't just a part of the Haskell experience, they pretty much are the Haskell experience. It turns out that if you want to define computations by defining what stuff is instead of defining steps that change some state and maybe looping them, higher order functions are indispensable. They're a really powerful way of solving problems and thinking about programs.

-

Curried functions

-

Every function in Haskell officially only takes one parameter. So how is it possible that we defined and used several functions that take more than one parameter so far? Well, it's a clever trick! All the functions that accepted several parameters so far have been curried functions. What does that mean? You'll understand it best on an example. Let's take our good friend, the max function. It looks like it takes two parameters and returns the one that's bigger. Doing max 4 5 first creates a function that takes a parameter and returns either 4 or that parameter, depending on which is bigger. Then, 5 is applied to that function and that function produces our desired result. That sounds like a mouthful but it's actually a really cool concept. The following two calls are equivalent:

-
-ghci> max 4 5
-5
-ghci> (max 4) 5
-5
-
-haskell curry -

Putting a space between two things is simply function application. The space is sort of like an operator and it has the highest precedence. Let's examine the type of max. It's max :: (Ord a) => a -> a -> a. That can also be written as max :: (Ord a) => a -> (a -> a). That could be read as: max takes an a and returns (that's the ->) a function that takes an a and returns an a. That's why the return type and the parameters of functions are all simply separated with arrows.

-

So how is that beneficial to us? Simply speaking, if we call a function with too few parameters, we get back a partially applied function, meaning a function that takes as many parameters as we left out. Using partial application (calling functions with too few parameters, if you will) is a neat way to create functions on the fly so we can pass them to another function or to seed them with some data.

-

Take a look at this offensively simple function:

-
-multThree :: (Num a) => a -> a -> a -> a
-multThree x y z = x * y * z
-
-

What really happens when we do multThree 3 5 9 or ((multThree 3) 5) 9? First, 3 is applied to multThree, because they're separated by a space. That creates a function that takes one parameter and returns a function. So then 5 is applied to that, which creates a function that will take a parameter and multiply it by 15. 9 is applied to that function and the result is 135 or something. Remember that this function's type could also be written as multThree :: (Num a) => a -> (a -> (a -> a)). The thing before the -> is the parameter that a function takes and the thing after it is what it returns. So our function takes an a and returns a function of type (Num a) => a -> (a -> a). Similarly, this function takes an a and returns a function of type (Num a) => a -> a. And this function, finally, just takes an a and returns an a. Take a look at this:

-
-ghci> let multTwoWithNine = multThree 9
-ghci> multTwoWithNine 2 3
-54
-ghci> let multWithEighteen = multTwoWithNine 2
-ghci> multWithEighteen 10
-180
-
-

By calling functions with too few parameters, so to speak, we're creating new functions on the fly. What if we wanted to create a function that takes a number and compares it to 100? We could do something like this:

-
-compareWithHundred :: (Num a, Ord a) => a -> Ordering
-compareWithHundred x = compare 100 x
-
-

If we call it with 99, it returns a GT. Simple stuff. Notice that the x is on the right-hand side on both sides of the equation. Now let's think about what compare 100 returns. It returns a function that takes a number and compares it with 100. Wow! Isn't that the function we wanted? We can rewrite this as:

-
-compareWithHundred :: (Num a, Ord a) => a -> Ordering
-compareWithHundred = compare 100
-
-

The type declaration stays the same, because compare 100 returns a function. Compare has a type of (Ord a) => a -> (a -> Ordering) and calling it with 100 returns a (Num a, Ord a) => a -> Ordering. The additional class constraint sneaks up there because 100 is also part of the Num typeclass.

-
Yo! Make sure you really understand how curried functions and partial application work because they're really important!

Infix functions can also be partially applied by using sections. To section an infix function, simply surround it with parentheses and only supply a parameter on one side. That creates a function that takes one parameter and then applies it to the side that's missing an operand. An insultingly trivial function:

-
-divideByTen :: (Floating a) => a -> a
-divideByTen = (/10)
-
-

Calling, say, divideByTen 200 is equivalent to doing 200 / 10, as is doing (/10) 200. A function that checks if a character supplied to it is an uppercase letter:

-
-isUpperAlphanum :: Char -> Bool
-isUpperAlphanum = (`elem` ['A'..'Z'])
-
-

The only special thing about sections is using -. From the definition of sections, (-4) would result in a function that takes a number and subtracts 4 from it. However, for convenience, (-4) means minus four. So if you want to make a function that subtracts 4 from the number it gets as a parameter, partially apply the subtract function like so: (subtract 4).

-

What happens if we try to just do multThree 3 4 in GHCI instead of binding it to a name with a let or passing it to another function?

-
-ghci> multThree 3 4
-<interactive>:1:0:
-    No instance for (Show (t -> t))
-      arising from a use of `print' at <interactive>:1:0-12
-    Possible fix: add an instance declaration for (Show (t -> t))
-    In the expression: print it
-    In a 'do' expression: print it
-
-

GHCI is telling us that the expression produced a function of type a -> a but it doesn't know how to print it to the screen. Functions aren't instances of the Show typeclass, so we can't get a neat string representation of a function. When we do, say, 1 + 1 at the GHCI prompt, it first calculates that to 2 and then calls show on 2 to get a textual representation of that number. And the textual representation of 2 is just the string "2", which then gets printed to our screen.

-

Some higher-orderism is in order

-

Functions can take functions as parameters and also return functions. To illustrate this, we're going to make a function that takes a function and then applies it twice to something!

-
-applyTwice :: (a -> a) -> a -> a
-applyTwice f x = f (f x)
-
-rocktopus -

First of all, notice the type declaration. Before, we didn't need parentheses because -> is naturally right-associative. However, here, they're mandatory. They indicate that the first parameter is a function that takes something and returns that same thing. The second parameter is something of that type also and the return value is also of the same type. We could read this type declaration in the curried way, but to save ourselves a headache, we'll just say that this function takes two parameters and returns one thing. The first parameter is a function (of type a -> a) and the second is that same a. The function can also be Int -> Int or String -> String or whatever. But then, the second parameter to also has to be of that type.

-
Note: From now on, we'll say that functions take several parameters despite each function actually taking only one parameter and returning partially applied functions until we reach a function that returns a solid value. So for simplicity's sake, we'll say that a -> a -> a takes two parameters, even though we know what's really going on under the hood.
-

The body of the function is pretty simple. We just use the parameter f as a function, applying x to it by separating them with a space and then applying the result to f again. Anyway, playing around with the function:

-
-ghci> applyTwice (+3) 10
-16
-ghci> applyTwice (++ " HAHA") "HEY"
-"HEY HAHA HAHA"
-ghci> applyTwice ("HAHA " ++) "HEY"
-"HAHA HAHA HEY"
-ghci> applyTwice (multThree 2 2) 9
-144
-ghci> applyTwice (3:) [1]
-[3,3,1]
-
-

The awesomeness and usefulness of partial application is evident. If our function requires us to pass it a function that takes only one parameter, we can just partially apply a function to the point where it takes only one parameter and then pass it.

-

Now we're going to use higher order programming to implement a really useful function that's in the standard library. It's called zipWith. It takes a function and two lists as parameters and then joins the two lists by applying the function between corresponding elements. Here's how we'll implement it:

-
-zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
-zipWith' _ [] _ = []
-zipWith' _ _ [] = []
-zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
-
-

Look at the type declaration. The first parameter is a function that takes two things and produces a third thing. They don't have to be of the same type, but they can. The second and third parameter are lists. The result is also a list. The first has to be a list of a's, because the joining function takes a's as its first argument. The second has to be a list of b's, because the second parameter of the joining function is of type b. The result is a list of c's. If the type declaration of a function says it accepts an a -> b -> c function as a parameter, it will also accept an a -> a -> a function, but not the other way around! Remember that when you're making functions, especially higher order ones, and you're unsure of the type, you can just try omitting the type declaration and then checking what Haskell infers it to be by using :t.

-

The action in the function is pretty similar to the normal zip. The edge conditions are the same, only there's an extra argument, the joining function, but that argument doesn't matter in the edge conditions, so we just use a _ for it. And function body at the last pattern is also similar to zip, only it doesn't do (x,y), but f x y. A single higher order function can be used for a multitude of different tasks if it's general enough. Here's a little demonstration of all the different things our zipWith' function can do:

-
-ghci> zipWith' (+) [4,2,5,6] [2,6,2,3]
-[6,8,7,9]
-ghci> zipWith' max [6,3,2,1] [7,3,1,5]
-[7,3,2,5]
-ghci> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
-["foo fighters","bar hoppers","baz aldrin"]
-ghci> zipWith' (*) (replicate 5 2) [1..]
-[2,4,6,8,10]
-ghci> zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]
-[[3,4,6],[9,20,30],[10,12,12]]
-
-

-As you can see, a single higher order function can be used in very versatile ways. Imperative programming usually uses stuff like for loops, while loops, setting something to a variable, checking its state, etc. to achieve some behavior and then wrap it around an interface, like a function. Functional programming uses higher order functions to abstract away common patterns, like examining two lists in pairs and doing something with those pairs or getting a set of solutions and eliminating the ones you don't need. -

-

We'll implement another function that's already in the standard library, called flip. Flip simply takes a function and returns a function that is like our original function, only the first two arguments are flipped. We can implement it like so:

-
-flip' :: (a -> b -> c) -> (b -> a -> c)
-flip' f = g
-    where g x y = f y x
-
-

Reading the type declaration, we say that it takes a function that takes an a and a b and returns a function that takes a b and an a. But because functions are curried by default, the second pair of parentheses is really unnecessary, because -> is right associative by default. (a -> b -> c) -> (b -> a -> c) is the same as (a -> b -> c) -> (b -> (a -> c)), which is the same as (a -> b -> c) -> b -> a -> c. We wrote that g x y = f y x. If that's true, then f y x = g x y must also hold, right? Keeping that in mind, we can define this function in an even simpler manner.

-
-flip' :: (a -> b -> c) -> b -> a -> c
-flip' f y x = f x y
-
-

Here, we take advantage of the fact that functions are curried. When we call flip' f without the parameters y and x, it will return an f that takes those two parameters but calls them flipped. Even though flipped functions are usually passed to other functions, we can take advantage of currying when making higher-order functions by thinking ahead and writing what their end result would be if they were called fully applied.

-
-ghci> flip' zip [1,2,3,4,5] "hello"
-[('h',1),('e',2),('l',3),('l',4),('o',5)]
-ghci> zipWith (flip' div) [2,2..] [10,8,6,4,2]
-[5,4,3,2,1]
-
-

Maps and filters

-

map takes a function and a list and applies that function to every element in the list, producing a new list. Let's see what its type signature is and how it's defined.

-
-map :: (a -> b) -> [a] -> [b]
-map _ [] = []
-map f (x:xs) = f x : map f xs
-
-

The type signature says that it takes a function that takes an a and returns a b, a list of a's and returns a list of b's. It's interesting that just by looking at a function's type signature, you can sometimes tell what it does. map is one of those really versatile higher-order functions that can be used in millions of different ways. Here it is in action:

-
-ghci> map (+3) [1,5,3,1,6]
-[4,8,6,4,9]
-ghci> map (++ "!") ["BIFF", "BANG", "POW"]
-["BIFF!","BANG!","POW!"]
-ghci> map (replicate 3) [3..6]
-[[3,3,3],[4,4,4],[5,5,5],[6,6,6]]
-ghci> map (map (^2)) [[1,2],[3,4,5,6],[7,8]]
-[[1,4],[9,16,25,36],[49,64]]
-ghci> map fst [(1,2),(3,5),(6,3),(2,6),(2,5)]
-[1,3,6,2,2]
-
-

You've probably noticed that each of these could be achieved with a list comprehension. map (+3) [1,5,3,1,6] is the same as writing [x+3 | x <- [1,5,3,1,6]]. However, using map is much more readable for cases where you only apply some function to the elements of a list, especially once you're dealing with maps of maps and then the whole thing with a lot of brackets can get a bit messy.

-

filter is a function that takes a predicate (a predicate is a function that tells whether something is true or not, so in our case, a function that returns a boolean value) and a list and then returns the list of elements that satisfy the predicate. The type signature and implementation go like this:

-
-filter :: (a -> Bool) -> [a] -> [a]
-filter _ [] = []
-filter p (x:xs)
-    | p x       = x : filter p xs
-    | otherwise = filter p xs
-
-

Pretty simple stuff. If p x evaluates to True, the element gets included in the new list. If it doesn't, it stays out. Some usage examples:

-
-ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]
-[5,6,4]
-ghci> filter (==3) [1,2,3,4,5]
-[3]
-ghci> filter even [1..10]
-[2,4,6,8,10]
-ghci> let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]]
-[[1,2,3],[3,4,5],[2,2]]
-ghci> filter (`elem` ['a'..'z']) "u LaUgH aT mE BeCaUsE I aM diFfeRent"
-"uagameasadifeent"
-ghci> filter (`elem` ['A'..'Z']) "i Laugh At you Because u R All The Same"
-"LABRATS"
-
-

All of this could also be achieved with list comprehensions by the use of predicates. There's no set rule for when to use map and filter versus using list comprehension, you just have to decide what's more readable depending on the code and the context. The filter equivalent of applying several predicates in a list comprehension is either filtering something several times or joining the predicates with the logical && function.

-

Remember our quicksort function from the previous chapter? We used list comprehensions to filter out the list elements that are smaller than (or equal to) and larger than the pivot. We can achieve the same functionality in a more readable way by using filter:

-
-quicksort :: (Ord a) => [a] -> [a]
-quicksort [] = []
-quicksort (x:xs) =
-    let smallerSorted = quicksort (filter (<=x) xs)
-        biggerSorted = quicksort (filter (>x) xs)
-    in  smallerSorted ++ [x] ++ biggerSorted
-
-map -

Mapping and filtering is the bread and butter of every functional programmer's toolbox. Uh. It doesn't matter if you do it with the map and filter functions or list comprehensions. Recall how we solved the problem of finding right triangles with a certain circumference. With imperative programming, we would have solved it by nesting three loops and then testing if the current combination satisfies a right triangle and if it has the right perimeter. If that's the case, we would have printed it out to the screen or something. In functional programming, that pattern is achieved with mapping and filtering. You make a function that takes a value and produces some result. We map that function over a list of values and then we filter the resulting list out for the results that satisfy our search. Thanks to Haskell's laziness, even if you map something over a list several times and filter it several times, it will only pass over the list once.

-

Let's find the largest number under 100,000 that's divisible by 3829. To do that, we'll just filter a set of possibilities in which we know the solution lies.

-
-largestDivisible :: (Integral a) => a
-largestDivisible = head (filter p [100000,99999..])
-    where p x = x `mod` 3829 == 0
-
-

We first make a list of all numbers lower than 100,000, descending. Then we filter it by our predicate and because the numbers are sorted in a descending manner, the largest number that satisfies our predicate is the first element of the filtered list. We didn't even need to use a finite list for our starting set. That's laziness in action again. Because we only end up using the head of the filtered list, it doesn't matter if the filtered list is finite or infinite. The evaluation stops when the first adequate solution is found.

-

Next up, we're going to find the sum of all odd squares that are smaller than 10,000. But first, because we'll be using it in our solution, we're going to introduce the takeWhile function. It takes a predicate and a list and then goes from the beginning of the list and returns its elements while the predicate holds true. Once an element is found for which the predicate doesn't hold, it stops. If we wanted to get the first word of the string "elephants know how to party", we could do takeWhile (/=' ') "elephants know how to party" and it would return "elephants". Okay. The sum of all odd squares that are smaller than 10,000. First, we'll begin by mapping the (^2) function to the infinite list [1..]. Then we filter them so we only get the odd ones. And then, we'll take elements from that list while they are smaller than 10,000. Finally, we'll get the sum of that list. We don't even have to define a function for that, we can do it in one line in GHCI:

-
-ghci> sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
-166650
-
-

Awesome! We start with some initial data (the infinite list of all natural numbers) and then we map over it, filter it and cut it until it suits our needs and then we just sum it up. We could have also written this using list comprehensions:

-
-ghci> sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)])
-166650
-
-

It's a matter of taste as to which one you find prettier. Again, Haskell's property of laziness is what makes this possible. We can map over and filter an infinite list, because it won't actually map and filter it right away, it'll delay those actions. Only when we force Haskell to show us the sum does the sum function say to the takeWhile that it needs those numbers. takeWhile forces the filtering and mapping to occur, but only until a number greater than or equal to 10,000 is encountered.

-

For our next problem, we'll be dealing with Collatz sequences. We take a natural number. If that number is even, we divide it by two. If it's odd, we multiply it by 3 and then add 1 to that. We take the resulting number and apply the same thing to it, which produces a new number and so on. In essence, we get a chain of numbers. It is thought that for all starting numbers, the chains finish at the number 1. So if we take the starting number 13, we get this sequence: 13, 40, 20, 10, 5, 16, 8, 4, 2, 1. 13*3 + 1 equals 40. 40 divided by 2 is 20, etc. We see that the chain has 10 terms.

-

Now what we want to know is this: for all starting numbers between 1 and 100, how many chains have a length greater than 15? First off, we'll write a function that produces a chain:

-
-chain :: (Integral a) => a -> [a]
-chain 1 = [1]
-chain n
-    | even n =  n:chain (n `div` 2)
-    | odd n  =  n:chain (n*3 + 1)
-
-

Because the chains end at 1, that's the edge case. This is a pretty standard recursive function.

-
-ghci> chain 10
-[10,5,16,8,4,2,1]
-ghci> chain 1
-[1]
-ghci> chain 30
-[30,15,46,23,70,35,106,53,160,80,40,20,10,5,16,8,4,2,1]
-
-

Yay! It seems to be working correctly. And now, the function that tells us the answer to our question:

-
-numLongChains :: Int
-numLongChains = length (filter isLong (map chain [1..100]))
-    where isLong xs = length xs > 15
-
-

We map the chain function to [1..100] to get a list of chains, which are themselves represented as lists. Then, we filter them by a predicate that just checks whether a list's length is longer than 15. Once we've done the filtering, we see how many chains are left in the resulting list.

-
Note: This function has a type of numLongChains :: Int because length returns an Int instead of a Num a for historical reasons. If we wanted to return a more general Num a, we could have used fromIntegral on the resulting length.
-

Using map, we can also do stuff like map (*) [0..], if not for any other reason than to illustrate how currying works and how (partially applied) functions are real values that you can pass around to other functions or put into lists (you just can't turn them to strings). So far, we've only mapped functions that take one parameter over lists, like map (*2) [0..] to get a list of type (Num a) => [a], but we can also do map (*) [0..] without a problem. What happens here is that the number in the list is applied to the function *, which has a type of (Num a) => a -> a -> a. Applying only one parameter to a function that takes two parameters returns a function that takes one parameter. If we map * over the list [0..], we get back a list of functions that only take one parameter, so (Num a) => [a -> a]. map (*) [0..] produces a list like the one we'd get by writing [(0*),(1*),(2*),(3*),(4*),(5*)...

-
-ghci> let listOfFuncs = map (*) [0..]
-ghci> (listOfFuncs !! 4) 5
-20
-
-

Getting the element with the index 4 from our list returns a function that's equivalent to (4*). And then, we just apply 5 to that function. So that's like writing (4*) 5 or just 4 * 5.

-

Lambdas

-lambda -

Lambdas are basically anonymous functions that are used because we need some functions only once. Normally, we make a lambda with the sole purpose of passing it to a higher-order function. To make a lambda, we write a \ (because it kind of looks like the greek letter lambda if you squint hard enough) and then we write the parameters, separated by spaces. After that comes a -> and then the function body. We usually surround them by parentheses, because otherwise they extend all the way to the right.

-

If you look about 5 inches up, you'll see that we used a where binding in our numLongChains function to make the isLong function for the sole purpose of passing it to filter. Well, instead of doing that, we can use a lambda:

-
-numLongChains :: Int
-numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))
-
-

Lambdas are expressions, that's why we can just pass them like that. The expression (\xs -> length xs > 15) returns a function that tells us whether the length of the list passed to it is greater than 15.

-lamb -

People who are not well acquainted with how currying and partial application works often use lambdas where they don't need to. For instance, the expressions map (+3) [1,6,3,2] and map (\x -> x + 3) [1,6,3,2] are equivalent since both (+3) and (\x -> x + 3) are functions that take a number and add 3 to it. Needless to say, making a lambda in this case is stupid since using partial application is much more readable.

-

Like normal functions, lambdas can take any number of parameters:

-
-ghci> zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]
-[153.0,61.5,31.0,15.75,6.6]
-
-

And like normal functions, you can pattern match in lambdas. The only difference is that you can't define several patterns for one parameter, like making a [] and a (x:xs) pattern for the same parameter and then having values fall through. If a pattern matching fails in a lambda, a runtime error occurs, so be careful when pattern matching in lambdas!

-
-ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
-[3,8,9,8,7]
-
-

Lambdas are normally surrounded by parentheses unless we mean for them to extend all the way to the right. Here's something interesting: due to the way functions are curried by default, these two are equivalent:

-
-addThree :: (Num a) => a -> a -> a -> a
-addThree x y z = x + y + z
-
-
-addThree :: (Num a) => a -> a -> a -> a
-addThree = \x -> \y -> \z -> x + y + z
-
-

If we define a function like this, it's obvious why the type declaration is what it is. There are three ->'s in both the type declaration and the equation. But of course, the first way to write functions is far more readable, the second one is pretty much a gimmick to illustrate currying.

-

However, there are times when using this notation is cool. I think that the flip function is the most readable when defined like so:

-
-flip' :: (a -> b -> c) -> b -> a -> c
-flip' f = \x y -> f y x
-
-

Even though that's the same as writing flip' f x y = f y x, we make it obvious that this will be used for producing a new function most of the time. The most common use case with flip is calling it with just the function parameter and then passing the resulting function on to a map or a filter. So use lambdas in this way when you want to make it explicit that your function is mainly meant to be partially applied and passed on to a function as a parameter.

-

Only folds and horses

-folded bird -

Back when we were dealing with recursion, we noticed a theme throughout many of the recursive functions that operated on lists. Usually, we'd have an edge case for the empty list. We'd introduce the x:xs pattern and then we'd do some action that involves a single element and the rest of the list. It turns out this is a very common pattern, so a couple of very useful functions were introduced to encapsulate it. These functions are called folds. They're sort of like the map function, only they reduce the list to some single value.

-

A fold takes a binary function, a starting value (I like to call it the accumulator) and a list to fold up. The binary function itself takes two parameters. The binary function is called with the accumulator and the first (or last) element and produces a new accumulator. Then, the binary function is called again with the new accumulator and the now new first (or last) element, and so on. Once we've walked over the whole list, only the accumulator remains, which is what we've reduced the list to.

-

First let's take a look at the foldl function, also called the left fold. It folds the list up from the left side. The binary function is applied between the starting value and the head of the list. That produces a new accumulator value and the binary function is called with that value and the next element, etc.

-

Let's implement sum again, only this time, we'll use a fold instead of explicit recursion.

-
-sum' :: (Num a) => [a] -> a
-sum' xs = foldl (\acc x -> acc + x) 0 xs
-
-

Testing, one two three:

-
-ghci> sum' [3,5,2,1]
-11
-
-foldl -

Let's take an in-depth look into how this fold happens. \acc x -> acc + x is the binary function. 0 is the starting value and xs is the list to be folded up. Now first, 0 is used as the acc parameter to the binary function and 3 is used as the x (or the current element) parameter. 0 + 3 produces a 3 and it becomes the new accumulator value, so to speak. Next up, 3 is used as the accumulator value and 5 as the current element and 8 becomes the new accumulator value. Moving forward, 8 is the accumulator value, 2 is the current element, the new accumulator value is 10. Finally, that 10 is used as the accumulator value and 1 as the current element, producing an 11. Congratulations, you've done a fold!

-

This professional diagram on the left illustrates how a fold happens, step by step (day by day!). The greenish brown number is the accumulator value. You can see how the list is sort of consumed up from the left side by the accumulator. Om nom nom nom! If we take into account that functions are curried, we can write this implementation ever more succinctly, like so:

-
-sum' :: (Num a) => [a] -> a
-sum' = foldl (+) 0
-
-

The lambda function (\acc x -> acc + x) is the same as (+). We can omit the xs as the parameter because calling foldl (+) 0 will return a function that takes a list. Generally, if you have a function like foo a = bar b a, you can rewrite it as foo = bar b, because of currying.

-

Anyhoo, let's implement another function with a left fold before moving on to right folds. I'm sure you all know that elem checks whether a value is part of a list so I won't go into that again (whoops, just did!). Let's implement it with a left fold.

-
-elem' :: (Eq a) => a -> [a] -> Bool
-elem' y ys = foldl (\acc x -> if x == y then True else acc) False ys
-
-

Well, well, well, what do we have here? The starting value and accumulator here is a boolean value. The type of the accumulator value and the end result is always the same when dealing with folds. Remember that if you ever don't know what to use as a starting value, it'll give you some idea. We start off with False. It makes sense to use False as a starting value. We assume it isn't there. Also, if we call a fold on an empty list, the result will just be the starting value. Then we check the current element is the element we're looking for. If it is, we set the accumulator to True. If it's not, we just leave the accumulator unchanged. If it was False before, it stays that way because this current element is not it. If it was True, we leave it at that.

-

The right fold, foldr works in a similar way to the left fold, only the accumulator eats up the values from the right. Also, the left fold's binary function has the accumulator as the first parameter and the current value as the second one (so \acc x -> ...), the right fold's binary function has the current value as the first parameter and the accumulator as the second one (so \x acc -> ...). It kind of makes sense that the right fold has the accumulator on the right, because it folds from the right side.

-

The accumulator value (and hence, the result) of a fold can be of any type. It can be a number, a boolean or even a new list. We'll be implementing the map function with a right fold. The accumulator will be a list, we'll be accumulating the mapped list element by element. From that, it's obvious that the starting element will be an empty list.

-
-map' :: (a -> b) -> [a] -> [b]
-map' f xs = foldr (\x acc -> f x : acc) [] xs
-
-

If we're mapping (+3) to [1,2,3], we approach the list from the right side. We take the last element, which is 3 and apply the function to it, which ends up being 6. Then, we prepend it to the accumulator, which is []. 6:[] is [6] and that's now the accumulator. We apply (+3) to 2, that's 5 and we prepend (:) it to the accumulator, so the accumulator is now [5,6]. We apply (+3) to 1 and prepend that to the accumulator and so the end value is [4,5,6].

-

Of course, we could have implemented this function with a left fold too. It would be map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs, but the thing is that the ++ function is much more expensive than :, so we usually use right folds when we're building up new lists from a list.

-fold this up! -

If you reverse a list, you can do a right fold on it just like you would have done a left fold and vice versa. Sometimes you don't even have to do that. The sum function can be implemented pretty much the same with a left and right fold. One big difference is that right folds work on infinite lists, whereas left ones don't! To put it plainly, if you take an infinite list at some point and you fold it up from the right, you'll eventually reach the beginning of the list. However, if you take an infinite list at a point and you try to fold it up from the left, you'll never reach an end!

-

Folds can be used to implement any function where you traverse a list once, element by element, and then return something based on that. Whenever you want to traverse a list to return something, chances are you want a fold. That's why folds are, along with maps and filters, one of the most useful types of functions in functional programming.

-

The foldl1 and foldr1 functions work much like foldl and foldr, only you don't need to provide them with an explicit starting value. They assume the first (or last) element of the list to be the starting value and then start the fold with the element next to it. With that in mind, the sum function can be implemented like so: sum = foldl1 (+). Because they depend on the lists they fold up having at least one element, they cause runtime errors if called with empty lists. foldl and foldr, on the other hand, work fine with empty lists. When making a fold, think about how it acts on an empty list. If the function doesn't make sense when given an empty list, you can probably use a foldl1 or foldr1 to implement it.

-

Just to show you how powerful folds are, we're going to implement a bunch of standard library functions by using folds:

-
-maximum' :: (Ord a) => [a] -> a
-maximum' = foldr1 (\x acc -> if x > acc then x else acc)
-
-reverse' :: [a] -> [a]
-reverse' = foldl (\acc x -> x : acc) []
-
-product' :: (Num a) => [a] -> a
-product' = foldr1 (*)
-
-filter' :: (a -> Bool) -> [a] -> [a]
-filter' p = foldr (\x acc -> if p x then x : acc else acc) []
-
-head' :: [a] -> a
-head' = foldr1 (\x _ -> x)
-
-last' :: [a] -> a
-last' = foldl1 (\_ x -> x)
-
-

head is better implemented by pattern matching, but this just goes to show, you can still achieve it by using folds. Our reverse' definition is pretty clever, I think. We take a starting value of an empty list and then approach our list from the left and just prepend to our accumulator. In the end, we build up a reversed list. \acc x -> x : acc kind of looks like the : function, only the parameters are flipped. That's why we could have also written our reverse as foldl (flip (:)) [].

-

Another way to picture right and left folds is like this: say we have a right fold and the binary function is f and the starting value is z. If we're right folding over the list [3,4,5,6], we're essentially doing this: f 3 (f 4 (f 5 (f 6 z))). f is called with the last element in the list and the accumulator, that value is given as the accumulator to the next to last value and so on. If we take f to be + and the starting accumulator value to be 0, that's 3 + (4 + (5 + (6 + 0))). Or if we write + as a prefix function, that's (+) 3 ((+) 4 ((+) 5 ((+) 6 0))). Similarly, doing a left fold over that list with g as the binary function and z as the accumulator is the equivalent of g (g (g (g z 3) 4) 5) 6. If we use flip (:) as the binary function and [] as the accumulator (so we're reversing the list), then that's the equivalent of flip (:) (flip (:) (flip (:) (flip (:) [] 3) 4) 5) 6. And sure enough, if you evaluate that expression, you get [6,5,4,3].

-

scanl and scanr are like foldl and foldr, only they report all the intermediate accumulator states in the form of a list. There are also scanl1 and scanr1, which are analogous to foldl1 and foldr1.

-
-ghci> scanl (+) 0 [3,5,2,1]
-[0,3,8,10,11]
-ghci> scanr (+) 0 [3,5,2,1]
-[11,8,3,1,0]
-ghci> scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]
-[3,4,5,5,7,9,9,9]
-ghci> scanl (flip (:)) [] [3,2,1]
-[[],[3],[2,3],[1,2,3]]
-
-

When using a scanl, the final result will be in the last element of the resulting list while a scanr will place the result in the head.

-

Scans are used to monitor the progression of a function that can be implemented as a fold. Let's answer us this question: How many elements does it take for the sum of the square roots of all natural numbers to exceed 1000? To get the square roots of all natural numbers, we just do map sqrt [1..]. Now, to get the sum, we could do a fold, but because we're interested in how the sum progresses, we're going to do a scan. Once we've done the scan, we just see how many sums are under 1000. The first sum in the scanlist will be 1, normally. The second will be 1 plus the square root of 2. The third will be that plus the square root of 3. If there are X sums under 1000, then it takes X+1 elements for the sum to exceed 1000.

-
-sqrtSums :: Int
-sqrtSums = length (takeWhile (<1000) (scanl1 (+) (map sqrt [1..]))) + 1
-
-
-ghci> sqrtSums
-131
-ghci> sum (map sqrt [1..131])
-1005.0942035344083
-ghci> sum (map sqrt [1..130])
-993.6486803921487
-
-

We use takeWhile here instead of filter because filter doesn't work on infinite lists. Even though we know the list is ascending, filter doesn't, so we use takeWhile to cut the scanlist off at the first occurrence of a sum greater than 1000.

-

Function application with $

-

Alright, next up, we'll take a look at the $ function, also called function application. First of all, let's check out how it's defined:

-
-($) :: (a -> b) -> a -> b
-f $ x = f x
-
-dollar -

What the heck? What is this useless operator? It's just function application! Well, almost, but not quite! Whereas normal function application (putting a space between two things) has a really high precedence, the $ function has the lowest precedence. Function application with a space is left-associative (so f a b c is the same as ((f a) b) c)), function application with $ is right-associative.

-

That's all very well, but how does this help us? Most of the time, it's a convenience function so that we don't have to write so many parentheses. Consider the expression sum (map sqrt [1..130]). Because $ has such a low precedence, we can rewrite that expression as sum $ map sqrt [1..130], saving ourselves precious keystrokes! When a $ is encountered, the expression on its right is applied as the parameter to the function on its left. How about sqrt 3 + 4 + 9? This adds together 9, 4 and the square root of 3. If we want to get the square root of 3 + 4 + 9, we'd have to write sqrt (3 + 4 + 9) or if we use $ we can write it as sqrt $ 3 + 4 + 9 because $ has the lowest precedence of any operator. That's why you can imagine a $ being sort of the equivalent of writing an opening parenthesis and then writing a closing one on the far right side of the expression.

-

How about sum (filter (> 10) (map (*2) [2..10]))? Well, because $ is right-associative, f (g (z x)) is equal to f $ g $ z x. And so, we can rewrite sum (filter (> 10) (map (*2) [2..10])) as sum $ filter (> 10) $ map (*2) [2..10].

-

But apart from getting rid of parentheses, $ means that function application can be treated just like another function. That way, we can, for instance, map function application over a list of functions.

-
-ghci> map ($ 3) [(4+), (10*), (^2), sqrt]
-[7.0,30.0,9.0,1.7320508075688772]
-
-

Function composition

-

In mathematics, function composition is defined like this:  (f . g)(x) = f(g(x)), meaning that composing two functions produces a new function that, when called with a parameter, say, x is the equivalent of calling g with the parameter x and then calling the f with that result.

-

In Haskell, function composition is pretty much the same thing. We do function composition with the . function, which is defined like so:

-
-(.) :: (b -> c) -> (a -> b) -> a -> c
-f . g = \x -> f (g x)
-
-notes -

Mind the type declaration. f must take as its parameter a value that has the same type as g's return value. So the resulting function takes a parameter of the same type that g takes and returns a value of the same type that f returns. The expression negate . (* 3) returns a function that takes a number, multiplies it by 3 and then negates it.

-

One of the uses for function composition is making functions on the fly to pass to other functions. Sure, can use lambdas for that, but many times, function composition is clearer and more concise. Say we have a list of numbers and we want to turn them all into negative numbers. One way to do that would be to get each number's absolute value and then negate it, like so:

-
-ghci> map (\x -> negate (abs x)) [5,-3,-6,7,-3,2,-19,24]
-[-5,-3,-6,-7,-3,-2,-19,-24]
-
-

Notice the lambda and how it looks like the result function composition. Using function composition, we can rewrite that as:

-
-ghci> map (negate . abs) [5,-3,-6,7,-3,2,-19,24]
-[-5,-3,-6,-7,-3,-2,-19,-24]
-
-

Fabulous! Function composition is right-associative, so we can compose many functions at a time. The expression f (g (z x)) is equivalent to (f . g . z) x. With that in mind, we can turn

-
-ghci> map (\xs -> negate (sum (tail xs))) [[1..5],[3..6],[1..7]]
-[-14,-15,-27]
-
-

into

-
-ghci> map (negate . sum . tail) [[1..5],[3..6],[1..7]]
-[-14,-15,-27]
-
-

But what about functions that take several parameters? Well, if we want to use them in function composition, we usually have to partially apply them just so much that each function takes just one parameter. sum (replicate 5 (max 6.7 8.9)) can be rewritten as (sum . replicate 5 . max 6.7) 8.9 or as sum . replicate 5 . max 6.7 $ 8.9. What goes on in here is this: a function that takes what max 6.7 takes and applies replicate 5 to it is created. Then, a function that takes the result of that and does a sum of it is created. Finally, that function is called with 8.9. But normally, you just read that as: apply 8.9 to max 6.7, then apply replicate 5 to that and then apply sum to that. If you want to rewrite an expression with a lot of parentheses by using function composition, you can start by putting the last parameter of the innermost function after a $ and then just composing all the other function calls, writing them without their last parameter and putting dots between them. If you have replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] [4,5,6,7,8]))), you can write it as replicate 100 . product . map (*3) . zipWith max [1,2,3,4,5] $ [4,5,6,7,8]. If the expression ends with three parentheses, chances are that if you translate it into function composition, it'll have three composition operators.

-

Another common use of function composition is defining functions in the so-called point free style (also called the pointless style). Take for example this function that we wrote earlier:

-
-sum' :: (Num a) => [a] -> a
-sum' xs = foldl (+) 0 xs
-
-

The xs is exposed on both sides, right? Because of currying, we can omit the xs on both sides, because calling foldl (+) 0 creates a function that takes a list. Writing the function as sum' = foldl (+) 0 is called writing it in point free style. How would we write this in point free style?

-
-fn x = ceiling (negate (tan (cos (max 50 x))))
-
-

We can't just get rid of the x on both right sides. The x in the function body has parentheses after it. cos (max 50) wouldn't make sense. You can't get the cosine of a function. What we can do is express fn as a composition of functions.

-
-fn = ceiling . negate . tan . cos . max 50
-
-

Excellent! Many times, a point free style is more readable and concise, because it makes you think about functions and what kind of functions composing them results in instead of thinking about data and how it's shuffled around. You can take simple functions and use composition as glue to form more complex functions. However, many times, writing a function in point free style can be less readable if a function is too complex. That's why making long chains of function composition is discouraged, although I plead guilty of sometimes being too composition-happy. The preferred style is to use let bindings to give labels to intermediary results or split the problem into sub-problems and then put it together so that the function makes sense to someone reading it instead of just making a huge composition chain.

-

In the section about maps and filters, we solved a problem of finding the sum of all odd squares that are smaller than 10,000. Here's what the solution looks like when put into a function.

-
-oddSquareSum :: Integer
-oddSquareSum = sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
-
-

Being such a fan of function composition, I would have probably written that like this:

-
-oddSquareSum :: Integer
-oddSquareSum = sum . takeWhile (<10000) . filter odd . map (^2) $ [1..]
-
-

However, if there was a chance of someone else reading that code, I would have written it like this:

-
-oddSquareSum :: Integer
-oddSquareSum =
-    let oddSquares = filter odd $ map (^2) [1..]
-        belowLimit = takeWhile (<10000) oddSquares
-    in  sum belowLimit
-
-

It wouldn't win any code golf competition, but someone reading the function will probably find it easier to read than a composition chain.

-
- -
-
- - - - -
- - - - diff --git a/docs/index.html b/docs/index.html index 1da96f2..57b75cf 100644 --- a/docs/index.html +++ b/docs/index.html @@ -48,7 +48,7 @@ - +