From 5c6089f2272525168e1b6e44f21aa71267663989 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Wed, 23 Nov 2022 18:43:15 +0900 Subject: [PATCH 01/27] Fix unmatched paragraph tags --- docs/chapters.html | 1 + docs/for-a-few-monads-more.html | 2 ++ docs/functionally-solving-problems.html | 2 +- .../functors-applicative-functors-and-monoids.html | 3 ++- docs/input-and-output.html | 12 ++++++------ docs/modules.html | 14 +++++++------- docs/recursion.html | 2 +- docs/starting-out.html | 4 ++-- docs/types-and-typeclasses.html | 2 +- 9 files changed, 23 insertions(+), 19 deletions(-) diff --git a/docs/chapters.html b/docs/chapters.html index ced6cf2..5c12302 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -164,6 +164,7 @@

Learn You a Haskell for Great Good!

+

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/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index 64d4a4e..6adf1e1 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -48,6 +48,7 @@

For a Few Monads More

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 @@ -927,6 +928,7 @@

Comparing Performance

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
diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html
index 0655212..514b652 100644
--- a/docs/functionally-solving-problems.html
+++ b/docs/functionally-solving-problems.html
@@ -182,7 +182,7 @@ 

Functionally Solving Problems

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

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:

diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html
index 79ffd19..9c06112 100644
--- a/docs/functors-applicative-functors-and-monoids.html
+++ b/docs/functors-applicative-functors-and-monoids.html
@@ -251,7 +251,7 @@ 

Functors, Applicative Functors and Monoids

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

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 a 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
@@ -1074,6 +1074,7 @@ 

type vs. newtype vs. < 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 diff --git a/docs/input-and-output.html b/docs/input-and-output.html index 4285ddd..d1a79da 100644 --- a/docs/input-and-output.html +++ b/docs/input-and-output.html @@ -148,7 +148,7 @@

Input and Output

To get a feel of what it does, you can run it before we go over the code.

Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

First, let's take a look at the reverseWords function. It's just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we'd have to write something like reverseWords st = unwords (map reverse (words st)).

-

What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn't, the I/O action under the else is performed. That's why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action. +

What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn't, the I/O action under the else is performed. That's why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

Let's first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

         else (do
@@ -673,10 +673,10 @@ 

Input and Output

removeFile "todo.txt" renameFile tempName "todo.txt"
-

At first, we just open todo.txt in read mode and bind its handle to handle. +

At first, we just open todo.txt in read mode and bind its handle to handle.

Next up, we use a function that we haven't met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it's better practice to use openTempFile so you know you're probably not overwriting anything.

-

The reason we didn't use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows +

The reason we didn't use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

We ask the user which one they want to delete and wait for them to enter a number. Let's say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

Remember the delete and !! functions from Data.List. !! returns an element from a list with some index and delete deletes the first occurrence of an element in a list and returns a new list without that occurrence. (todoTasks !! number) (number is now 1) returns "Dust the dog". We bind todoTasks without the first occurrence of "Dust the dog" to newTodoItems and then join that into a single string with unlines before writing it to the temporary file that we opened. The old file is now unchanged and the temporary file contains all the lines that the old one does, except the one we deleted.

@@ -880,7 +880,7 @@

Input and Output

It's not very useful as a random number function because it will always return 4, even though I can assure you that the 4 is completely random, because I used a die to determine it.

How do other languages make seemingly random numbers? Well, they take various info from your computer, like the current time, how much and where you moved your mouse and what kind of noises you made behind your computer and based on that, give a number that looks really random. The combination of those factors (that randomness) is probably different in any given moment in time, so you get a different random number.

-

Ah. So in Haskell, we can make a random number then if we make a function that takes as its parameter that randomness and based on that returns some number (or other data type).

+

Ah. So in Haskell, we can make a random number then if we make a function that takes as its parameter that randomness and based on that returns some number (or other data type).

Enter the System.Random module. It has all the functions that satisfy our need for randomness. Let's just dive into one of the functions it exports then, namely random. Here's its type: random :: (RandomGen g, Random a) => g -> (a, g). Whoa! Some new typeclasses in this type declaration up in here! The RandomGen typeclass is for types that can act as sources of randomness. The Random typeclass is for things that can take on random values. A boolean value can take on a random value, namely True or False. A number can also take up a plethora of different random values. Can a function take on a random value? I don't think so, probably not! If we try to translate the type declaration of random to English, we get something like: it takes a random generator (that's our source of randomness) and returns a random value and a new random generator. Why does it also return a new generator as well as a random value? Well, we'll see in a moment.

To use our random function, we have to get our hands on one of those random generators. The System.Random module exports a cool type, namely StdGen that is an instance of the RandomGen typeclass. We can either make a StdGen manually or we can tell the system to give us one based on a multitude of sort of random stuff.

To manually make a random generator, use the mkStdGen function. It has a type of mkStdGen :: Int -> StdGen. It takes an integer and based on that, gives us a random generator. Okay then, let's try using random and mkStdGen in tandem to get a (hardly random) number.

@@ -964,7 +964,7 @@

Input and Output

in (value:restOfList, finalGen)

Again, a recursive definition. We say that if we want 0 numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

-

What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it's kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

+

What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it's kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

 ghci> randomR (1,6) (mkStdGen 359353)
 (6,1494289578 40692)
@@ -1269,7 +1269,7 @@ 

Input and Output

| otherwise = ioError e

Where notifyCops and freeSomeSpace are some I/O actions that you define. Be sure to re-throw exceptions if they don't match any of your criteria, otherwise you're causing your program to fail silently in some cases where it shouldn't.

-

System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can't print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn't know about anything else. A function depends only on the parameters it was called with. That's why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it's kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let's modify our program to print out the file path that's responsible for the exception occurring. +

System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can't print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn't know about anything else. A function depends only on the parameters it was called with. That's why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it's kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let's modify our program to print out the file path that's responsible for the exception occurring.

 import System.Environment
 import System.IO
diff --git a/docs/modules.html b/docs/modules.html
index 1d032bc..9acbb9e 100644
--- a/docs/modules.html
+++ b/docs/modules.html
@@ -57,7 +57,7 @@ 

Modules

 import Data.List (nub, sort)
 
-

You can also choose to import all of the functions of a module except a few select ones. That's often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that's called nub and we want to import all the functions from Data.List except the nub function: +

You can also choose to import all of the functions of a module except a few select ones. That's often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that's called nub and we want to import all the functions from Data.List except the nub function:

 import Data.List hiding (nub)
 
@@ -276,7 +276,7 @@

Modules

find :: (a -> Bool) -> [a] -> Maybe a

Notice the type of find. Its result is Maybe a. That's kind of like having the type of [a], only a value of the type Maybe can contain either no elements or one element, whereas a list can contain no elements, one element or several elements.

-

Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we'd be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we'd get back a Nothing. But if there was a valid answer in that list, we'd get, say, Just (1001.4,2008,9,4). +

Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we'd be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we'd get back a Nothing. But if there was a valid answer in that list, we'd get, say, Just (1001.4,2008,9,4).

elemIndex is kind of like elem, only it doesn't return a boolean value. It maybe returns the index of the element we're looking for. If that element isn't in our list, it returns a Nothing.

 ghci> :t elemIndex
@@ -372,7 +372,7 @@ 

Modules

[1,3,4,4,4,1]

The 4 is inserted right after the 3 and before the 5 in the first example and in between the 3 and 4 in the second example.

-If we use insert to insert into a sorted list, the resulting list will be kept sorted.

+

If we use insert to insert into a sorted list, the resulting list will be kept sorted.

 ghci> insert 4 [1,2,3,5,6,7]
 [1,2,3,4,5,6,7]
@@ -389,12 +389,12 @@ 

Modules

ghci> groupBy (\x y -> (x > 0) == (y > 0)) values [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
-

From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they're both negative or if they're both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this: +

From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they're both negative or if they're both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

 on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
 f `on` g = \x y -> f (g x) (g y)
 
-

So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do: +

So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do:

 ghci> groupBy ((==) `on` (> 0)) values
 [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
@@ -440,7 +440,7 @@ 

Modules

False

Kewl. In case you don't remember, all takes a predicate and a list and returns True only if that predicate holds for every element in the list.

-

We can also use isSpace to simulate the Data.List function words. +

We can also use isSpace to simulate the Data.List function words.

 ghci> words "hey folks its me"
 ["hey","folks","its","me"]
@@ -572,7 +572,7 @@ 

Modules

legomap

Works like a charm! If we have the friend's phone number, we Just get the number, otherwise we get Nothing.

We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they're internally implemented with trees) and also it provides a lot of utility functions. From now on, we'll say we're working with maps instead of association lists.

-

Because Data.Map exports functions that clash with the Prelude and Data.List ones, we'll do a qualified import. +

Because Data.Map exports functions that clash with the Prelude and Data.List ones, we'll do a qualified import.

 import qualified Data.Map as Map
 
diff --git a/docs/recursion.html b/docs/recursion.html index 1eb9b6b..7bb44bf 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -134,7 +134,7 @@

Recursion

quicksort

An element that is in place and won't move anymore is represented in orange. If you read them from left to right, you'll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They're in green here. We chose the head because it's easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

Thinking recursively

-

We did quite a bit of recursion so far and as you've probably noticed, there's a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn't matter if it's a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera ...

+

We did quite a bit of recursion so far and as you've probably noticed, there's a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn't matter if it's a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera ...

brain

Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn't make sense. When dealing with lists, the edge case is most often the empty list. If you're dealing with trees, the edge case is usually a node that doesn't have any children.

It's similar when you're dealing with numbers recursively. Usually it has to do with some number and the function applied to that number modified. We did the factorial function earlier and it's the product of a number and the factorial of that number minus one. Such a recursive application doesn't make sense with zero, because factorials are defined only for positive integers. Often the edge case value turns out to be an identity. The identity for multiplication is 1 because if you multiply something by 1, you get that something back. Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the identity for addition. In quicksort, the edge case is the empty list and the identity is also the empty list, because if you add an empty list to a list, you just get the original list back.

diff --git a/docs/starting-out.html b/docs/starting-out.html index e417440..1a8a625 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -306,7 +306,7 @@

Starting Out

 ghci> head []
 *** Exception: Prelude.head: empty list
-

Oh my! It all blows up in our face! If there's no monster, it doesn't have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it's always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list. +

Oh my! It all blows up in our face! If there's no monster, it doesn't have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it's always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list.

length takes a list and returns its length, obviously.

 ghci> length [5,4,3,2,1]
@@ -431,7 +431,7 @@ 

Starting Out

Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let's say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that's less than 10 with "BOOM!". If a number isn't odd, we throw it out of our list. For convenience, we'll put that comprehension inside a function so we can easily reuse it.

 boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] 
-

The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True. +

The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True.

 ghci> boomBangs [7..13]
 ["BOOM!","BOOM!","BANG!","BANG!"] 
diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 1cbbc27..02fa337 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -165,7 +165,7 @@

Types and Typeclasses

(>) :: (Ord a) => a -> a -> Bool

-All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively. +All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively.

To be a member of Ord, a type must first have membership in the prestigious and exclusive Eq club.

 ghci> "Abrakadabra" < "Zebra"

From d7fde6e6789a60bbc7759e9d754a83f3d047b8df Mon Sep 17 00:00:00 2001
From: Gregory Cox 
Date: Mon, 21 Nov 2022 02:03:53 +0900
Subject: [PATCH 02/27] Capitalize Higher Order Functions title consistently

---
 docs/higher-order-functions.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html
index 0ef250d..d644e06 100644
--- a/docs/higher-order-functions.html
+++ b/docs/higher-order-functions.html
@@ -31,7 +31,7 @@
                                             
                 
             
-        

Higher order functions

+

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

From 143f05ca561802c56e317a1b3cc442f8aaa34082 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Mon, 21 Nov 2022 17:13:38 +0900 Subject: [PATCH 03/27] Change apostrophes in text to curved closing quotes --- docs/a-fistful-of-monads.html | 378 ++++++------- docs/chapters.html | 8 +- docs/faq.html | 10 +- docs/for-a-few-monads-more.html | 520 +++++++++--------- docs/functionally-solving-problems.html | 112 ++-- ...tors-applicative-functors-and-monoids.html | 474 ++++++++-------- docs/higher-order-functions.html | 138 ++--- docs/index.html | 4 +- docs/input-and-output.html | 388 ++++++------- docs/introduction.html | 22 +- .../making-our-own-types-and-typeclasses.html | 322 +++++------ docs/modules.html | 182 +++--- docs/recursion.html | 58 +- docs/starting-out.html | 128 ++--- docs/syntax-in-functions.html | 106 ++-- docs/types-and-typeclasses.html | 58 +- docs/zippers.html | 314 +++++------ 17 files changed, 1611 insertions(+), 1611 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 590b0a5..db145c6 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -42,7 +42,7 @@

A Fistful of Monads

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

@@ -50,7 +50,7 @@

A Fistful of Monads

more cool than u

-When we started off with functors, we saw that it's possible to map functions +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 @@ -77,7 +77,7 @@

A Fistful of Monads

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 +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 @@ -133,7 +133,7 @@

A Fistful of Monads

-Monads are a natural extension of applicative functors and with them we're +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 @@ -157,11 +157,11 @@

A Fistful of Monads

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 -> 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 +take into account their behavior, but you’ll see that it’s easy as one two three.

@@ -171,18 +171,18 @@

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 +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 +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 +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. @@ -200,8 +200,8 @@

Getting our feet wet with Maybe

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 the insides if it’s a Just value, otherwise the +Nothing is kept because there’s nothing to map it over!

@@ -220,12 +220,12 @@

Getting our feet wet with Maybe

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 +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 +class="fixed">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:

@@ -240,8 +240,8 @@

Getting our feet wet with Maybe

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 Maybe values, it’s similar. All the values have to be Just values, otherwise it’s all for Nothing!

@@ -253,7 +253,7 @@

Getting our feet wet with Maybe

-And now, let's think about how we would do >>= +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 @@ -266,7 +266,7 @@

Getting our feet wet with Maybe

class="fixed">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 +class="fixed">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: @@ -284,19 +284,19 @@

Getting our feet wet with Maybe

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 +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 +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 +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 +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 Getting our feet wet with Maybe

-Okay, now let's play with it for a bit. We'll use it as an infix function so that +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:

@@ -328,7 +328,7 @@

Getting our feet wet with Maybe

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 +What about if the function returns a Nothing? Let’s see:

@@ -350,7 +350,7 @@

Getting our feet wet with Maybe

-It looks like that for Maybe, we've figured out how +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 @@ -360,14 +360,14 @@

Getting our feet wet with Maybe

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. +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. +We’ll come back to Maybe in a minute, but first, +let’s check out the type class that belongs to monads.

@@ -395,26 +395,26 @@

The Monad type class

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 +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 +there should, but when Haskell was made, it hadn’t occurred to people that +applicative functors are a good fit for Haskell so they weren’t in there. But rest assured, every monad is an applicative functor, even if the Monad class declaration doesn't say so. +class="fixed">Monad class declaration doesn’t say so.

The first function that the Monad type class defines -is return. It's the same as 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 +class="fixed">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 The Monad type class

Just a reminder: return is nothing like the -return that's in most other languages. It doesn't end +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.
@@ -432,14 +432,14 @@

The Monad type class

hmmm yaes

-The next function is >>=, or bind. It's like +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 +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. @@ -447,12 +447,12 @@

The Monad type class

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. +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 +like, let’s take a look at how Maybe is an instance of Monad!

@@ -466,7 +466,7 @@

The Monad type class

return is the same as pure, so that one's a no-brainer. We do what we did in the +class="fixed">pure, so that one’s a no-brainer. We do what we did in the Applicative type class and wrap it in a Just.

@@ -476,9 +476,9 @@

The Monad type class

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. +class="fixed">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.

@@ -507,8 +507,8 @@

The Monad type class

class="fixed">\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, +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.

@@ -521,32 +521,32 @@

Walk the line

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 +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 +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 +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 +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 +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 @@ -566,7 +566,7 @@

Walk the line

First we made a type synonym for Int, called -Birds, because we're using integers to represent +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).

@@ -585,7 +585,7 @@

Walk the line

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

@@ -649,7 +649,7 @@ 

Walk the line

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

@@ -664,7 +664,7 @@

Walk the line

-10 birds on the left side and only 3 on the right? That's sure to send poor +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:

@@ -675,14 +675,14 @@

Walk the line

-It might seem like everything is okay but if you follow the steps here, you'll +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. +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 +failure to value than by using Maybe! Let’s rework these functions:

@@ -705,11 +705,11 @@

Walk the line

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. +isn’t, we return a Nothing, indicating failure.

-Let's give these babies a go: +Let’s give these babies a go:

@@ -724,9 +724,9 @@ 

Walk the line

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)) anymore +can’t do landLeft 1 (landRight 1 (0,0)) anymore because when we apply landRight 1 to (0,0), we don't get a Pole, but +class="fixed">(0,0), we don’t get a Pole, but a Maybe Pole. landLeft 1 takes a Pole and not a Maybe Pole. @@ -736,7 +736,7 @@

Walk the line

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: +which does just that for Maybe. Let’s give it a go:

@@ -746,7 +746,7 @@ 

Walk the line

Remember, landLeft 2 has a type of Pole -> Maybe Pole. We couldn't just feed it the 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. Walk the line

-Here's a sequence of birdy landings: +Here’s a sequence of birdy landings:

@@ -789,7 +789,7 @@ 

Walk the line

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

@@ -798,8 +798,8 @@ 

Walk the line

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

@@ -812,7 +812,7 @@

Walk the line

iama banana

-Awesome. The final result represents failure, which is what we expected. Let's +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) >>= @@ -828,21 +828,21 @@

Walk the line

class="fixed">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 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. +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 +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. @@ -861,7 +861,7 @@

Walk the line

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

@@ -891,7 +891,7 @@

Walk the line

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 +monads however, their context and meaning has to be considered as well. Here’s how >> acts with Maybe:

@@ -906,7 +906,7 @@

Walk the line

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

@@ -923,9 +923,9 @@

Walk the line

-It's also worth taking a look at what this would look like if we hadn't made the +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 +a failure context and feeding them to functions like we did. Here’s how a series of bird landings would look like:

@@ -957,7 +957,7 @@

Walk the line

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 +class="fixed">Nothing right away and if it isn’t, going forward with what’s inside the Just.

@@ -978,13 +978,13 @@

do notation

Monads in Haskell are so useful that they got their own special syntax called -do notation. We've already encountered 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 +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. +values in sequence. We’re going to take a look at how do notation works and why it’s useful.

@@ -999,8 +999,8 @@

do notation

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

@@ -1026,7 +1026,7 @@

do notation

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 +are monadic. They’re values with a failure context. We can replace any of them with a failure:

@@ -1052,7 +1052,7 @@

do notation

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

@@ -1080,15 +1080,15 @@

do notation

90s owl

-It would seem as though we've gained the ability to temporarily extract things +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 +class="fixed">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 +comes with those values. It’s important to remember that do expressions are just different syntax for chaining monadic values.

@@ -1101,8 +1101,8 @@

do notation

class="fixed">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 +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 @@ -1133,20 +1133,20 @@

do notation

-If we compare these two, it's easy to see why the result of the whole monadic +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 tightwalker's routine can also be expressed with do +Our tightwalker’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 tightwalker 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 +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:

@@ -1160,7 +1160,7 @@

do notation

-Let's see if he succeeds: +Let’s see if he succeeds:

@@ -1184,12 +1184,12 @@ 

do notation

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

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:

@@ -1227,10 +1227,10 @@ 

do notation

When we write a line in do notation without binding -the monadic value with <-, it's just like putting +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 +we don’t care what it is and it’s prettier than writing _ <- Nothing, which is equivalent to the above.

@@ -1247,7 +1247,7 @@

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 +expressions and function parameters. Here’s an example of pattern matching in a do expression:

@@ -1270,10 +1270,10 @@

do notation

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 +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 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: @@ -1297,10 +1297,10 @@

do notation

It ignores the error message and makes a Nothing. So -when pattern matching fails in a Maybe value that's +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 +crash. Here’s a do expression with a pattern that’s bound to fail:

@@ -1313,7 +1313,7 @@

do notation

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 +the pattern was replaced with a Nothing. Let’s try this out:

@@ -1331,16 +1331,16 @@

do notation

The list monad

dead cat

-So far, we've seen how Maybe values can be viewed as +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 +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 +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 @@ -1362,7 +1362,7 @@

The list monad

-This context of non-determinism translates to monads very nicely. Let's go ahead +This context of non-determinism translates to monads very nicely. Let’s go ahead and see what the Monad instance for lists looks like:

@@ -1384,13 +1384,13 @@

The list monad

-To understand how >>= works for lists, it's +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 +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:

@@ -1408,10 +1408,10 @@

The list monad

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 +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. +from the lambda takes on every value from the list that’s fed to it.

@@ -1426,7 +1426,7 @@

The list monad

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

@@ -1434,8 +1434,8 @@

The list monad

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

@@ -1472,17 +1472,17 @@ 

The list monad

class="fixed">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 +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], +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 +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.

@@ -1494,7 +1494,7 @@

The list monad

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

@@ -1511,14 +1511,14 @@

The list monad

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 +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: +something we’ve seen before. Check out the following piece of code:

@@ -1533,7 +1533,7 @@ 

The list monad

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 +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. @@ -1564,7 +1564,7 @@

The list monad

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: +type class is for monads that can also act as monoids. Here’s its definition:

@@ -1601,7 +1601,7 @@ 

The list monad

-It takes a boolean value and if it's True, takes a () and puts it in a minimal default context +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:

@@ -1629,7 +1629,7 @@

The list monad

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

@@ -1655,7 +1655,7 @@

The list monad

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

@@ -1669,7 +1669,7 @@ 

The list monad

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: +of empty tuples. Here’s this again in the form of a list comprehension:

@@ -1682,20 +1682,20 @@ 

The list monad

class="fixed">guard.

-

A knight's quest

+

A knight’s quest

-Here's a problem that really lends itself to being solved with non-determinism. +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 +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: +Let’s make a type synonym for the knight’s current position on the chess board:

@@ -1703,12 +1703,12 @@ 

A knight's quest

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

@@ -1726,9 +1726,9 @@ 

A knight's quest

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 it's not, +move, (c',r') is still on the board. If it it’s not, it produces an empty list, which causes a failure and return (c',r') isn't carried out for that position. +class="fixed">return (c',r') isn’t carried out for that position.

@@ -1747,7 +1747,7 @@

A knight's quest

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

@@ -1762,7 +1762,7 @@

A knight's quest

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 +class="fixed">moveKnight. Here’s a function that takes a position and returns all the positions that you can reach from it in three moves:

@@ -1801,7 +1801,7 @@

A knight's quest

-Now, let's make a function that takes two positions and tells us if you can +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:

@@ -1812,7 +1812,7 @@

A knight's quest

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

@@ -1834,7 +1834,7 @@

A knight's quest

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

@@ -1848,20 +1848,20 @@

Monad laws

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 +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 +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 -the if the laws hold. But don't worry, they're not complicated. +when we go about making our own monads, we’re going to have to manually check +the if the laws hold. But don’t worry, they’re not complicated.

Left identity

@@ -1869,7 +1869,7 @@

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 +using >>=, it’s the same as just taking the value and applying the function to it. To put it formally:

@@ -1883,8 +1883,8 @@

Left identity

class="fixed">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. +function shouldn’t be much different than just applying the function to the +normal value, and indeed it isn’t different at all.

@@ -1892,7 +1892,7 @@

Left identity

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:

@@ -1907,7 +1907,7 @@

Left identity

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 +since there’s only one value in a singleton list, it’s the same as applying the function to that value:

@@ -1939,15 +1939,15 @@

Right identity

-This one might be a bit less obvious than the first one, but let's take a look +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: +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:

@@ -1978,8 +1978,8 @@ 

Right identity

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 +class="fixed">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.

@@ -1987,8 +1987,8 @@

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: +applications with >>=, it shouldn’t matter how +they’re nested. Formally written:

    @@ -1997,14 +1997,14 @@

    Associativity

-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. +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 +class="fixed">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.

@@ -2023,7 +2023,7 @@

Associativity

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 +monadic function, and so on. If we were to explicitly parenthesize this, we’d write:

@@ -2048,7 +2048,7 @@

Associativity

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), @@ -2056,8 +2056,8 @@

Associativity

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

@@ -2074,7 +2074,7 @@

Associativity

class="fixed">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 +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 @@ -2104,7 +2104,7 @@

Associativity

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. +way of saying that for monads, the nesting of operations shouldn’t matter.

@@ -2127,7 +2127,7 @@

Associativity

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 +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/chapters.html b/docs/chapters.html index 5c12302..7b676cb 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -23,7 +23,7 @@

Learn You a Haskell for Great Good!

Introduction @@ -31,10 +31,10 @@

Learn You a Haskell for Great Good!

Starting Out @@ -166,7 +166,7 @@

Learn You a Haskell for Great Good!

- 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. + 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 index 9593b05..3c28ebf 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -19,16 +19,16 @@

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.

+

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.

+

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!

+

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.

+

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.

+

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 index 6adf1e1..f7ee7d5 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -36,29 +36,29 @@

    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 +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 +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 +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 +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 For a Few Monads More

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

    @@ -87,9 +87,9 @@

    Writer? I hardly know her!

    -For instance, we might want to equip our values with strings that explain what's +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 +number of bandits in a gang and tells us if that’s a big gang or not. That’s a very simple function:

    @@ -113,8 +113,8 @@

    Writer? I hardly know her!

    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: +component is the string that accompanies that value. There’s some added context +to our value now. Let’s give this a go:

    @@ -124,16 +124,16 @@ 

    Writer? I hardly know her!

    (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 +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 +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?

    @@ -154,15 +154,15 @@

    Writer? I hardly know her!

    -In the same vein, let's make a function that takes a value with an attached log, +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 +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: +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:

    @@ -177,19 +177,19 @@ 

    Writer? I hardly know her!

    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 +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 +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: +Here’s applyLog in action:

    @@ -228,7 +228,7 @@ 

    Monoids to the rescue

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

    @@ -239,13 +239,13 @@

    Monoids to the rescue

    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 +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 +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 @@ -277,7 +277,7 @@

    Monoids to the rescue

    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: +operate with the items. Here’s a function that adds drink to some cowboy food:

    @@ -306,12 +306,12 @@ 

    Monoids to the rescue

    -The addDrink function is pretty simple. If we're +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 +class="fixed">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:

    @@ -327,9 +327,9 @@

    Monoids to the rescue

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

    @@ -338,7 +338,7 @@

    Monoids to the rescue

    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: +along with our drink and how much that will cost us. Let’s give it a shot:

    @@ -357,8 +357,8 @@ 

    Monoids to the rescue

    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 +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 @@ -366,7 +366,7 @@

    The Writer type

    -First, let's examine the type itself. To attach a monoid to a value, we just +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: @@ -377,7 +377,7 @@

    The Writer type

    -It's wrapped in a newtype so that it can be made an +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 @@ -395,9 +395,9 @@

    The Writer type

    -when you have to poop, poop, don't talk +when you have to poop, poop, don’t talk

    -First off, let's examine >>=. Its +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. @@ -425,8 +425,8 @@

    The Writer type

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

    @@ -440,18 +440,18 @@

    The Writer type

    -Because Writer doesn't have a 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 +class="fixed">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 +The Writer instance doesn’t feature an implementation for fail, so if a pattern match fails in do notation, error is called.

    @@ -459,13 +459,13 @@

    The Writer type

    Using do notation with Writer

    -Now that we have a Monad instance, we're free to use +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. 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 +and so are reflected in the final result. Here’s a simple example of using do notation with Writer to multiply two numbers:

    @@ -493,7 +493,7 @@

    Using do notation with Writer

    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 +context, we can be sure that it won’t add anything to the log. Here’s what we see if we run this:

    @@ -505,14 +505,14 @@

    Using do notation with Writer

    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 +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 +result, we don’t bind it to a variable. Here’s multWithLog but with some extra reporting included:

    @@ -526,12 +526,12 @@

    Using do notation with Writer

-It's important that return (a*b) is the last line, +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. +class="fixed">do expression. We’d lose the result of the multiplication. However, the log would be the same. Here is this in action:

@@ -543,11 +543,11 @@

Using do notation with Writer

Adding logging to programs

-Euclid's algorithm is an algorithm that takes two numbers and computes their +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: +function, which does exactly this, but let’s implement our own and then equip it +with logging capabilities. Here’s the normal algorithm:

@@ -561,16 +561,16 @@

Adding logging to programs

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 +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 +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 +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: +is now 0, the final result is 1. Let’s see if our code agrees:

@@ -580,7 +580,7 @@ 

Adding logging to programs

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

@@ -590,7 +590,7 @@

Adding logging to programs

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

@@ -613,7 +613,7 @@ 

Adding logging to programs

class="fixed">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 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: @@ -625,28 +625,28 @@

Adding logging to programs

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 +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 +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 +class="fixed">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 +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: +tuple is the result. Let’s see if it’s okay:

@@ -655,7 +655,7 @@ 

Adding logging to programs

-Good! Now what about the log? Because the log is a list of strings, let's use +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:

@@ -668,7 +668,7 @@

Adding logging to programs

-I think it's awesome how we were able to change our ordinary algorithm to one +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 @@ -683,7 +683,7 @@

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

@@ -698,9 +698,9 @@

Inefficient list construction

-Lists are a data structure that's constructed from left to right, and this is +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 +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:

@@ -750,7 +750,7 @@

Inefficient list construction

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

@@ -761,9 +761,9 @@

Difference lists

Because lists can sometimes be inefficient when repeatedly appended in this -manner, it's best to use a data structure that always supports efficient +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 +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 @@ -789,7 +789,7 @@

Difference lists

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 +f `append` g makes a new function that’s equivalent to the following:

@@ -798,12 +798,12 @@

Difference lists

-We've appended two difference lists just by making a new function that first +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 +Let’s make a newtype wrapper for difference lists so that we can easily give them monoid instances:

@@ -833,7 +833,7 @@

Difference lists

-Here's the Monoid instance: +Here’s the Monoid instance:

@@ -845,7 +845,7 @@ 

Difference lists

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

@@ -878,7 +878,7 @@ 

Difference lists

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 +with toDiffList. Let’s see if the log gets assembled properly:

@@ -926,7 +926,7 @@

Comparing Performance

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 +like 500000, you’ll see that it quickly starts counting from 0 onwards:

@@ -960,7 +960,7 @@

Comparing Performance

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

@@ -988,7 +988,7 @@

Reader? Ugh, not this joke again.

class="fixed">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:

@@ -1001,9 +1001,9 @@

Reader? Ugh, not this joke again.

-We've also seen that functions are applicative functors. They allow us to +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: +Here’s an example:

@@ -1026,17 +1026,17 @@ 

Reader? Ugh, not this joke again.

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

@@ -1048,7 +1048,7 @@

Reader? Ugh, not this joke again.

-We've already seen how pure is implemented for +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 @@ -1058,10 +1058,10 @@

Reader? Ugh, not this joke again.

The implementation for >>= seems a bit cryptic, -but it's really not all that. When we use >>= +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 +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 @@ -1073,8 +1073,8 @@

Reader? Ugh, not this joke again.

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

@@ -1097,7 +1097,7 @@

Reader? Ugh, not this joke again.

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

@@ -1129,8 +1129,8 @@

Reader? Ugh, not this joke again.

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 +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. @@ -1138,18 +1138,18 @@

Reader? Ugh, not this joke again.

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

@@ -1176,23 +1176,23 @@

Tasteful stateful computations

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

@@ -1231,15 +1231,15 @@

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. +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. Stacks and stones

We used () as the result when pushing to the stack -because pushing an item onto the stack doesn't have any important result value, +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 +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:

@@ -1289,9 +1289,9 @@

Stacks and stones

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 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: +tuple with that number and that stack. Let’s try it out:

@@ -1302,14 +1302,14 @@ 

Stacks and stones

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. +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 +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 the next one. Wouldn’t it be cooler if, instead of giving the stack manually to each function, we could write something like this:

@@ -1330,7 +1330,7 @@

The State monad

The Control.Monad.State module provides a newtype that wraps stateful computations. Here's its definition: +class="fixed">newtype that wraps stateful computations. Here’s its definition:

@@ -1344,8 +1344,8 @@ 

The State monad

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

@@ -1358,9 +1358,9 @@

The State monad

-Let's take a gander at return first. Our aim +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 +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 @@ -1378,7 +1378,7 @@

The State monad

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 +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 @@ -1399,8 +1399,8 @@

The State monad

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

@@ -1433,7 +1433,7 @@

The State monad

-See how we've glued a push and two pops into one stateful computation? When we +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:

@@ -1444,8 +1444,8 @@

The State monad

-We didn't have to bind the second pop to a because we didn't use that a +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:

@@ -1460,8 +1460,8 @@

The State monad

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 +stack and stop but if it isn’t 5, we push 3 and 8 back on? Well, here’s the code:

@@ -1477,7 +1477,7 @@

The State monad

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

@@ -1512,7 +1512,7 @@ 

The State monad

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

The State monad

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

@@ -1569,11 +1569,11 @@

The State monad

-It makes sense that the monad itself, Maybe, doesn't -change. It wouldn't make sense to use >>= +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, we’d be using >>= between two different monads.

@@ -1598,7 +1598,7 @@

Randomness and the state monad

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 +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: @@ -1635,7 +1635,7 @@

Randomness and the state monad

class="fixed">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: +without changing the most recent generator. Let’s give this a go:

@@ -1656,7 +1656,7 @@ 

Error error on the wall

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

@@ -1682,8 +1682,8 @@

Error error on the wall

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. +with an added context of possible failure, only now there’s a value attached +when there’s an error as well.

@@ -1702,7 +1702,7 @@

Error error on the wall

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 +constructor because we’re using Right to represent a successful computation where a result is present. This is a lot like return for Maybe.

@@ -1721,7 +1721,7 @@

Error error on the wall

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 +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 @@ -1740,7 +1740,7 @@

Error error on the wall

But since we usually use String to describe the error -when using Either, we don't have to worry about this +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.

@@ -1761,13 +1761,13 @@

Error error on the wall

class="fixed">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 +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. +also succeeds, we’re tripped up by a peculiar type error! Hmmm.

@@ -1781,9 +1781,9 @@ 

Error error on the wall

-Haskell says that it doesn't know which type to choose for the +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. +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 @@ -1814,12 +1814,12 @@

Some useful monadic functions

-In this section, we're going to explore a few functions that either operate on +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! +Let’s see what they are then!

liftM and friends

@@ -1843,17 +1843,17 @@

liftM and friends

class="fixed">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 +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 +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 +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’s type:

@@ -1871,10 +1871,10 @@ 

liftM and friends

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 +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 +constraint whereas the other has a Monad one. Let’s try liftM out:

@@ -1962,7 +1962,7 @@

liftM and friends

-So it's kind of like fmap, only the function itself +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 @@ -1977,7 +1977,7 @@

liftM and friends

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: +class="fixed">Applicative one. Here’s its definition:

@@ -1993,7 +1993,7 @@ 

liftM and friends

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: +to the value and present that as a result. Here’s a quick demonstration:

@@ -2023,7 +2023,7 @@

liftM and friends

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

@@ -2041,7 +2041,7 @@

liftM and friends

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 +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. @@ -2050,7 +2050,7 @@

liftM and friends

The join function

-Here's some food for thought: if the result of one monadic value is another +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 The join function

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. +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 @@ -2172,12 +2172,12 @@

The join function

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

@@ -2197,19 +2197,19 @@ 

The join 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 +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). +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 +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 >>=.

@@ -2235,10 +2235,10 @@

filterM

class="fixed">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 +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 +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.

@@ -2254,14 +2254,14 @@

filterM

The predicate returns a monadic value whose result is a Bool, but because it's a monadic value, its context can be +class="fixed">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: +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:

@@ -2270,9 +2270,9 @@ 

filterM

-That's pretty easy. Now, let's make a predicate that, aside from presenting a +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:

@@ -2289,14 +2289,14 @@

filterM

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 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. +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. @@ -2309,7 +2309,7 @@

filterM

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 +we see that everything is in order. Now, let’s print the log and see what we got:

@@ -2355,12 +2355,12 @@

filterM

-To make a function that returns a powerset of some list, we're going to rely on +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:

@@ -2370,9 +2370,9 @@

filterM

-Wait, that's it? Yup. We choose to drop and keep every element, regardless of +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 +list will also be a non-deterministic value and will thus be a list of lists. Let’s give this a go:

@@ -2383,8 +2383,8 @@

filterM

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

@@ -2413,7 +2413,7 @@

foldM

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 +whole fold is monadic as well. Let’s sum a list of numbers with a fold:

@@ -2436,9 +2436,9 @@

foldM

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

@@ -2450,7 +2450,7 @@

foldM

-Because our binary function is now a monadic function, we can't use it with the +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:

@@ -2472,14 +2472,14 @@

foldM

Making a safe RPN calculator

-i've found yellow!

When we were solving the problem of implementing a 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 +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.

@@ -2529,7 +2529,7 @@

Making a safe RPN calculator

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

@@ -2545,7 +2545,7 @@

Making a safe RPN calculator

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

@@ -2568,7 +2568,7 @@

Making a safe RPN calculator

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

@@ -2591,7 +2591,7 @@

Making a safe RPN calculator

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: +Let’s try out the folding function by itself:

@@ -2608,7 +2608,7 @@ 

Making a safe RPN calculator

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

@@ -2626,11 +2626,11 @@

Making a safe RPN calculator

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) +class="fixed">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 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 @@ -2640,7 +2640,7 @@

Making a safe RPN calculator

-Let's give it a shot: +Let’s give it a shot:

@@ -2655,7 +2655,7 @@ 

Making a safe RPN calculator

-The first failure happens because the final stack isn't a list with one element +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. @@ -2692,7 +2692,7 @@

Composing monadic functions

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: +function. Here’s an example:

@@ -2707,7 +2707,7 @@ 

Composing monadic functions

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 +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. @@ -2717,7 +2717,7 @@

Composing monadic functions

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 +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: @@ -2744,7 +2744,7 @@

Composing monadic functions

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: +positions. So now, let’s make it more general. Here’s how to do it:

@@ -2779,9 +2779,9 @@ 

Making monads

kewl

-In this section, we're going to look at an example of how a type gets made, +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 +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 @@ -2789,9 +2789,9 @@

Making monads

-As we've seen, lists are used to represent non-deterministic values. A list like +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 +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. @@ -2800,16 +2800,16 @@

Making monads

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

@@ -2818,11 +2818,11 @@

Making monads

-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 +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, 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 @@ -2842,7 +2842,7 @@

Making monads

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'use throw out our floating points and use +nineteen twelfths. So let’use throw out our floating points and use Rational for our probabilities:

@@ -2859,8 +2859,8 @@

Making monads

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

@@ -2873,8 +2873,8 @@

Making monads

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

@@ -2886,7 +2886,7 @@

Making monads

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: +probabilities as they are and then wrap it back up. Let’s see if it works:

@@ -2897,7 +2897,7 @@ 

Making monads

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 +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. @@ -2905,29 +2905,29 @@

Making monads

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 +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 +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. +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 +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 +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 +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 +are also equally likely to happen. Here’s a picture of a probability list that models this scenario:

@@ -2947,7 +2947,7 @@

Making monads

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

@@ -2960,10 +2960,10 @@ 

Making monads

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 +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 +here’s flatten, which we’ll use because the name join is already taken:

@@ -2999,12 +2999,12 @@

Making monads

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 +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 +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 @@ -3012,7 +3012,7 @@

Making monads

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 +class="fixed">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 @@ -3032,7 +3032,7 @@

Making monads

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

@@ -3074,9 +3074,9 @@

Making monads

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

@@ -3084,8 +3084,8 @@

Making monads

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 +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 index 514b652..b1161bc 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -32,22 +32,22 @@

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.

+

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

+

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 on to 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 on to 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 a RPN expression, like "10 4 3 + 2 * -" and gives us back its result.

+

Let’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to 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 a 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.

-
Protip: 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.
+
Protip: 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:

+

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
 
@@ -56,7 +56,7 @@ 

Functionally Solving Problems

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:

+

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
 
@@ -64,7 +64,7 @@ 

Functionally Solving Problems

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

+

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
@@ -73,11 +73,11 @@ 

Functionally Solving Problems

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 on to 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 intial 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:

+

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 on to 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 intial 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
@@ -92,8 +92,8 @@ 

Functionally Solving Problems

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.

+

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
 
@@ -125,13 +125,13 @@ 

Functionally Solving Problems

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.

+

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.

+

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:

+

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
@@ -146,60 +146,60 @@ 

Functionally Solving Problems

8 0
-

To mentally parse the input file, read it in threes and mentally split the road system into sections. Each section is comprised 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:

+

To mentally parse the input file, read it in threes and mentally split the road system into sections. Each section is comprised 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:

    -
  • Forget Haskell for a minute and think about how we'd solve the problem by hand
  • -
  • Think about how we're going to represent our data in Haskell
  • +
  • Forget Haskell for a minute and think about how we’d solve the problem by hand
  • +
  • Think about how we’re going to represent our data in Haskell
  • Figure out how to operate on that data in Haskell so that we produce at a solution
-

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:

+

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!

+

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!

+

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.

+

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.

+

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!

+

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

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.

+

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:

+

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.

+

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)
@@ -219,17 +219,17 @@ 

Functionally Solving Problems

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.

+

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.

+

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.

+
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 =
@@ -238,22 +238,22 @@ 

Functionally Solving Problems

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!

+

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.

+

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

+

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:

+

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
 
diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html
index 9c06112..0481086 100644
--- a/docs/functors-applicative-functors-and-monoids.html
+++ b/docs/functors-applicative-functors-and-monoids.html
@@ -32,18 +32,18 @@
                 
             
         

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.

+

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.

+

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.

+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
@@ -51,24 +51,24 @@ 

Functors, Applicative Functors and Monoids

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:

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

+

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.

+

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 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
@@ -82,8 +82,8 @@ 

Functors, Applicative Functors and Monoids

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

+

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
@@ -94,14 +94,14 @@ 

Functors, Applicative Functors and Monoids

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:

+

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.

+

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
@@ -114,12 +114,12 @@ 

Functors, Applicative Functors and Monoids

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.

+

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:

+

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
@@ -130,7 +130,7 @@ 

Functors, Applicative Functors and Monoids

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.

+

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]]
@@ -143,9 +143,9 @@ 

Functors, Applicative Functors and Monoids

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.

+

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.

+

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

 ghci> fmap id (Just 3)
 Just 3
@@ -170,14 +170,14 @@ 

Functors, Applicative Functors and Monoids

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:

+

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.

+

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
@@ -190,13 +190,13 @@ 

Functors, Applicative Functors and Monoids

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.

+

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:

+

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"
@@ -205,23 +205,23 @@ 

Functors, Applicative Functors and Monoids

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.

+

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.

+

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.

+

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!

+

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])
@@ -232,8 +232,8 @@ 

Functors, Applicative Functors and Monoids

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.

+

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
@@ -241,18 +241,18 @@ 

Functors, Applicative Functors and Monoids

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:

+

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.

+

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

+

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 a 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
@@ -260,10 +260,10 @@ 

Functors, Applicative Functors and Monoids

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

+

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
@@ -276,8 +276,8 @@ 

Functors, Applicative Functors and Monoids

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:

+

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
@@ -287,16 +287,16 @@ 

Functors, Applicative Functors and Monoids

Nothing
whaale -

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

+

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

+
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"
@@ -307,22 +307,22 @@ 

Functors, Applicative Functors and Monoids

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

+

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.

+

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.

+

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]
@@ -332,31 +332,31 @@ 

Functors, Applicative Functors and Monoids

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.

+

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.

+

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:

+

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:

+

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:

+

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
@@ -366,9 +366,9 @@ 

Functors, Applicative Functors and Monoids

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.

+

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.

+

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
@@ -384,21 +384,21 @@ 

Functors, Applicative Functors and Monoids

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:

+

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

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

+

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
@@ -408,26 +408,26 @@ 

Functors, Applicative Functors and Monoids

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.

+

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.

+

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.

+

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:

+

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)
@@ -435,7 +435,7 @@ 

Functors, Applicative Functors and Monoids

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

+

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]
@@ -447,40 +447,40 @@ 

Functors, Applicative Functors and Monoids

[('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:

+

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:

+

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.

+

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.

+

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

+

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.

+

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]
@@ -493,17 +493,17 @@ 

Functors, Applicative Functors and Monoids

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.

+

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:

+

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:

+

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]
@@ -511,8 +511,8 @@ 

Functors, Applicative Functors and Monoids

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:

+

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]]
@@ -527,17 +527,17 @@ 

Functors, Applicative Functors and Monoids

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.

+

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.

  • We start off with sequenceA [[1,2],[3,4]]
  • That evaluates to (:) <$> [1,2] <*> sequenceA [[3,4]]
  • Evaluating the inner sequenceA further, we get (:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])
  • -
  • We've reached the edge condition, so this is now (:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
  • +
  • We’ve reached the edge condition, so this is now (:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
  • Now, we evaluate the (:) <$> [3,4] <*> [[]] part, which will use : with every possible value in the left list (possible values are 3 and 4) with every possible value on the right list (only possible value is []), which results in [3:[], 4:[]], which is [[3],[4]]. So now we have (:) <$> [1,2] <*> [[3],[4]]
  • Now, : is used with every possible value from the left list (1 and 2) with every possible value in the right list ([3] and [4]), which results in [1:[3], 1:[4], 2:[3], 2:[4]], which is [[1,3],[1,4],[2,3],[2,4]
-

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.

+

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
@@ -545,25 +545,25 @@ 

Functors, Applicative Functors and Monoids

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:

+

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:

  • pure id <*> v = v
  • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
  • pure f <*> pure x = pure (f x)
  • u <*> pure y = pure ($ y) <*> u
-

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.

+

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 +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. +newtype keyword and why we’d want to do that in the first place.

@@ -584,17 +584,17 @@

Functors, Applicative Functors and Monoids

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 +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. +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: +we’re done, unwrap them with getZipList:

@@ -643,9 +643,9 @@ 

Functors, Applicative Functors and Monoids

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 +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), +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. @@ -669,7 +669,7 @@

Functors, Applicative Functors and Monoids

-When using newtype, you're restricted to just one constructor with one +When using newtype, you’re restricted to just one constructor with one field.

@@ -682,7 +682,7 @@

Functors, Applicative Functors and Monoids

Bounded, Show and Read. -If we derive the instance for a type class, the type that we're wrapping +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: @@ -693,7 +693,7 @@

Functors, Applicative Functors and Monoids

-Let's give that a go: +Let’s give that a go:

@@ -739,7 +739,7 @@ 

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

@@ -770,7 +770,7 @@

Using newtype to make type class instances

wow, very evil

-Isn't that just peachy? Now what if we wanted to make the tuple an instance of +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). @@ -778,7 +778,7 @@

Using newtype to make type class instances

class="fixed">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 +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 @@ -845,11 +845,11 @@

On newtype laziness

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. +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 +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 @@ -867,7 +867,7 @@

On newtype laziness

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

@@ -885,9 +885,9 @@

On newtype laziness

-It's your run-of-the-mill algebraic data type that was defined with the +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 +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 @@ -901,7 +901,7 @@

On newtype laziness

Instead of applying this function to a normal CoolBool, -let's throw it a curveball and apply it to undefined! +let’s throw it a curveball and apply it to undefined!

@@ -922,7 +922,7 @@ 

On newtype laziness

Instead of using the data keyword for CoolBool, -let's try using newtype: +let’s try using newtype:

@@ -930,9 +930,9 @@ 

On newtype laziness

-We don't have to change our helloMe function, because +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 +data to define your type. Let’s do the same thing here and apply helloMe to an undefined value:

@@ -945,25 +945,25 @@

On newtype laziness

top of the mornin to ya!!!

-It worked! Hmmm, why is that? Well, like we've said, when we use newtype, +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 +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 +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 +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 +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 +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.

@@ -971,7 +971,7 @@

type vs. newtype vs. <

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 +between type, data and newtype is, so let’s refresh our memory a bit.

@@ -988,9 +988,9 @@

type vs. newtype vs. <

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. +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 +are only two ways to refer to the same type, it doesn’t matter which name we use in our type annotations:

@@ -1002,7 +1002,7 @@

type vs. newtype vs. <

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 +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 @@ -1011,7 +1011,7 @@

type vs. newtype vs. <

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

@@ -1021,12 +1021,12 @@

type vs. newtype vs. <

-We can't use ++ to put together a +We can’t use ++ to put together a CharList and a list of type -[Char]. We can't even use +[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 +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.

@@ -1035,7 +1035,7 @@

type vs. newtype vs. < 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 +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.

@@ -1057,8 +1057,8 @@

type vs. newtype vs. < 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 +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.

@@ -1088,13 +1088,13 @@

type vs. newtype vs. <

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 +class="fixed">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 +*, it also has a certain value which doesn’t change the other one when used with ++. That value is the empty list: [].

@@ -1120,15 +1120,15 @@

type vs. newtype vs. <
  • The function takes two parameters.
  • The parameters and the returned value have the same type.
  • -
  • There exists such a value that doesn't change other values when used +
  • There exists such a value that doesn’t change other values when used with the binary function.

-There's another thing that these two operations have in common that may not be +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 +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 ++: @@ -1165,7 +1165,7 @@

type vs. newtype vs. < class="fixed">[] 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 +exists. It’s for types which can act like monoids. Let’s see how the type class is defined:

@@ -1182,37 +1182,37 @@

type vs. newtype vs. <

The Monoid type class is defined in -import Data.Monoid. Let's take some time and get +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 +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 +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 +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 +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 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 +class="fixed">* 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 +class="fixed">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. @@ -1221,10 +1221,10 @@

type vs. newtype vs. <

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 +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 +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. @@ -1236,13 +1236,13 @@

type vs. newtype vs. <

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

@@ -1258,14 +1258,14 @@

type vs. newtype vs. < 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 +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 ++ +Yes, lists are monoids! Like we’ve seen, the ++ function and the empty list [] form a monoid. The instance is very simple:

@@ -1310,7 +1310,7 @@

Lists are monoids

Notice that in the last line, we had to write an explicit type annotation, -because if we just did mempty, GHCi wouldn't know +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]) @@ -1322,18 +1322,18 @@

Lists are monoids

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 +because that’s the equivalent of doing ++ between all the adjecent 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 +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 +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: +they clearly aren’t:

@@ -1344,9 +1344,9 @@ 

Lists are monoids

-And that's okay. The fact that for multiplication 3 * 5 and +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. +multiplication, but it doesn’t hold for all (and indeed, most) monoids.

Product and Sum

@@ -1354,7 +1354,7 @@

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

@@ -1374,7 +1374,7 @@

Product and Sum

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 +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. @@ -1408,9 +1408,9 @@

Product and Sum

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

@@ -1430,7 +1430,7 @@

Product and Sum

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 +But a bit later, we’ll see how these Monoid instances that may seem trivial at this time can come in handy.

@@ -1462,7 +1462,7 @@

Any and All

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: +in this fashion. It’s defined like this:

@@ -1481,7 +1481,7 @@ 

Any and All

-The reason it's called Any is because +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 @@ -1548,7 +1548,7 @@

Any and All

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 +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 @@ -1558,7 +1558,7 @@

Any and All

The Ordering monoid

-Hey, remember the Ordering type? It's used as the +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: @@ -1579,7 +1579,7 @@

The Ordering monoid

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: +we’ve met so far and also quite useful:

@@ -1590,7 +1590,7 @@ 

The Ordering monoid

GT `mappend` _ = GT
-did anyone ORDER pizza?!?! I can't BEAR these puns! +did anyone ORDER pizza?!?! I can’t BEAR these puns!

The instance is set up like this: when we mappend two @@ -1606,22 +1606,22 @@

The Ordering monoid

For instance, if we were to alphabetically compare the words -"ox" and "on", we'd first +"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 +wouldn’t change their alphabetical ordering. "oix" is still alphabetically greater than and "oin".

-It's important to note that in the Monoid instance +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, 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: @@ -1639,7 +1639,7 @@

The Ordering monoid

-OK, so how is this monoid useful? Let's say you were writing a function that +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 @@ -1685,11 +1685,11 @@

The Ordering monoid

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, +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: +most important criterion for comparison, we’d just modify it like this:

@@ -1725,7 +1725,7 @@ 

The Ordering monoid

"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 +same number of vowels, so they’re compared alphabetically and "zen" wins.

@@ -1738,7 +1738,7 @@

The Ordering monoid

Maybe the monoid

-Let's take a look at the various ways that Maybe a +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.

@@ -1749,9 +1749,9 @@

Maybe the monoid

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 +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: +keep the other value. Here’s the instance declaration:

@@ -1773,7 +1773,7 @@ 

Maybe the monoid

class="fixed">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 +that the type of what’s inside the Just is an instance of Monoid.

@@ -1787,20 +1787,20 @@

Maybe the monoid

-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 +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 +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 +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: @@ -1826,10 +1826,10 @@

Maybe the monoid

Just like we said. mempty is just a Nothing wrapped with the First -newtype constructor. If mappend's 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 +second parameter as a result, regardless of whether it’s a Just or a Nothing:

@@ -1876,8 +1876,8 @@

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

@@ -1889,7 +1889,7 @@

Using monoids to fold data structures

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): +it’s best imported qualified (and served with basil):

@@ -1897,12 +1897,12 @@ 

Using monoids to fold data structures

-To save ourselves precious keystrokes, we've chosen to import it qualified as +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 +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:

@@ -1930,7 +1930,7 @@

Using monoids to fold data structures

-Okay then, what are some other data structures that support folds? Well, there's +Okay then, what are some other data structures that support folds? Well, there’s the Maybe we all know and love!

@@ -1942,11 +1942,11 @@

Using monoids to fold data structures

-But folding over a Maybe value isn't terribly +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. +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.

@@ -1960,10 +1960,10 @@

Using monoids to fold data structures

-We said that a tree is either an empty tree that doesn't hold any values or it's a node that +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 +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. @@ -1984,8 +1984,8 @@

Using monoids to fold data structures

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 +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 @@ -2012,11 +2012,11 @@

Using monoids to fold data structures

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

@@ -2035,7 +2035,7 @@

Using monoids to fold data structures

-Notice that we didn't have to provide the function that takes a value and +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. @@ -2062,7 +2062,7 @@

Using monoids to fold data structures

It has 5 at its root and then its left node is has 3 with 1 on the left and -6 on the right. The root's right node has a 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, @@ -2077,7 +2077,7 @@

Using monoids to fold data structures

-And also, foldMap isn't only useful for making new instances of +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: @@ -2107,7 +2107,7 @@

Using monoids to fold data structures

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

@@ -2127,7 +2127,7 @@

Using monoids to fold data structures

-What's cool is that all of these trick aren't limited to trees, they work on any +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 index d644e06..c81e931 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -33,9 +33,9 @@

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.

+

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:

+

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
@@ -43,14 +43,14 @@ 

Higher Order Functions

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.

+

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:

+

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
@@ -59,18 +59,18 @@ 

Higher Order Functions

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:

+

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:

+

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:

+
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)
@@ -91,16 +91,16 @@ 

Higher Order Functions

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.

+

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!

+

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

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
@@ -115,15 +115,15 @@ 

Higher Order Functions

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

+

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:

+

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]
@@ -137,15 +137,15 @@ 

Higher Order Functions

[[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. +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:

+

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.

+

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
@@ -158,13 +158,13 @@ 

Higher Order Functions

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

+

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]
@@ -177,7 +177,7 @@ 

Higher Order Functions

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.

+

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]
@@ -186,7 +186,7 @@ 

Higher Order Functions

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

+

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]
@@ -201,7 +201,7 @@ 

Higher Order Functions

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.

+

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]
@@ -212,15 +212,15 @@ 

Higher Order Functions

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.

+

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:

+

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
@@ -230,9 +230,9 @@ 

Higher Order Functions

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:

+

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]
@@ -240,7 +240,7 @@ 

Higher Order Functions

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

+

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]
@@ -255,37 +255,37 @@ 

Higher Order Functions

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.

+

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

+

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 listOfFuns = map (*) [0..]
 ghci> (listOfFuns !! 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.

+

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:

+

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.

+

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.

+

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!

+

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:

+

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
@@ -294,19 +294,19 @@ 

Higher Order Functions

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.

+

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.

+

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.

+

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
@@ -317,32 +317,32 @@ 

Higher Order Functions

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!

+

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.

+

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.

+

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.

+

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:

+

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)
@@ -362,8 +362,8 @@ 

Higher Order Functions

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

+

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]
@@ -376,7 +376,7 @@ 

Higher Order Functions

[[],[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 roots of all natural numbers to exceed 1000? To get the squares 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.

+

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 roots of all natural numbers to exceed 1000? To get the squares 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
@@ -389,16 +389,16 @@ 

Higher Order Functions

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.

+

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:

+

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.

+

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.

@@ -413,8 +413,8 @@ 

Higher Order Functions

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:

+

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]
@@ -434,7 +434,7 @@ 

Higher Order Functions

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.

+

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
@@ -444,12 +444,12 @@ 

Higher Order Functions

 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.

+

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.

+

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..])))
@@ -467,7 +467,7 @@ 

Higher Order Functions

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.

+

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 580b73d..31fa118 100644 --- a/docs/index.html +++ b/docs/index.html @@ -48,7 +48,7 @@ - +

    Input and Output

    poor dog -

    We've mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can't change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you're coming from an imperative world, we've seen that it's actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won't burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn't insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can't change the old one.

    -

    While functions being unable to change state is good because it helps us reason about our programs, there's one problem with that. If a function can't change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

    +

    We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

    +

    While functions being unable to change state is good because it helps us reason about our programs, there’s one problem with that. If a function can’t change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

    Do not despair, all is not lost. It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen. With those two parts separated, we can still reason about our pure program and take advantage of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

    Hello, world!

    HELLO! -

    Up until now, we've always loaded our functions into GHCI to test them out and play with them. We've also explored the standard library functions that way. But now, after eight or so chapters, we're finally going to write our first real Haskell program! Yay! And sure enough, we're going to do the good old "hello, world" schtick.

    -
    Hey! For the purposes of this chapter, I'm going to assume you're using a unix-y environment for learning Haskell. If you're on Windows, I'd suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.
    +

    Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

    +
    Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

    So, for starters, punch in the following in your favorite text editor:

     main = putStrLn "hello, world"
     
    -

    We just defined a name called main and in it we call a function called putStrLn with the parameter "hello, world". Looks pretty much run of the mill, but it isn't, as we'll see in just a few moments. Save that file as helloworld.hs.

    -

    And now, we're going to do something we've never done before. We're actually going to compile our program! I'm so excited! Open up your terminal and navigate to the directory where helloworld.hs is located and do the following:

    +

    We just defined a name called main and in it we call a function called putStrLn with the parameter "hello, world". Looks pretty much run of the mill, but it isn’t, as we’ll see in just a few moments. Save that file as helloworld.hs.

    +

    And now, we’re going to do something we’ve never done before. We’re actually going to compile our program! I’m so excited! Open up your terminal and navigate to the directory where helloworld.hs is located and do the following:

     $ ghc --make helloworld
     [1 of 1] Compiling Main             ( helloworld.hs, helloworld.o )
    @@ -57,67 +57,67 @@ 

    Input and Output

    hello, world

    And there we go, our first compiled program that printed out something to the terminal. How extraordinarily boring!

    -

    Let's examine what we wrote. First, let's look at the type of the function putStrLn.

    +

    Let’s examine what we wrote. First, let’s look at the type of the function putStrLn.

     ghci> :t putStrLn
     putStrLn :: String -> IO ()
     ghci> :t putStrLn "hello, world"
     putStrLn "hello, world" :: IO ()
     
    -

    We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that's usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn't really have any kind of meaningful return value, so a dummy value of () is used.

    +

    We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

    The empty tuple is a value of () and it also has a type of ().

    So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

    -

    Having your whole program be just one I/O action seems kind of limiting. That's why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

    +

    Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

     main = do
         putStrLn "Hello, what's your name?"
         name <- getLine
         putStrLn ("Hey " ++ name ++ ", you rock!")
     
    -

    Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that's the type of the last I/O action inside.

    -

    Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don't usually specify a type declaration for main.

    -

    An interesting thing that we haven't met before is the third line, which states name <- getLine. It looks like it reads a line from the input and stores it into a variable called name. Does it really? Well, let's examine the type of getLine.

    +

    Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that’s the type of the last I/O action inside.

    +

    Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don’t usually specify a type declaration for main.

    +

    An interesting thing that we haven’t met before is the third line, which states name <- getLine. It looks like it reads a line from the input and stores it into a variable called name. Does it really? Well, let’s examine the type of getLine.

     ghci> :t getLine
     getLine :: IO String
     
    luggage -

    Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what's up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it's fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we're taking data out of an I/O action, we can only take it out when we're inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That's why it's sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

    -

    When I say tainted, I don't mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what's inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life's future based on your name. We can do this:

    +

    Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

    +

    When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

     main = do
         putStrLn "Hello, what's your name?"
         name <- getLine
         putStrLn $ "Read this carefully, because this is your future: " ++ tellFortune name
     
    -

    and tellFortune (or any of the functions it passes name to) doesn't have to know anything about I/O, it's just a normal String -> String function!

    +

    and tellFortune (or any of the functions it passes name to) doesn’t have to know anything about I/O, it’s just a normal String -> String function!

    Take a look at this piece of code. Is it valid?

     nameTag = "Hello, my name is " ++ getLine
     
    -

    If you said no, go eat a cookie. If you said yes, drink a bowl of molten lava. Just kidding, don't! The reason that this doesn't work is that ++ requires both its parameters to be lists over the same type. The left parameter has a type of String (or [Char] if you will), whilst getLine has a type of IO String. You can't concatenate a string and an I/O action. We first have to get the result out of the I/O action to get a value of type String and the only way to do that is to say something like name <- getLine inside some other I/O action. If we want to deal with impure data, we have to do it in an impure environment. So the taint of impurity spreads around much like the undead scourge and it's in our best interest to keep the I/O parts of our code as small as possible.

    -

    Every I/O action that gets performed has a result encapsulated within it. That's why our previous example program could also have been written like this:

    +

    If you said no, go eat a cookie. If you said yes, drink a bowl of molten lava. Just kidding, don’t! The reason that this doesn’t work is that ++ requires both its parameters to be lists over the same type. The left parameter has a type of String (or [Char] if you will), whilst getLine has a type of IO String. You can’t concatenate a string and an I/O action. We first have to get the result out of the I/O action to get a value of type String and the only way to do that is to say something like name <- getLine inside some other I/O action. If we want to deal with impure data, we have to do it in an impure environment. So the taint of impurity spreads around much like the undead scourge and it’s in our best interest to keep the I/O parts of our code as small as possible.

    +

    Every I/O action that gets performed has a result encapsulated within it. That’s why our previous example program could also have been written like this:

     main = do
         foo <- putStrLn "Hello, what's your name?"
         name <- getLine
         putStrLn ("Hey " ++ name ++ ", you rock!")
     
    -

    However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn't bind the last putStrLn to anything. That's because in a do block, the last action cannot be bound to a name like the first two were. We'll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

    -

    Except for the last line, every line in a do block that doesn't bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that's useless, so we leave out the <- for I/O actions that don't contain an important result, like putStrLn something.

    +

    However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

    +

    Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

    Beginners sometimes think that doing

     name = getLine
     
    -

    will read from the input and then bind the value of that to name. Well, it won't, all this does is give the getLine I/O action a different name called, well, name. Remember, to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-.

    -

    I/O actions will only be performed when they are given a name of main or when they're inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they'll be performed only if they eventually fall into main.

    -

    Oh, right, there's also one more case when I/O actions will be performed. When we type out an I/O action in GHCI and press return, it will be performed.

    +

    will read from the input and then bind the value of that to name. Well, it won’t, all this does is give the getLine I/O action a different name called, well, name. Remember, to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-.

    +

    I/O actions will only be performed when they are given a name of main or when they’re inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they’ll be performed only if they eventually fall into main.

    +

    Oh, right, there’s also one more case when I/O actions will be performed. When we type out an I/O action in GHCI and press return, it will be performed.

     ghci> putStrLn "HEEY"
     HEEY
     

    Even when we just punch out a number or call a function in GHCI and press return, it will evaluate it (as much as it needs) and then call show on it and then it will print that string to the terminal using putStrLn implicitly.

    -

    Remember let bindings? If you don't, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn't needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

    +

    Remember let bindings? If you don’t, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn’t needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

     import Data.Char
     
    @@ -130,9 +130,9 @@ 

    Input and Output

    bigLastName = map toUpper lastName putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?"
    -

    See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That's good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

    -

    You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn't an I/O action. It's a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we'd still have to run it through a <- to perform it.

    -

    Now we're going to make a program that continuously reads a line and prints out the same line with the words reversed. The program's execution will stop when we input a blank line. This is the program:

    +

    See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

    +

    You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action. It’s a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we’d still have to run it through a <- to perform it.

    +

    Now we’re going to make a program that continuously reads a line and prints out the same line with the words reversed. The program’s execution will stop when we input a blank line. This is the program:

     main = do
         line <- getLine
    @@ -147,17 +147,17 @@ 

    Input and Output

    To get a feel of what it does, you can run it before we go over the code.

    Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.
    -

    First, let's take a look at the reverseWords function. It's just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we'd have to write something like reverseWords st = unwords (map reverse (words st)).

    -

    What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn't, the I/O action under the else is performed. That's why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

    -

    Let's first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

    +

    First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

    +

    What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

    +

    Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

             else (do
                 putStrLn $ reverseWords line
                 main)
     
    -

    This makes it more explicit that the do block can be viewed as one I/O action, but it's uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It's called recursively and that's okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

    -

    Now what happens when null line holds true? What's after the then is performed in that case. If we look up, we'll see that it says then return (). If you've done imperative languages like C, Java or Python, you're probably thinking that you know what this return does and chances are you've already skipped this really long paragraph. Well, here's the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it's quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn't actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What's the point of just transforming a pure value into an I/O action that doesn't do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That's why we just made a bogus I/O action that doesn't do anything by writing return ().

    -

    Using return doesn't cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

    +

    This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

    +

    Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

    +

    Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

     main = do
         return ()
    @@ -167,7 +167,7 @@ 

    Input and Output

    return 4 putStrLn line
    -

    All these returns do is that they make I/O actions that don't really do anything except have an encapsulated result and that result is thrown away because it isn't bound to a name. We can use return in combination with <- to bind stuff to names.

    +

    All these returns do is that they make I/O actions that don’t really do anything except have an encapsulated result and that result is thrown away because it isn’t bound to a name. We can use return in combination with <- to bind stuff to names.

     main = do
         a <- return "hell"
    @@ -181,10 +181,10 @@ 

    Input and Output

    b = "yeah" putStrLn $ a ++ " " ++ b
    -

    When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn't do anything or because we don't want the I/O action that's made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

    -
    A do block can also have just one I/O action. In that case, it's the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.
    -

    Before we move on to files, let's take a look at some functions that are useful when dealing with I/O.

    -

    putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn't jump into a new line after printing out the string while putStrLn does.

    +

    When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

    +
    A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.
    +

    Before we move on to files, let’s take a look at some functions that are useful when dealing with I/O.

    +

    putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

     main = do   putStr "Hey, "
                 putStr "I'm "
    @@ -194,7 +194,7 @@ 

    Input and Output

    $ runhaskell putstr_test.hs Hey, I'm Andy!
    -

    Its type signature is putStr :: String -> IO (), so the result encapsulated within the resulting I/O action is the unit. A dud value, so it doesn't make sense to bind it.

    +

    Its type signature is putStr :: String -> IO (), so the result encapsulated within the resulting I/O action is the unit. A dud value, so it doesn’t make sense to bind it.

    putChar takes a character and returns an I/O action that will print it out to the terminal.

     main = do   putChar 't'
    @@ -205,7 +205,7 @@ 

    Input and Output

    $ runhaskell putchar_test.hs teh
    -

    putStr is actually defined recursively with the help of putChar. The edge condition of putStr is the empty string, so if we're printing an empty string, just return an I/O action that does nothing by using return (). If it's not empty, then print the first character of the string by doing putChar and then print of them using putStr.

    +

    putStr is actually defined recursively with the help of putChar. The edge condition of putStr is the empty string, so if we’re printing an empty string, just return an I/O action that does nothing by using return (). If it’s not empty, then print the first character of the string by doing putChar and then print of them using putStr.

     putStr :: String -> IO ()
     putStr [] = return ()
    @@ -213,8 +213,8 @@ 

    Input and Output

    putChar x putStr xs
    -

    See how we can use recursion in I/O, just like we can use it in pure code. Just like in pure code, we define the edge case and then think what the result actually is. It's an action that first outputs the first character and then outputs the rest of the string.

    -

    print takes a value of any type that's an instance of Show (meaning that we know how to represent it as a string), calls show with that value to stringify it and then outputs that string to the terminal. Basically, it's just putStrLn . show. It first runs show on a value and then feeds that to putStrLn, which returns an I/O action that will print out our value.

    +

    See how we can use recursion in I/O, just like we can use it in pure code. Just like in pure code, we define the edge case and then think what the result actually is. It’s an action that first outputs the first character and then outputs the rest of the string.

    +

    print takes a value of any type that’s an instance of Show (meaning that we know how to represent it as a string), calls show with that value to stringify it and then outputs that string to the terminal. Basically, it’s just putStrLn . show. It first runs show on a value and then feeds that to putStrLn, which returns an I/O action that will print out our value.

     main = do   print True
                 print 2
    @@ -230,7 +230,7 @@ 

    Input and Output

    3.2 [3,4,3]
    -

    As you can see, it's a very handy function. Remember how we talked about how I/O actions are performed only when they fall into main or when we try to evaluate them in the GHCI prompt? When we type out a value (like 3 or [1,2,3]) and press the return key, GHCI actually uses print on that value to display it on our terminal!

    +

    As you can see, it’s a very handy function. Remember how we talked about how I/O actions are performed only when they fall into main or when we try to evaluate them in the GHCI prompt? When we type out a value (like 3 or [1,2,3]) and press the return key, GHCI actually uses print on that value to display it on our terminal!

     ghci> 3
     3
    @@ -241,8 +241,8 @@ 

    Input and Output

    ghci> print (map (++"!") ["hey","ho","woo"]) ["hey!","ho!","woo!"]
    -

    When we want to print out strings, we usually use putStrLn because we don't want the quotes around them, but for printing out values of other types to the terminal, print is used the most.

    -

    getChar is an I/O action that reads a character from the input. Thus, its type signature is getChar :: IO Char, because the result contained within the I/O action is a Char. Note that due to buffering, reading of the characters won't actually happen until the user mashes the return key.

    +

    When we want to print out strings, we usually use putStrLn because we don’t want the quotes around them, but for printing out values of other types to the terminal, print is used the most.

    +

    getChar is an I/O action that reads a character from the input. Thus, its type signature is getChar :: IO Char, because the result contained within the I/O action is a Char. Note that due to buffering, reading of the characters won’t actually happen until the user mashes the return key.

     main = do
         c <- getChar
    @@ -252,14 +252,14 @@ 

    Input and Output

    main else return ()
    -

    This program looks like it should read a character and then check if it's a space. If it is, halt execution and if it isn't, print it to the terminal and then do the same thing all over again. Well, it kind of does, only not in the way you might expect. Check this out:

    +

    This program looks like it should read a character and then check if it’s a space. If it is, halt execution and if it isn’t, print it to the terminal and then do the same thing all over again. Well, it kind of does, only not in the way you might expect. Check this out:

     $ runhaskell getchar_test.hs
     hello sir
     hello
     
    -

    The second line is the input. We input hello sir and then press return. Due to buffering, the execution of the program will begin only when after we've hit return and not after every inputted character. But once we press return, it acts on what we've been putting in so far. Try playing with this program to get a feel for it!

    -

    The when function is found in Control.Monad (to get access to it, do import Control.Monad). It's interesting because in a do block it looks like a control flow statement, but it's actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it's False, it returns the return (), action, so an I/O action that doesn't do anything. Here's how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

    +

    The second line is the input. We input hello sir and then press return. Due to buffering, the execution of the program will begin only when after we’ve hit return and not after every inputted character. But once we press return, it acts on what we’ve been putting in so far. Try playing with this program to get a feel for it!

    +

    The when function is found in Control.Monad (to get access to it, do import Control.Monad). It’s interesting because in a do block it looks like a control flow statement, but it’s actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return (), action, so an I/O action that doesn’t do anything. Here’s how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

     import Control.Monad
     
    @@ -269,7 +269,7 @@ 

    Input and Output

    putChar c main
    -

    So as you can see, it's useful for encapsulating the if something then do some I/O action else return () pattern.

    +

    So as you can see, it’s useful for encapsulating the if something then do some I/O action else return () pattern.

    sequence takes a list of I/O actions and returns an I/O actions that will perform those actions one after the other. The result contained in that I/O action will be a list of the results of all the I/O actions that were performed. Its type signature is sequence :: [IO a] -> IO [a]. Doing this:

     main = do
    @@ -285,7 +285,7 @@ 

    Input and Output

    print rs

    So sequence [getLine, getLine, getLine] makes an I/O action that will perform getLine three times. If we bind that action to a name, the result is a list of all the results, so in our case, a list of three things that the user entered at the prompt.

    -

    A common pattern with sequence is when we map functions like print or putStrLn over lists. Doing map print [1,2,3,4] won't create an I/O action. It will create a list of I/O actions, because that's like writing [print 1, print 2, print 3, print 4]. If we want to transform that list of I/O actions into an I/O action, we have to sequence it.

    +

    A common pattern with sequence is when we map functions like print or putStrLn over lists. Doing map print [1,2,3,4] won’t create an I/O action. It will create a list of I/O actions, because that’s like writing [print 1, print 2, print 3, print 4]. If we want to transform that list of I/O actions into an I/O action, we have to sequence it.

     ghci> sequence (map print [1,2,3,4,5])
     1
    @@ -295,8 +295,8 @@ 

    Input and Output

    5 [(),(),(),(),()]
    -

    What's with the [(),(),(),(),()] at the end? Well, when we evaluate an I/O action in GHCI, it's performed and then its result is printed out, unless that result is (), in which case it's not printed out. That's why evaluating putStrLn "hehe" in GHCI just prints out hehe (because the contained result in putStrLn "hehe" is ()). But when we do getLine in GHCI, the result of that I/O action is printed out, because getLine has a type of IO String.

    -

    Because mapping a function that returns an I/O action over a list and then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM takes a function and a list, maps the function over the list and then sequences it. mapM_ does the same, only it throws away the result later. We usually use mapM_ when we don't care what result our sequenced I/O actions have.

    +

    What’s with the [(),(),(),(),()] at the end? Well, when we evaluate an I/O action in GHCI, it’s performed and then its result is printed out, unless that result is (), in which case it’s not printed out. That’s why evaluating putStrLn "hehe" in GHCI just prints out hehe (because the contained result in putStrLn "hehe" is ()). But when we do getLine in GHCI, the result of that I/O action is printed out, because getLine has a type of IO String.

    +

    Because mapping a function that returns an I/O action over a list and then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM takes a function and a list, maps the function over the list and then sequences it. mapM_ does the same, only it throws away the result later. We usually use mapM_ when we don’t care what result our sequenced I/O actions have.

     ghci> mapM print [1,2,3]
     1
    @@ -308,7 +308,7 @@ 

    Input and Output

    2 3
    -

    forever takes an I/O action and returns an I/O action that just repeats the I/O action it got forever. It's located in Control.Monad. This little program will indefinitely ask the user for some input and spit it back to him, CAPSLOCKED:

    +

    forever takes an I/O action and returns an I/O action that just repeats the I/O action it got forever. It’s located in Control.Monad. This little program will indefinitely ask the user for some input and spit it back to him, CAPSLOCKED:

     import Control.Monad
     import Data.Char
    @@ -330,8 +330,8 @@ 

    Input and Output

    putStrLn "The colors that you associate with 1, 2, 3 and 4 are: " mapM putStrLn colors
    -

    The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn't have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it's the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

    -

    You can think of forM as meaning: make an I/O action for every element in this list. What each I/O action will do can depend on the element that was used to make the action. Finally, perform those actions and bind their results to something. We don't have to bind it, we can also just throw it away.

    +

    The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn’t have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it’s the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

    +

    You can think of forM as meaning: make an I/O action for every element in this list. What each I/O action will do can depend on the element that was used to make the action. Finally, perform those actions and bind their results to something. We don’t have to bind it, we can also just throw it away.

     $ runhaskell form_test.hs
     Which color do you associate with the number 1?
    @@ -348,20 +348,20 @@ 

    Input and Output

    red orange
    -

    We could have actually done that without forM, only with forM it's more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

    -

    In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What's special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that's when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

    -

    Don't think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

    +

    We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

    +

    In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

    +

    Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

    Files and streams

    streams -

    getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let's meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What's cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn't read all of the input at once, store it in memory and then bind it to foo. No, it's lazy! It'll say: "Yeah yeah, I'll read the input from the terminal later as we go along, when you really need it!".

    -

    getContents is really useful when we're piping the output from one program into the input of our program. In case you don't know how piping works in unix-y systems, here's a quick primer. Let's make a text file that contains the following little haiku:

    +

    getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: "Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!".

    +

    getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

     I'm a lil' teapot
     What's with that airplane food, huh?
     It's so small, tasteless
     

    Yeah, the haiku sucks, what of it? If anyone knows of any good haiku tutorials, let me know.

    -

    Now, recall the little program we wrote when we were introducing the forever function. It prompted the user for a line, returned it to him in CAPSLOCK and then did that all over again, indefinitely. Just so you don't have to scroll all the way back, here it is again:

    +

    Now, recall the little program we wrote when we were introducing the forever function. It prompted the user for a line, returned it to him in CAPSLOCK and then did that all over again, indefinitely. Just so you don’t have to scroll all the way back, here it is again:

     import Control.Monad
     import Data.Char
    @@ -371,7 +371,7 @@ 

    Input and Output

    l <- getLine putStrLn $ map toUpper l
    -

    We'll save that program as capslocker.hs or something and compile it. And then, we're going to use a unix pipe to feed our text file directly to our little program. We're going to use the help of the GNU cat program, which prints out a file that's given to it as an argument. Check it out, booyaka!

    +

    We’ll save that program as capslocker.hs or something and compile it. And then, we’re going to use a unix pipe to feed our text file directly to our little program. We’re going to use the help of the GNU cat program, which prints out a file that’s given to it as an argument. Check it out, booyaka!

     $ ghc --make capslocker
     [1 of 1] Compiling Main             ( capslocker.hs, capslocker.o )
    @@ -386,8 +386,8 @@ 

    Input and Output

    IT'S SO SMALL, TASTELESS capslocker <stdin>: hGetLine: end of file
    -

    As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we've done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that's usually done by pressing Ctrl-D). It's like running cat haiku.txt and saying: “Wait, don't print this out to the terminal, tell it to capslocker instead!”.

    -

    So what we're essentially doing with that use of forever is taking the input and transforming it into some output. That's why we can use getContents to make our program even shorter and better:

    +

    As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

    +

    So what we’re essentially doing with that use of forever is taking the input and transforming it into some output. That’s why we can use getContents to make our program even shorter and better:

     import Data.Char
     
    @@ -395,7 +395,7 @@ 

    Input and Output

    contents <- getContents putStr (map toUpper contents)
    -

    We run the getContents I/O action and name the string it produces contents. Then, we map toUpper over that string and print that to the terminal. Keep in mind that because strings are basically lists, which are lazy, and getContents is I/O lazy, it won't try to read the whole content at once and store it into memory before printing out the capslocked version. Rather, it will print out the capslocked version as it reads it, because it will only read a line from the input when it really needs to.

    +

    We run the getContents I/O action and name the string it produces contents. Then, we map toUpper over that string and print that to the terminal. Keep in mind that because strings are basically lists, which are lazy, and getContents is I/O lazy, it won’t try to read the whole content at once and store it into memory before printing out the capslocked version. Rather, it will print out the capslocked version as it reads it, because it will only read a line from the input when it really needs to.

     $ cat haiku.txt | ./capslocker
     I'M A LIL' TEAPOT
    @@ -410,8 +410,8 @@ 

    Input and Output

    lets go LETS GO
    -

    We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it's not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that's also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: "Hey, I need a capslocked line!". It doesn't have any lines yet, so it says to contents: "Hey, how about actually getting a line from the terminal?". So that's when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: "Hey, I need the next line, come on!" and this repeats until there's no more input, which is signified by an end-of-file character.

    -

    Let's make program that takes some input and prints out only those lines that are shorter than 10 characters. Observe:

    +

    We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it’s not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that’s also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: "Hey, I need a capslocked line!". It doesn’t have any lines yet, so it says to contents: "Hey, how about actually getting a line from the terminal?". So that’s when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: "Hey, I need the next line, come on!" and this repeats until there’s no more input, which is signified by an end-of-file character.

    +

    Let’s make program that takes some input and prints out only those lines that are shorter than 10 characters. Observe:

     main = do
         contents <- getContents
    @@ -424,8 +424,8 @@ 

    Input and Output

    result = unlines shortLines in result
    -

    We've made our I/O part of the program as short as possible. Because our program is supposed to take some input and print out some output based on the input, we can implement it by reading the input contents, running a function on them and then printing out what the function gave back.

    -

    The shortLinesOnly function works like this: it takes a string, like "short\nlooooooooooooooong\nshort again". That string has three lines, two of them are short and the middle one is long. It runs the lines function on that string, which converts it to ["short", "looooooooooooooong", "short again"], which we then bind to the name allLines. That list of string is then filtered so that only those lines that are shorter than 10 characters remain in the list, producing ["short", "short again"]. And finally, unlines joins that list into a single newline delimited string, giving "short\nshort again". Let's give it a go.

    +

    We’ve made our I/O part of the program as short as possible. Because our program is supposed to take some input and print out some output based on the input, we can implement it by reading the input contents, running a function on them and then printing out what the function gave back.

    +

    The shortLinesOnly function works like this: it takes a string, like "short\nlooooooooooooooong\nshort again". That string has three lines, two of them are short and the middle one is long. It runs the lines function on that string, which converts it to ["short", "looooooooooooooong", "short again"], which we then bind to the name allLines. That list of string is then filtered so that only those lines that are shorter than 10 characters remain in the list, producing ["short", "short again"]. And finally, unlines joins that list into a single newline delimited string, giving "short\nshort again". Let’s give it a go.

     i'm short
     so am i
    @@ -445,7 +445,7 @@ 

    Input and Output

    short

    We pipe the contents of shortlines.txt into the output of shortlinesonly and as the output, we only get the short lines.

    -

    This pattern of getting some string from the input, transforming it with a function and then outputting that is so common that there exists a function which makes that even easier, called interact. interact takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it and then print out the function's result. Let's modify our program to use that.

    +

    This pattern of getting some string from the input, transforming it with a function and then outputting that is so common that there exists a function which makes that even easier, called interact. interact takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it and then print out the function’s result. Let’s modify our program to use that.

     main = interact shortLinesOnly
     
    @@ -456,18 +456,18 @@ 

    Input and Output

    result = unlines shortLines in result
    -

    Just to show that this can be achieved in much less code (even though it will be less readable) and to demonstrate our function composition skill, we're going to rework that a bit further.

    +

    Just to show that this can be achieved in much less code (even though it will be less readable) and to demonstrate our function composition skill, we’re going to rework that a bit further.

     main = interact $ unlines . filter ((<10) . length) . lines
     

    Wow, we actually reduced that to just one line, which is pretty cool!

    -

    interact can be used to make programs that are piped some contents into them and then dump some result out or it can be used to make programs that appear to take a line of input from the user, give back some result based on that line and then take another line and so on. There isn't actually a real distinction between the two, it just depends on how the user is supposed to use them.

    -

    Let's make a program that continuously reads a line and then tells us if the line is a palindrome or not. We could just use getLine to read a line, tell the user if it's a palindrome and then run main all over again. But it's simpler if we use interact. When using interact, think about what you need to do to transform some input into the desired output. In our case, we have to replace each line of the input with either "palindrome" or "not a palindrome". So we have to write a function that transforms something like "elephant\nABCBA\nwhatever" into "not a palindrome\npalindrome\nnot a palindrome". Let's do this!

    +

    interact can be used to make programs that are piped some contents into them and then dump some result out or it can be used to make programs that appear to take a line of input from the user, give back some result based on that line and then take another line and so on. There isn’t actually a real distinction between the two, it just depends on how the user is supposed to use them.

    +

    Let’s make a program that continuously reads a line and then tells us if the line is a palindrome or not. We could just use getLine to read a line, tell the user if it’s a palindrome and then run main all over again. But it’s simpler if we use interact. When using interact, think about what you need to do to transform some input into the desired output. In our case, we have to replace each line of the input with either "palindrome" or "not a palindrome". So we have to write a function that transforms something like "elephant\nABCBA\nwhatever" into "not a palindrome\npalindrome\nnot a palindrome". Let’s do this!

     respondPalindromes contents = unlines (map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") (lines contents))
         where   isPalindrome xs = xs == reverse xs
     
    -

    Let's write this in point-free.

    +

    Let’s write this in point-free.

     respondPalindromes = unlines . map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") . lines
         where   isPalindrome xs = xs == reverse xs
    @@ -476,7 +476,7 @@ 

    Input and Output

     main = interact respondPalindromes
     
    -

    Let's test this out:

    +

    Let’s test this out:

     $ runhaskell palindromes.hs
     hehe
    @@ -487,8 +487,8 @@ 

    Input and Output

    not a palindrome
    -

    Even though we made a program that transforms one big string of input into another, it acts like we made a program that does it line by line. That's because Haskell is lazy and it wants to print the first line of the result string, but it can't because it doesn't have the first line of the input yet. So as soon as we give it the first line of input, it prints the first line of the output. We get out of the program by issuing an end-of-line character.

    -

    We can also use this program by just piping a file into it. Let's say we have this file:

    +

    Even though we made a program that transforms one big string of input into another, it acts like we made a program that does it line by line. That’s because Haskell is lazy and it wants to print the first line of the result string, but it can’t because it doesn’t have the first line of the input yet. So as soon as we give it the first line of input, it prints the first line of the output. We get out of the program by issuing an end-of-line character.

    +

    We can also use this program by just piping a file into it. Let’s say we have this file:

     dogaroo
     radar
    @@ -503,17 +503,17 @@ 

    Input and Output

    palindrome palindrome
    -

    Again, we get the same output as if we had run our program and put in the words ourselves at the standard input. We just don't see the input that palindromes.hs because the input came from the file and not from us typing the words in.

    +

    Again, we get the same output as if we had run our program and put in the words ourselves at the standard input. We just don’t see the input that palindromes.hs because the input came from the file and not from us typing the words in.

    So now you probably see how lazy I/O works and how we can use it to our advantage. You can just think in terms of what the output is supposed to be for some given input and write a function to do that transformation. In lazy I/O, nothing is eaten from the input until it absolutely has to be because what we want to print right now depends on that input.

    -

    So far, we've worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we've already been doing that. One way to think about reading from the terminal is to imagine that it's like reading from a (somewhat special) file. Same goes for writing to the terminal, it's kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we'll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

    -

    We'll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne's #1 hit Girlfriend, and just prints it out to the terminal. Here's girlfriend.txt:

    +

    So far, we’ve worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we’ve already been doing that. One way to think about reading from the terminal is to imagine that it’s like reading from a (somewhat special) file. Same goes for writing to the terminal, it’s kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we’ll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

    +

    We’ll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne’s #1 hit Girlfriend, and just prints it out to the terminal. Here’s girlfriend.txt:

     Hey! Hey! You! You!
     I don't like your girlfriend!
     No way! No way!
     I think you need a new one!
     
    -

    And here's our program:

    +

    And here’s our program:

     import System.IO
     
    @@ -531,23 +531,23 @@ 

    Input and Output

    No way! No way! I think you need a new one!
    -

    Let's go over this line by line. The first line is just four exclamations, to get our attention. In the second line, Avril tells us that she doesn't like our current romantic partner. The third line serves to emphasize that disapproval, whereas the fourth line suggests we should seek out a new girlfriend.

    -

    Let's also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file's associated handle encapsulated as its result.

    +

    Let’s go over this line by line. The first line is just four exclamations, to get our attention. In the second line, Avril tells us that she doesn’t like our current romantic partner. The third line serves to emphasize that disapproval, whereas the fourth line suggests we should seek out a new girlfriend.

    +

    Let’s also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file’s associated handle encapsulated as its result.

    FilePath is just a type synonym for String, simply defined as:

     type FilePath = String
     
    -

    IOMode is a type that's defined like this:

    +

    IOMode is a type that’s defined like this:

     data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
     
    A FILE IN A CAKE!!!

    Just like our type that represents the seven possible values for the days of the week, this type is an enumeration that represents what we want to do with our opened file. Very simple. Just note that this type is IOMode and not IO Mode. IO Mode would be the type of an I/O action that has a value of some type Mode as its result, but IOMode is just a simple enumeration.

    -

    Finally, it returns an I/O action that will open the specified file in the specified mode. If we bind that action to something we get a Handle. A value of type Handle represents where our file is. We'll use that handle so we know which file to read from. It would be stupid to read a file but not bind that read to a handle because we wouldn't be able to do anything with the file. So in our case, we bound the handle to handle.

    -

    In the next line, we see a function called hGetContents. It takes a Handle, so it knows which file to get the contents from and returns an IO String — an I/O action that holds as its result the contents of the file. This function is pretty much like getContents. The only difference is that getContents will automatically read from the standard input (that is from the terminal), whereas hGetContents takes a file handle which tells it which file to read from. In all other respects, they work the same. And just like getContents, hGetContents won't attempt to read the file at once and store it in memory, but it will read it as needed. That's really cool because we can treat contents as the whole contents of the file, but it's not really loaded in memory. So if this were a really huge file, doing hGetContents wouldn't choke up our memory, but it would read only what it needed to from the file, when it needed to.

    -

    Note the difference between the handle used to identify a file and the contents of the file, bound in our program to handle and contents. The handle is just something by which we know what our file is. If you imagine your whole file system to be a really big book and each file is a chapter in the book, the handle is a bookmark that shows where you're currently reading (or writing) a chapter, whereas the contents are the actual chapter.

    +

    Finally, it returns an I/O action that will open the specified file in the specified mode. If we bind that action to something we get a Handle. A value of type Handle represents where our file is. We’ll use that handle so we know which file to read from. It would be stupid to read a file but not bind that read to a handle because we wouldn’t be able to do anything with the file. So in our case, we bound the handle to handle.

    +

    In the next line, we see a function called hGetContents. It takes a Handle, so it knows which file to get the contents from and returns an IO String — an I/O action that holds as its result the contents of the file. This function is pretty much like getContents. The only difference is that getContents will automatically read from the standard input (that is from the terminal), whereas hGetContents takes a file handle which tells it which file to read from. In all other respects, they work the same. And just like getContents, hGetContents won’t attempt to read the file at once and store it in memory, but it will read it as needed. That’s really cool because we can treat contents as the whole contents of the file, but it’s not really loaded in memory. So if this were a really huge file, doing hGetContents wouldn’t choke up our memory, but it would read only what it needed to from the file, when it needed to.

    +

    Note the difference between the handle used to identify a file and the contents of the file, bound in our program to handle and contents. The handle is just something by which we know what our file is. If you imagine your whole file system to be a really big book and each file is a chapter in the book, the handle is a bookmark that shows where you’re currently reading (or writing) a chapter, whereas the contents are the actual chapter.

    With putStr contents we just print the contents out to the standard output and then we do hClose, which takes a handle and returns an I/O action that closes the file. You have to close the file yourself after opening it with openFile!

    -

    Another way of doing what we just did is to use the withFile function, which has a type signature of withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a. It takes a path to a file, an IOMode and then it takes a function that takes a handle and returns some I/O action. What it returns is an I/O action that will open that file, do something we want with the file and then close it. The result encapsulated in the final I/O action that's returned is the same as the result of the I/O action that the function we give it returns. This might sound a bit complicated, but it's really simple, especially with lambdas, here's our previous example rewritten to use withFile:

    +

    Another way of doing what we just did is to use the withFile function, which has a type signature of withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a. It takes a path to a file, an IOMode and then it takes a function that takes a handle and returns some I/O action. What it returns is an I/O action that will open that file, do something we want with the file and then close it. The result encapsulated in the final I/O action that’s returned is the same as the result of the I/O action that the function we give it returns. This might sound a bit complicated, but it’s really simple, especially with lambdas, here’s our previous example rewritten to use withFile:

     import System.IO
     
    @@ -556,7 +556,7 @@ 

    Input and Output

    contents <- hGetContents handle putStr contents)
    -

    As you can see, it's very similar to the previous piece of code. (\handle -> ... ) is the function that takes a handle and returns an I/O action and it's usually done like this, with a lambda. The reason it has to take a function that returns an I/O action instead of just taking an I/O action to do and then close the file is because the I/O action that we'd pass to it wouldn't know on which file to operate. This way, withFile opens the file and then passes the handle to the function we gave it. It gets an I/O action back from that function and then makes an I/O action that's just like it, only it closes the file afterwards. Here's how we can make our own withFile function:

    +

    As you can see, it’s very similar to the previous piece of code. (\handle -> ... ) is the function that takes a handle and returns an I/O action and it’s usually done like this, with a lambda. The reason it has to take a function that returns an I/O action instead of just taking an I/O action to do and then close the file is because the I/O action that we’d pass to it wouldn’t know on which file to operate. This way, withFile opens the file and then passes the handle to the function we gave it. It gets an I/O action back from that function and then makes an I/O action that’s just like it, only it closes the file afterwards. Here’s how we can make our own withFile function:

     withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
     withFile' path mode f = do
    @@ -567,14 +567,14 @@ 

    Input and Output

    butter toast

    We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

    -

    Just like we have hGetContents that works like getContents but for a specific file, there's also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

    +

    Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

    Loading files and then treating their contents as strings is so common that we have these three nice little functions to make our work even easier:

    readFile has a type signature of readFile :: FilePath -> IO String. Remember, FilePath is just a fancy name for String. readFile takes a path to a file and returns an I/O action that will read that file (lazily, of course) and bind its contents to something as a string. -It's usually more handy than doing openFile and binding it to a handle and then doing hGetContents. -Here's how we could have written our previous example with readFile: +It’s usually more handy than doing openFile and binding it to a handle and then doing hGetContents. +Here’s how we could have written our previous example with readFile:

     import System.IO
    @@ -584,11 +584,11 @@ 

    Input and Output

    putStr contents

    -Because we don't get a handle with which to identify our file, we can't close it manually, so Haskell does that for us when we use readFile. +Because we don’t get a handle with which to identify our file, we can’t close it manually, so Haskell does that for us when we use readFile.

    writeFile has a type of writeFile :: FilePath -> String -> IO (). -It takes a path to a file and a string to write to that file and returns an I/O action that will do the writing. If such a file already exists, it will be stomped down to zero length before being written on. Here's how to turn girlfriend.txt into a CAPSLOCKED version and write it to girlfriendcaps.txt: +It takes a path to a file and a string to write to that file and returns an I/O action that will do the writing. If such a file already exists, it will be stomped down to zero length before being written on. Here’s how to turn girlfriend.txt into a CAPSLOCKED version and write it to girlfriendcaps.txt:

     import System.IO
    @@ -606,8 +606,8 @@ 

    Input and Output

    NO WAY! NO WAY! I THINK YOU NEED A NEW ONE!
    -

    appendFile has a type signature that's just like writeFile, only appendFile doesn't truncate the file to zero length if it already exists but it appends stuff to it.

    -

    Let's say we have a file todo.txt that has one task per line that we have to do. Now let's make a program that takes a line from the standard input and adds that to our to-do list.

    +

    appendFile has a type signature that’s just like writeFile, only appendFile doesn’t truncate the file to zero length if it already exists but it appends stuff to it.

    +

    Let’s say we have a file todo.txt that has one task per line that we have to do. Now let’s make a program that takes a line from the standard input and adds that to our to-do list.

     import System.IO
     
    @@ -627,17 +627,17 @@ 

    Input and Output

    Dust the dog Take salad out of the oven
    -

    We needed to add the "\n" to the end of each line because getLine doesn't give us a newline character at the end.

    -

    Ooh, one more thing. We talked about how doing contents <- hGetContents handle doesn't cause the whole file to be read at once and stored in-memory. It's I/O lazy, so doing this:

    +

    We needed to add the "\n" to the end of each line because getLine doesn’t give us a newline character at the end.

    +

    Ooh, one more thing. We talked about how doing contents <- hGetContents handle doesn’t cause the whole file to be read at once and stored in-memory. It’s I/O lazy, so doing this:

     main = do
         withFile "something.txt" ReadMode (\handle -> do
             contents <- hGetContents handle
             putStr contents)
     
    -

    is actually like connecting a pipe from the file to the output. Just like you can think of lists as streams, you can also think of files as streams. This will read one line at a time and print it out to the terminal as it goes along. So you may be asking, how wide is this pipe then? How often will the disk be accessed? Well, for text files, the default buffering is line-buffering usually. That means that the smallest part of the file to be read at once is one line. That's why in this case it actually reads a line, prints it to the output, reads the next line, prints it, etc. For binary files, the default buffering is usually block-buffering. That means that it will read the file chunk by chunk. The chunk size is some size that your operating system thinks is cool.

    -

    You can control how exactly buffering is done by using the hSetBuffering function. It takes a handle and a BufferMode and returns an I/O action that sets the buffering. BufferMode is a simple enumeration data type and the possible values it can hold are: NoBuffering, LineBuffering or BlockBuffering (Maybe Int). The Maybe Int is for how big the chunk should be, in bytes. If it's Nothing, then the operating system determines the chunk size. NoBuffering means that it will be read one character at a time. NoBuffering usually sucks as a buffering mode because it has to access the disk so much.

    -

    Here's our previous piece of code, only it doesn't read it line by line but reads the whole file in chunks of 2048 bytes.

    +

    is actually like connecting a pipe from the file to the output. Just like you can think of lists as streams, you can also think of files as streams. This will read one line at a time and print it out to the terminal as it goes along. So you may be asking, how wide is this pipe then? How often will the disk be accessed? Well, for text files, the default buffering is line-buffering usually. That means that the smallest part of the file to be read at once is one line. That’s why in this case it actually reads a line, prints it to the output, reads the next line, prints it, etc. For binary files, the default buffering is usually block-buffering. That means that it will read the file chunk by chunk. The chunk size is some size that your operating system thinks is cool.

    +

    You can control how exactly buffering is done by using the hSetBuffering function. It takes a handle and a BufferMode and returns an I/O action that sets the buffering. BufferMode is a simple enumeration data type and the possible values it can hold are: NoBuffering, LineBuffering or BlockBuffering (Maybe Int). The Maybe Int is for how big the chunk should be, in bytes. If it’s Nothing, then the operating system determines the chunk size. NoBuffering means that it will be read one character at a time. NoBuffering usually sucks as a buffering mode because it has to access the disk so much.

    +

    Here’s our previous piece of code, only it doesn’t read it line by line but reads the whole file in chunks of 2048 bytes.

     main = do
         withFile "something.txt" ReadMode (\handle -> do
    @@ -646,10 +646,10 @@ 

    Input and Output

    putStr contents)

    Reading files in bigger chunks can help if we want to minimize disk access or when our file is actually a slow network resource.

    -

    We can also use hFlush, which is a function that takes a handle and returns an I/O action that will flush the buffer of the file associated with the handle. When we're doing line-buffering, the buffer is flushed after every line. When we're doing block-buffering, it's after we've read a chunk. It's also flushed after closing a handle. That means that when we've reached a newline character, the reading (or writing) mechanism reports all the data so far. But we can use hFlush to force that reporting of data that has been read so far. After flushing, the data is available to other programs that are running at the same time.

    -

    Think of reading a block-buffered file like this: your toilet bowl is set to flush itself after it has one gallon of water inside it. So you start pouring in water and once the gallon mark is reached, that water is automatically flushed and the data in the water that you've poured in so far is read. But you can flush the toilet manually too by pressing the button on the toilet. This makes the toilet flush and all the water (data) inside the toilet is read. In case you haven't noticed, flushing the toilet manually is a metaphor for hFlush. This is not a very great analogy by programming analogy standards, but I wanted a real world object that can be flushed for the punchline.

    -

    We already made a program to add a new item to our to-do list in todo.txt, now let's make a program to remove an item. I'll just paste the code and then we'll go over the program together so you see that it's really easy. We'll be using a few new functions from System.Directory and one new function from System.IO, but they'll all be explained.

    -

    Anyway, here's the program for removing an item from todo.txt:

    +

    We can also use hFlush, which is a function that takes a handle and returns an I/O action that will flush the buffer of the file associated with the handle. When we’re doing line-buffering, the buffer is flushed after every line. When we’re doing block-buffering, it’s after we’ve read a chunk. It’s also flushed after closing a handle. That means that when we’ve reached a newline character, the reading (or writing) mechanism reports all the data so far. But we can use hFlush to force that reporting of data that has been read so far. After flushing, the data is available to other programs that are running at the same time.

    +

    Think of reading a block-buffered file like this: your toilet bowl is set to flush itself after it has one gallon of water inside it. So you start pouring in water and once the gallon mark is reached, that water is automatically flushed and the data in the water that you’ve poured in so far is read. But you can flush the toilet manually too by pressing the button on the toilet. This makes the toilet flush and all the water (data) inside the toilet is read. In case you haven’t noticed, flushing the toilet manually is a metaphor for hFlush. This is not a very great analogy by programming analogy standards, but I wanted a real world object that can be flushed for the punchline.

    +

    We already made a program to add a new item to our to-do list in todo.txt, now let’s make a program to remove an item. I’ll just paste the code and then we’ll go over the program together so you see that it’s really easy. We’ll be using a few new functions from System.Directory and one new function from System.IO, but they’ll all be explained.

    +

    Anyway, here’s the program for removing an item from todo.txt:

     import System.IO
     import System.Directory
    @@ -674,14 +674,14 @@ 

    Input and Output

    renameFile tempName "todo.txt"

    At first, we just open todo.txt in read mode and bind its handle to handle.

    -

    Next up, we use a function that we haven't met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it's better practice to use openTempFile so you know you're probably not overwriting anything. +

    Next up, we use a function that we haven’t met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it’s better practice to use openTempFile so you know you’re probably not overwriting anything.

    -

    The reason we didn't use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

    +

    The reason we didn’t use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

    Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

    -

    We ask the user which one they want to delete and wait for them to enter a number. Let's say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

    +

    We ask the user which one they want to delete and wait for them to enter a number. Let’s say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

    Remember the delete and !! functions from Data.List. !! returns an element from a list with some index and delete deletes the first occurrence of an element in a list and returns a new list without that occurrence. (todoTasks !! number) (number is now 1) returns "Dust the dog". We bind todoTasks without the first occurrence of "Dust the dog" to newTodoItems and then join that into a single string with unlines before writing it to the temporary file that we opened. The old file is now unchanged and the temporary file contains all the lines that the old one does, except the one we deleted.

    After that we close both the original and the temporary files and then we remove the original one with removeFile, which, as you can see, takes a path to a file and deletes it. After deleting the old todo.txt, we use renameFile to rename the temporary file to todo.txt. Be careful, removeFile and renameFile (which are both in System.Directory by the way) take file paths as their parameters, not handles.

    -

    And that's that! We could have done this in even fewer lines, but we were very careful not to overwrite any existing files and politely asked the operating system to tell us where we can put our temporary file. Let's give this a go!

    +

    And that’s that! We could have done this in even fewer lines, but we were very careful not to overwrite any existing files and politely asked the operating system to tell us where we can put our temporary file. Let’s give this a go!

     $ runhaskell deletetodo.hs
     These are your TO-DO items:
    @@ -707,12 +707,12 @@ 

    Input and Output

    Command line arguments

    COMMAND LINE ARGUMENTS!!! ARGH -

    Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell's standard library has a nice way of getting command line arguments of a program.

    +

    Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line arguments of a program.

    In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

    -

    One way to solve that is to always ask the user which file they want to use as their to-do list. We used that approach when we wanted to know which item the user wants to delete. It works, but it's not so good, because it requires the user to run the program, wait for the program to ask something and then tell that to the program. That's called an interactive program and the difficult bit with interactive command line programs is this — what if you want to automate the execution of that program, like with a batch script? It's harder to make a batch script that interacts with a program than a batch script that just calls one program or several of them.

    -

    That's why it's sometimes better to have the user tell the program what they want when they run the program, instead of having the program ask the user once it's run. And what better way to have the user tell the program what they want it to do when they run it than via command line arguments!

    +

    One way to solve that is to always ask the user which file they want to use as their to-do list. We used that approach when we wanted to know which item the user wants to delete. It works, but it’s not so good, because it requires the user to run the program, wait for the program to ask something and then tell that to the program. That’s called an interactive program and the difficult bit with interactive command line programs is this — what if you want to automate the execution of that program, like with a batch script? It’s harder to make a batch script that interacts with a program than a batch script that just calls one program or several of them.

    +

    That’s why it’s sometimes better to have the user tell the program what they want when they run the program, instead of having the program ask the user once it’s run. And what better way to have the user tell the program what they want it to do when they run it than via command line arguments!

    The System.Environment module has two cool I/O actions. One is getArgs, which has a type of getArgs :: IO [String] and is an I/O action that will get the arguments that the program was run with and have as its contained result a list with the arguments. getProgName has a type of getProgName :: IO String and is an I/O action that contains the program name.

    -

    Here's a small program that demonstrates how these two work:

    +

    Here’s a small program that demonstrates how these two work:

      import System.Environment
      import Data.List
    @@ -725,7 +725,7 @@ 

    Input and Output

    putStrLn "The program name is:" putStrLn progName
    -

    We bind getArgs and progName to args and progName. We say The arguments are: and then for every argument in args, we do putStrLn. Finally, we also print out the program name. Let's compile this as arg-test.

    +

    We bind getArgs and progName to args and progName. We say The arguments are: and then for every argument in args, we do putStrLn. Finally, we also print out the program name. Let’s compile this as arg-test.

     $ ./arg-test first second w00t "multi word arg"
     The arguments are:
    @@ -736,16 +736,16 @@ 

    Input and Output

    The program name is: arg-test
    -

    Nice. Armed with this knowledge you could create some cool command line apps. In fact, let's go ahead and make one. In the previous section, we made a separate program for adding tasks and a separate program for deleting them. Now, we're going to join that into one program, what it does will depend on the command line arguments. We're also going to make it so it can operate on different files, not just todo.txt.

    -

    We'll call it simply todo and it'll be able to do (haha!) three different things:

    +

    Nice. Armed with this knowledge you could create some cool command line apps. In fact, let’s go ahead and make one. In the previous section, we made a separate program for adding tasks and a separate program for deleting them. Now, we’re going to join that into one program, what it does will depend on the command line arguments. We’re also going to make it so it can operate on different files, not just todo.txt.

    +

    We’ll call it simply todo and it’ll be able to do (haha!) three different things:

    • View tasks
    • Add tasks
    • Delete tasks
    -

    We're not going to concern ourselves with possible bad input too much right now.

    -

    Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we'll just do todo view todo.txt and to remove the task with the index of 2, we'll do todo remove todo.txt 2.

    -

    We'll start by making a dispatch association list. It's going to be a simple association list that has command line arguments as keys and functions as their corresponding values. All these functions will be of type [String] -> IO (). They're going to take the argument list as a parameter and return an I/O action that does the viewing, adding, deleting, etc.

    +

    We’re not going to concern ourselves with possible bad input too much right now.

    +

    Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we’ll just do todo view todo.txt and to remove the task with the index of 2, we’ll do todo remove todo.txt 2.

    +

    We’ll start by making a dispatch association list. It’s going to be a simple association list that has command line arguments as keys and functions as their corresponding values. All these functions will be of type [String] -> IO (). They’re going to take the argument list as a parameter and return an I/O action that does the viewing, adding, deleting, etc.

     import System.Environment
     import System.Directory
    @@ -758,7 +758,7 @@ 

    Input and Output

    , ("remove", remove) ]
    -

    We have yet to define main, add, view and remove, so let's start with main:

    +

    We have yet to define main, add, view and remove, so let’s start with main:

     main = do
         (command:args) <- getArgs
    @@ -766,15 +766,15 @@ 

    Input and Output

    action args

    First, we get the arguments and bind them to (command:args). If you remember your pattern matching, this means that the first argument will get bound to command and the rest of them will get bound to args. If we call our program like todo add todo.txt "Spank the monkey", command will be "add" and args will be ["todo.txt", "Spank the monkey"].

    -

    In the next line, we look up our command in the dispatch list. Because "add" points to add, we get Just add as a result. We use pattern matching again to extract our function out of the Maybe. What happens if our command isn't in the dispatch list? Well then the lookup will return Nothing, but we said we won't concern ourselves with failing gracefully too much, so the pattern matching will fail and our program will throw a fit.

    +

    In the next line, we look up our command in the dispatch list. Because "add" points to add, we get Just add as a result. We use pattern matching again to extract our function out of the Maybe. What happens if our command isn’t in the dispatch list? Well then the lookup will return Nothing, but we said we won’t concern ourselves with failing gracefully too much, so the pattern matching will fail and our program will throw a fit.

    Finally, we call our action function with the rest of the argument list. That will return an I/O action that either adds an item, displays a list of items or deletes an item and because that action is part of the main do block, it will get performed. If we follow our concrete example so far and our action function is add, it will get called with args (so ["todo.txt", "Spank the monkey"]) and return an I/O action that adds Spank the monkey to todo.txt.

    -

    Great! All that's left now is to implement add, view and remove. Let's start with add:

    +

    Great! All that’s left now is to implement add, view and remove. Let’s start with add:

     add :: [String] -> IO ()
     add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
     
    -

    If we call our program like todo add todo.txt "Spank the monkey", the "add" will get bound to command in the first pattern match in the main block, whereas ["todo.txt", "Spank the monkey"] will get passed to the function that we get from the dispatch list. So, because we're not dealing with bad input right now, we just pattern match against a list with those two elements right away and return an I/O action that appends that line to the end of the file, along with a newline character.

    -

    Next, let's implement the list viewing functionality. If we want to view the items in a file, we do todo view todo.txt. So in the first pattern match, command will be "view" and args will be ["todo.txt"].

    +

    If we call our program like todo add todo.txt "Spank the monkey", the "add" will get bound to command in the first pattern match in the main block, whereas ["todo.txt", "Spank the monkey"] will get passed to the function that we get from the dispatch list. So, because we’re not dealing with bad input right now, we just pattern match against a list with those two elements right away and return an I/O action that appends that line to the end of the file, along with a newline character.

    +

    Next, let’s implement the list viewing functionality. If we want to view the items in a file, we do todo view todo.txt. So in the first pattern match, command will be "view" and args will be ["todo.txt"].

     view :: [String] -> IO ()
     view [fileName] = do
    @@ -784,7 +784,7 @@ 

    Input and Output

    putStr $ unlines numberedTasks

    We already did pretty much the same thing in the program that only deleted tasks when we were displaying the tasks so that the user can choose one for deletion, only here we just display the tasks.

    -

    And finally, we're going to implement remove. It's going to be very similar to the program that only deleted the tasks, so if you don't understand how deleting an item here works, check out the explanation under that program. The main difference is that we're not hardcoding todo.txt but getting it as an argument. We're also not prompting the user for the task number to delete, we're getting it as an argument.

    +

    And finally, we’re going to implement remove. It’s going to be very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, check out the explanation under that program. The main difference is that we’re not hardcoding todo.txt but getting it as an argument. We’re also not prompting the user for the task number to delete, we’re getting it as an argument.

     remove :: [String] -> IO ()
     remove [fileName, numberString] = do
    @@ -801,7 +801,7 @@ 

    Input and Output

    renameFile tempName fileName

    We opened up the file based on fileName and opened a temporary file, deleted the line with the index that the user wants to delete, wrote that to the temporary file, removed the original file and renamed the temporary file back to fileName.

    -

    Here's the whole program at once, in all its glory!

    +

    Here’s the whole program at once, in all its glory!

     import System.Environment
     import System.Directory
    @@ -846,7 +846,7 @@ 

    Input and Output

    fresh baked salad

    To summarize our solution: we made a dispatch association that maps from commands to functions that take some command line arguments and return an I/O action. We see what the command is and based on that we get the appropriate function from the dispatch list. We call that function with the rest of the command line arguments to get back an I/O action that will do the appropriate thing and then just perform that action!

    In other languages, we might have implemented this with a big switch case statement or whatever, but using higher order functions allows us to just tell the dispatch list to give us the appropriate function and then tell that function to give us an I/O action for some command line arguments.

    -

    Let's try our app out!

    +

    Let’s try our app out!

     $ ./todo view todo.txt
     0 - Iron the dishes
    @@ -868,22 +868,22 @@ 

    Input and Output

    1 - Dust the dog 2 - Pick up children from drycleaners
    -

    Another cool thing about this is that it's easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you're laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

    +

    Another cool thing about this is that it’s easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you’re laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

    You could make this program fail a bit more gracefully in case of bad input (for example, if someone runs todo UP YOURS HAHAHAHA) by making an I/O action that just reports there has been an error (say, errorExit :: IO ()) and then check for possible erroneous input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

    Randomness

    this picture is the ultimate source of randomness and wackiness -

    Many times while programming, you need to get some random data. Maybe you're making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of uses for random data when programming. Well, actually, pseudo-random, because we all know that the only true source of randomness is a monkey on a unicycle with a cheese in one hand and its butt in the other. In this section, we'll take a look at how to make Haskell generate seemingly random data.

    -

    In most other programming languages, you have functions that give you back some random number. Each time you call that function, you get back a (hopefully) different random number. How about Haskell? Well, remember, Haskell is a pure functional language. What that means is that it has referential transparency. What THAT means is that a function, if given the same parameters twice, must produce the same result twice. That's really cool because it allows us to reason differently about programs and it enables us to defer evaluation until we really need it. If I call a function, I can be sure that it won't do any funny stuff before giving me the results. All that matters are its results. However, this makes it a bit tricky for getting random numbers. If I have a function like this:

    +

    Many times while programming, you need to get some random data. Maybe you’re making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of uses for random data when programming. Well, actually, pseudo-random, because we all know that the only true source of randomness is a monkey on a unicycle with a cheese in one hand and its butt in the other. In this section, we’ll take a look at how to make Haskell generate seemingly random data.

    +

    In most other programming languages, you have functions that give you back some random number. Each time you call that function, you get back a (hopefully) different random number. How about Haskell? Well, remember, Haskell is a pure functional language. What that means is that it has referential transparency. What THAT means is that a function, if given the same parameters twice, must produce the same result twice. That’s really cool because it allows us to reason differently about programs and it enables us to defer evaluation until we really need it. If I call a function, I can be sure that it won’t do any funny stuff before giving me the results. All that matters are its results. However, this makes it a bit tricky for getting random numbers. If I have a function like this:

     randomNumber :: (Num a) => a
     randomNumber = 4
     
    -

    It's not very useful as a random number function because it will always return 4, even though I can assure you that the 4 is completely random, because I used a die to determine it.

    +

    It’s not very useful as a random number function because it will always return 4, even though I can assure you that the 4 is completely random, because I used a die to determine it.

    How do other languages make seemingly random numbers? Well, they take various info from your computer, like the current time, how much and where you moved your mouse and what kind of noises you made behind your computer and based on that, give a number that looks really random. The combination of those factors (that randomness) is probably different in any given moment in time, so you get a different random number.

    Ah. So in Haskell, we can make a random number then if we make a function that takes as its parameter that randomness and based on that returns some number (or other data type).

    -

    Enter the System.Random module. It has all the functions that satisfy our need for randomness. Let's just dive into one of the functions it exports then, namely random. Here's its type: random :: (RandomGen g, Random a) => g -> (a, g). Whoa! Some new typeclasses in this type declaration up in here! The RandomGen typeclass is for types that can act as sources of randomness. The Random typeclass is for things that can take on random values. A boolean value can take on a random value, namely True or False. A number can also take up a plethora of different random values. Can a function take on a random value? I don't think so, probably not! If we try to translate the type declaration of random to English, we get something like: it takes a random generator (that's our source of randomness) and returns a random value and a new random generator. Why does it also return a new generator as well as a random value? Well, we'll see in a moment.

    +

    Enter the System.Random module. It has all the functions that satisfy our need for randomness. Let’s just dive into one of the functions it exports then, namely random. Here’s its type: random :: (RandomGen g, Random a) => g -> (a, g). Whoa! Some new typeclasses in this type declaration up in here! The RandomGen typeclass is for types that can act as sources of randomness. The Random typeclass is for things that can take on random values. A boolean value can take on a random value, namely True or False. A number can also take up a plethora of different random values. Can a function take on a random value? I don’t think so, probably not! If we try to translate the type declaration of random to English, we get something like: it takes a random generator (that’s our source of randomness) and returns a random value and a new random generator. Why does it also return a new generator as well as a random value? Well, we’ll see in a moment.

    To use our random function, we have to get our hands on one of those random generators. The System.Random module exports a cool type, namely StdGen that is an instance of the RandomGen typeclass. We can either make a StdGen manually or we can tell the system to give us one based on a multitude of sort of random stuff.

    -

    To manually make a random generator, use the mkStdGen function. It has a type of mkStdGen :: Int -> StdGen. It takes an integer and based on that, gives us a random generator. Okay then, let's try using random and mkStdGen in tandem to get a (hardly random) number.

    +

    To manually make a random generator, use the mkStdGen function. It has a type of mkStdGen :: Int -> StdGen. It takes an integer and based on that, gives us a random generator. Okay then, let’s try using random and mkStdGen in tandem to get a (hardly random) number.

     ghci> random (mkStdGen 100)
     
    @@ -893,7 +893,7 @@

    Input and Output

    `Random a' arising from a use of `random' at <interactive>:1:0-20 Probable fix: add a type signature that fixes these type variable(s)
    -

    What's this? Ah, right, the random function can return a value of any type that's part of the Random typeclass, so we have to inform Haskell what kind of type we want. Also let's not forget that it returns a random value and a random generator in a pair.

    +

    What’s this? Ah, right, the random function can return a value of any type that’s part of the Random typeclass, so we have to inform Haskell what kind of type we want. Also let’s not forget that it returns a random value and a random generator in a pair.

     ghci> random (mkStdGen 100) :: (Int, StdGen)
     (-1352021624,651872571 1655838864)
    @@ -903,7 +903,7 @@ 

    Input and Output

    ghci> random (mkStdGen 100) :: (Int, StdGen) (-1352021624,651872571 1655838864)
    -

    Of course. The same result for the same parameters. So let's try giving it a different random generator as a parameter.

    +

    Of course. The same result for the same parameters. So let’s try giving it a different random generator as a parameter.

     ghci> random (mkStdGen 949494) :: (Int, StdGen)
     (539963926,466647808 1655838864)
    @@ -917,8 +917,8 @@ 

    Input and Output

    ghci> random (mkStdGen 949488) :: (Integer, StdGen) (1691547873,1597344447 1655838864)
    -

    Let's make a function that simulates tossing a coin three times. If random didn't return a new generator along with a random value, we'd have to make this function take three random generators as a parameter and then return coin tosses for each of them. But that sounds wrong because if one generator can make a random value of type Int (which can take on a load of different values), it should be able to make three coin tosses (which can take on precisely eight combinations). So this is where random returning a new generator along with a value really comes in handy.

    -

    We'll represent a coin with a simple Bool. True is tails, False is heads.

    +

    Let’s make a function that simulates tossing a coin three times. If random didn’t return a new generator along with a random value, we’d have to make this function take three random generators as a parameter and then return coin tosses for each of them. But that sounds wrong because if one generator can make a random value of type Int (which can take on a load of different values), it should be able to make three coin tosses (which can take on precisely eight combinations). So this is where random returning a new generator along with a value really comes in handy.

    +

    We’ll represent a coin with a simple Bool. True is tails, False is heads.

     threeCoins :: StdGen -> (Bool, Bool, Bool)
     threeCoins gen =
    @@ -927,7 +927,7 @@ 

    Input and Output

    (thirdCoin, newGen'') = random newGen' in (firstCoin, secondCoin, thirdCoin)
    -

    We call random with the generator we got as a parameter to get a coin and a new generator. Then we call it again, only this time with our new generator, to get the second coin. We do the same for the third coin. Had we called it with the same generator every time, all the coins would have had the same value and we'd only be able to get (False, False, False) or (True, True, True) as a result.

    +

    We call random with the generator we got as a parameter to get a coin and a new generator. Then we call it again, only this time with our new generator, to get the second coin. We do the same for the third coin. Had we called it with the same generator every time, all the coins would have had the same value and we’d only be able to get (False, False, False) or (True, True, True) as a result.

     ghci> threeCoins (mkStdGen 21)
     (True,True,True)
    @@ -938,8 +938,8 @@ 

    Input and Output

    ghci> threeCoins (mkStdGen 944) (True,True,True)
    -

    Notice that we didn't have to do random gen :: (Bool, StdGen). That's because we already specified that we want booleans in the type declaration of the function. That's why Haskell can infer that we want a boolean value in this case.

    -

    So what if we want to flip four coins? Or five? Well, there's a function called randoms that takes a generator and returns an infinite sequence of values based on that generator.

    +

    Notice that we didn’t have to do random gen :: (Bool, StdGen). That’s because we already specified that we want booleans in the type declaration of the function. That’s why Haskell can infer that we want a boolean value in this case.

    +

    So what if we want to flip four coins? Or five? Well, there’s a function called randoms that takes a generator and returns an infinite sequence of values based on that generator.

     ghci> take 5 $ randoms (mkStdGen 11) :: [Int]
     [-1807975507,545074951,-1015194702,-1622477312,-502893664]
    @@ -948,12 +948,12 @@ 

    Input and Output

    ghci> take 5 $ randoms (mkStdGen 11) :: [Float] [7.904789e-2,0.62691015,0.26363158,0.12223756,0.38291094]
    -

    Why doesn't randoms return a new generator as well as a list? We could implement the randoms function very easily like this:

    +

    Why doesn’t randoms return a new generator as well as a list? We could implement the randoms function very easily like this:

     randoms' :: (RandomGen g, Random a) => g -> [a]
     randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
     
    -

    A recursive definition. We get a random value and a new generator from the current generator and then make a list that has the value as its head and random numbers based on the new generator as its tail. Because we have to be able to potentially generate an infinite amount of numbers, we can't give the new random generator back.

    +

    A recursive definition. We get a random value and a new generator from the current generator and then make a list that has the value as its head and random numbers based on the new generator as its tail. Because we have to be able to potentially generate an infinite amount of numbers, we can’t give the new random generator back.

    We could make a function that generates a finite stream of numbers and a new generator like this:

     finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)
    @@ -964,21 +964,21 @@ 

    Input and Output

    in (value:restOfList, finalGen)

    Again, a recursive definition. We say that if we want 0 numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

    -

    What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it's kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

    +

    What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it’s kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

     ghci> randomR (1,6) (mkStdGen 359353)
     (6,1494289578 40692)
     ghci> randomR (1,6) (mkStdGen 35935335)
     (3,1250031057 40692)
     
    -

    There's also randomRs, which produces a stream of random values within our defined ranges. Check this out:

    +

    There’s also randomRs, which produces a stream of random values within our defined ranges. Check this out:

     ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
     "ndkxbvmomg"
     

    Nice, looks like a super secret password or something.

    -

    You may be asking yourself, what does this section have to do with I/O anyway? We haven't done anything concerning I/O so far. Well, so far we've always made our random number generator manually by making it with some arbitrary integer. The problem is, if we do that in our real programs, they will always return the same random numbers, which is no good for us. That's why System.Random offers the getStdGen I/O action, which has a type of IO StdGen. When your program starts, it asks the system for a good random number generator and stores that in a so-called global generator. getStdGen fetches you that global random generator when you bind it to something.

    -

    Here's a simple program that generates a random string.

    +

    You may be asking yourself, what does this section have to do with I/O anyway? We haven’t done anything concerning I/O so far. Well, so far we’ve always made our random number generator manually by making it with some arbitrary integer. The problem is, if we do that in our real programs, they will always return the same random numbers, which is no good for us. That’s why System.Random offers the getStdGen I/O action, which has a type of IO StdGen. When your program starts, it asks the system for a good random number generator and stores that in a so-called global generator. getStdGen fetches you that global random generator when you bind it to something.

    +

    Here’s a simple program that generates a random string.

     import System.Random
     
    @@ -1029,8 +1029,8 @@ 

    Input and Output

    gen' <- newStdGen putStr $ take 20 (randomRs ('a','z') gen')
    -

    Not only do we get a new random generator when we bind newStdGen to something, the global one gets updated as well, so if we do getStdGen again and bind it to something, we'll get a generator that's not the same as gen.

    -

    Here's a little program that will make the user guess which number it's thinking of.

    +

    Not only do we get a new random generator when we bind newStdGen to something, the global one gets updated as well, so if we do getStdGen again and bind it to something, we’ll get a generator that’s not the same as gen.

    +

    Here’s a little program that will make the user guess which number it’s thinking of.

     import System.Random
     import Control.Monad(when)
    @@ -1052,11 +1052,11 @@ 

    Input and Output

    askForNumber newGen
    jack of diamonds -

    We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let's say that the number generated was 7. Then we tell the user to guess which number we're thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn't, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

    -
    Excuse me! If the user gives us some input here that read can't read (like "haha"), our program will crash with an ugly error message. If you don't want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn't consume as the other.
    -

    We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that's just like the one we performed, only it depends on a different generator and we perform it.

    +

    We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

    +
    Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.
    +

    We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

    main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

    -

    Here's our program in action!

    +

    Here’s our program in action!

     $ runhaskell guess_the_number.hs
     Which number in the range from 1 to 10 am I thinking of? 4
    @@ -1088,29 +1088,29 @@ 

    Input and Output

    newStdGen main
    -

    It's very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

    +

    It’s very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

    Bytestrings

    like normal string, only they byte ... what a pedestrian pun this is -

    Lists are a cool and useful data structure. So far, we've used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell's laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That's why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

    -

    However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don't have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn't take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

    -

    That overhead doesn't bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That's why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

    -

    Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can't have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there's less overhead because there are no thunks (the technical term for promise) involved. The downside is that they're likely to fill your memory up faster because they're read into memory at once.

    -

    The other variety of bytestrings resides in Data.ByteString.Lazy. They're lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That's what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it's just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won't cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU's L2 cache.

    -

    If you look through the documentation for Data.ByteString.Lazy, you'll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we're going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

    +

    Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

    +

    However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

    +

    That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

    +

    Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

    +

    The other variety of bytestrings resides in Data.ByteString.Lazy. They’re lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That’s what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it’s just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won’t cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU’s L2 cache.

    +

    If you look through the documentation for Data.ByteString.Lazy, you’ll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we’re going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

     import qualified Data.ByteString.Lazy as B
     import qualified Data.ByteString as S
     
    -

    B has lazy bytestring types and functions, whereas S has strict ones. We'll mostly be using the lazy version.

    -

    The function pack has the type signature pack :: [Word8] -> ByteString. What that means is that it takes a list of bytes of type Word8 and returns a ByteString. You can think of it as taking a list, which is lazy, and making it less lazy, so that it's lazy only at 32 KiB intervals.

    -

    What's the deal with that Word8 type? Well, it's like Int, only that it has a much smaller range, namely 0-255. It represents an 8-bit number. And just like Int, it's in the Num typeclass. For instance, we know that the value 5 is polymorphic in that it can act like any numeral type. Well, it can also take the type of Word8.

    +

    B has lazy bytestring types and functions, whereas S has strict ones. We’ll mostly be using the lazy version.

    +

    The function pack has the type signature pack :: [Word8] -> ByteString. What that means is that it takes a list of bytes of type Word8 and returns a ByteString. You can think of it as taking a list, which is lazy, and making it less lazy, so that it’s lazy only at 32 KiB intervals.

    +

    What’s the deal with that Word8 type? Well, it’s like Int, only that it has a much smaller range, namely 0-255. It represents an 8-bit number. And just like Int, it’s in the Num typeclass. For instance, we know that the value 5 is polymorphic in that it can act like any numeral type. Well, it can also take the type of Word8.

     ghci> B.pack [99,97,110]
     Chunk "can" Empty
     ghci> B.pack [98..120]
     Chunk "bcdefghijklmnopqrstuvwx" Empty
     
    -

    As you can see, you usually don't have to worry about the Word8 too much, because the type system can make the numbers choose that type. If you try to use a big number, like 336 as a Word8, it will just wrap around to 80.

    +

    As you can see, you usually don’t have to worry about the Word8 too much, because the type system can make the numbers choose that type. If you try to use a big number, like 336 as a Word8, it will just wrap around to 80.

    We packed only a handful of values into a ByteString, so they fit inside one chunk. The Empty is like the [] for lists.

    unpack is the inverse function of pack. It takes a bytestring and turns it into a list of bytes.

    fromChunks takes a list of strict bytestrings and converts it to a lazy bytestring. toChunks takes a lazy bytestring and converts it to a list of strict ones.

    @@ -1119,7 +1119,7 @@

    Input and Output

    Chunk "()*" (Chunk "+,-" (Chunk "./0" Empty))

    This is good if you have a lot of small strict bytestrings and you want to process them efficiently without joining them into one big strict bytestring in memory first.

    -

    The bytestring version of : is called cons It takes a byte and a bytestring and puts the byte at the beginning. It's lazy though, so it will make a new chunk even if the first chunk in the bytestring isn't full. That's why it's better to use the strict version of cons, cons' if you're going to be inserting a lot of bytes at the beginning of a bytestring.

    +

    The bytestring version of : is called cons It takes a byte and a bytestring and puts the byte at the beginning. It’s lazy though, so it will make a new chunk even if the first chunk in the bytestring isn’t full. That’s why it’s better to use the strict version of cons, cons' if you’re going to be inserting a lot of bytes at the beginning of a bytestring.

     ghci> B.cons 85 $ B.pack [80,81,82,84]
     Chunk "U" (Chunk "PQRT" Empty)
    @@ -1133,8 +1133,8 @@ 

    Input and Output

    As you can see, empty makes an empty bytestring. See the difference between cons and cons'? With the foldr, we started with an empty bytestring and then went over the list of numbers from the right, adding each number to the beginning of the bytestring. When we used cons, we ended up with one chunk for every byte, which kind of defeats the purpose.

    Otherwise, the bytestring modules have a load of functions that are analogous to those in Data.List, including, but not limited to, head, tail, init, null, length, map, reverse, foldl, foldr, concat, takeWhile, filter, etc.

    -

    It also has functions that have the same name and behave the same as some functions found in System.IO, only Strings are replaced with ByteStrings. For instance, the readFile function in System.IO has a type of readFile :: FilePath -> IO String, while the readFile from the bytestring modules has a type of readFile :: FilePath -> IO ByteString. Watch out, if you're using strict bytestrings and you attempt to read a file, it will read it into memory at once! With lazy bytestrings, it will read it into neat chunks.

    -

    Let's make a simple program that takes two filenames as command-line arguments and copies the first file into the second file. Note that System.Directory already has a function called copyFile, but we're going to implement our own file copying function and program anyway.

    +

    It also has functions that have the same name and behave the same as some functions found in System.IO, only Strings are replaced with ByteStrings. For instance, the readFile function in System.IO has a type of readFile :: FilePath -> IO String, while the readFile from the bytestring modules has a type of readFile :: FilePath -> IO ByteString. Watch out, if you’re using strict bytestrings and you attempt to read a file, it will read it into memory at once! With lazy bytestrings, it will read it into neat chunks.

    +

    Let’s make a simple program that takes two filenames as command-line arguments and copies the first file into the second file. Note that System.Directory already has a function called copyFile, but we’re going to implement our own file copying function and program anyway.

     import System.Environment
     import qualified Data.ByteString.Lazy as B
    @@ -1152,13 +1152,13 @@ 

    Input and Output

     $ runhaskell bytestringcopy.hs something.txt ../../something.txt
     
    -

    Notice that a program that doesn't use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that's not hard.

    -

    Whenever you need better performance in a program that reads a lot of data into strings, give bytestrings a try, chances are you'll get some good performance boosts with very little effort on your part. I usually write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

    +

    Notice that a program that doesn’t use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that’s not hard.

    +

    Whenever you need better performance in a program that reads a lot of data into strings, give bytestrings a try, chances are you’ll get some good performance boosts with very little effort on your part. I usually write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

    Exceptions

    timberr!!!! -

    All languages have procedures, functions, and pieces of code that might fail in some way. That's just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn't be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we've defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

    -

    Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we're not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell's type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

    -

    Despite having expressive types that support failed computations, Haskell still has support for exceptions, because they make more sense in I/O contexts. A lot of things can go wrong when dealing with the outside world because it is so unreliable. For instance, when opening a file, a bunch of things can go wrong. The file might be locked, it might not be there at all or the hard disk drive or something might not be there at all. So it's good to be able to jump to some error handling part of our code when such an error occurs.

    +

    All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn’t be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we’ve defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

    +

    Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we’re not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell’s type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

    +

    Despite having expressive types that support failed computations, Haskell still has support for exceptions, because they make more sense in I/O contexts. A lot of things can go wrong when dealing with the outside world because it is so unreliable. For instance, when opening a file, a bunch of things can go wrong. The file might be locked, it might not be there at all or the hard disk drive or something might not be there at all. So it’s good to be able to jump to some error handling part of our code when such an error occurs.

    Okay, so I/O code (i.e. impure code) can throw exceptions. It makes sense. But what about pure code? Well, it can throw exceptions too. Think about the div and head functions. They have types of (Integral a) => a -> a -> a and [a] -> a, respectively. No Maybe or Either in their return type and yet they can both fail! div explodes in your face if you try to divide by zero and head throws a tantrum when you give it an empty list.

     ghci> 4 `div` 0
    @@ -1166,10 +1166,10 @@ 

    Input and Output

    ghci> head [] *** Exception: Prelude.head: empty list
    -Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it's off to jail. -

    Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we're inside a do block that goes into main). That's because you don't know when (or if) anything will be evaluated in pure code, because it is lazy and doesn't have a well-defined order of execution, whereas I/O code does.

    -

    Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can't do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don't know when they will be evaluated and that it really shouldn't matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That's why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that's bad, because we want to keep the I/O part as small as possible. However, if we don't catch them in the I/O part of our code, our program crashes. The solution? Don't mix exceptions and pure code. Take advantage of Haskell's powerful type system and use types like Either and Maybe to represent results that may have failed.

    -

    That's why we'll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that's part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

    +Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail. +

    Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

    +

    Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can’t do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don’t know when they will be evaluated and that it really shouldn’t matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That’s why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that’s bad, because we want to keep the I/O part as small as possible. However, if we don’t catch them in the I/O part of our code, our program crashes. The solution? Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful type system and use types like Either and Maybe to represent results that may have failed.

    +

    That’s why we’ll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that’s part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

     import System.Environment
     import System.IO
    @@ -1178,12 +1178,12 @@ 

    Input and Output

    contents <- readFile fileName putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
    -

    A very simple program. We perform the getArgs I/O action and bind the first string in the list that it yields to fileName. Then we call the contents of the file with that name contents. Lastly, we apply lines to those contents to get a list of lines and then we get the length of that list and give it to show to get a string representation of that number. It works as expected, but what happens when we give it the name of a file that doesn't exist?

    +

    A very simple program. We perform the getArgs I/O action and bind the first string in the list that it yields to fileName. Then we call the contents of the file with that name contents. Lastly, we apply lines to those contents to get a list of lines and then we get the length of that list and give it to show to get a string representation of that number. It works as expected, but what happens when we give it the name of a file that doesn’t exist?

     $ runhaskell linecount.hs i_dont_exist.txt
     linecount.hs: i_dont_exist.txt: openFile: does not exist (No such file or directory)
     
    -

    Aha, we get an error from GHC, telling us that the file does not exist. Our program crashes. What if we wanted to print out a nicer message if the file doesn't exist? One way to do that is to check if the file exists before trying to open it by using the doesFileExist function from System.Directory.

    +

    Aha, we get an error from GHC, telling us that the file does not exist. Our program crashes. What if we wanted to print out a nicer message if the file doesn’t exist? One way to do that is to check if the file exists before trying to open it by using the doesFileExist function from System.Directory.

     import System.Environment
     import System.IO
    @@ -1196,13 +1196,13 @@ 

    Input and Output

    putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!" else do putStrLn "The file doesn't exist!"
    -

    We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can't just use doesFileExist in an if expression directly.

    -

    Another solution here would be to use exceptions. It's perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

    -

    To deal with this by using exceptions, we're going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

    +

    We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

    +

    Another solution here would be to use exceptions. It’s perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

    +

    To deal with this by using exceptions, we’re going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

    non sequitor -

    If you're familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

    -

    The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can't inspect values of the type IOError by pattern matching against them, just like we can't pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we'll learn in a second.

    -

    So let's put our new friend catch to use!

    +

    If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

    +

    The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

    +

    So let’s put our new friend catch to use!

     import System.Environment
     import System.IO
    @@ -1218,8 +1218,8 @@ 

    Input and Output

    handler :: IOError -> IO () handler e = putStrLn "Whoops, had some trouble!"
    -

    First of all, you'll see that put backticks around it so that we can use it as an infix function, because it takes two parameters. Using it as an infix function makes it more readable. So toTry `catch` handler is the same as catch toTry handler, which fits well with its type. toTry is the I/O action that we try to carry out and handler is the function that takes an IOError and returns an action to be carried out in case of an exception.

    -

    Let's give this a go:

    +

    First of all, you’ll see that put backticks around it so that we can use it as an infix function, because it takes two parameters. Using it as an infix function makes it more readable. So toTry `catch` handler is the same as catch toTry handler, which fits well with its type. toTry is the I/O action that we try to carry out and handler is the function that takes an IOError and returns an action to be carried out in case of an exception.

    +

    Let’s give this a go:

     $ runhaskell count_lines.hs i_exist.txt
     The file has 3 lines!
    @@ -1227,7 +1227,7 @@ 

    Input and Output

    $ runhaskell count_lines.hs i_dont_exist.txt Whoops, had some trouble!
    -

    In the handler, we didn't check to see what kind of IOError we got. We just say "Whoops, had some trouble!" for any kind of error. Just catching all types of exceptions in one handler is bad practice in Haskell just like it is in most other languages. What if some other exception happens that we don't want to catch, like us interrupting the program or something? That's why we're going to do the same thing that's usually done in other languages as well: we'll check to see what kind of exception we got. If it's the kind of exception we're waiting to catch, we do our stuff. If it's not, we throw that exception back into the wild. Let's modify our program to catch only the exceptions caused by a file not existing.

    +

    In the handler, we didn’t check to see what kind of IOError we got. We just say "Whoops, had some trouble!" for any kind of error. Just catching all types of exceptions in one handler is bad practice in Haskell just like it is in most other languages. What if some other exception happens that we don’t want to catch, like us interrupting the program or something? That’s why we’re going to do the same thing that’s usually done in other languages as well: we’ll check to see what kind of exception we got. If it’s the kind of exception we’re waiting to catch, we do our stuff. If it’s not, we throw that exception back into the wild. Let’s modify our program to catch only the exceptions caused by a file not existing.

     import System.Environment
     import System.IO
    @@ -1245,9 +1245,9 @@ 

    Input and Output

    | isDoesNotExistError e = putStrLn "The file doesn't exist!" | otherwise = ioError e
    -

    Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it's a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it's an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it's not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

    -

    So if the exception thrown in the toTry I/O action that we glued together with a do block isn't caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

    -

    There are several predicates that act on IOError and if a guard doesn't evaluate to True, evaluation falls through to the next guard. The predicates that act on IOError are:

    +

    Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

    +

    So if the exception thrown in the toTry I/O action that we glued together with a do block isn’t caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

    +

    There are several predicates that act on IOError and if a guard doesn’t evaluate to True, evaluation falls through to the next guard. The predicates that act on IOError are:

    • isAlreadyExistsError
    • isDoesNotExistError
    • @@ -1258,7 +1258,7 @@

      Input and Output

    • isPermissionError
    • isUserError
    -

    Most of these are pretty self-explanatory. isUserError evaluates to True when we use the function userError to make the exception, which is used for making exceptions from our code and equipping them with a string. For instance, you can do ioError $ userError "remote computer unplugged!", although it's preferred you use types like Either and Maybe to express possible failure instead of throwing exceptions yourself with userError.

    +

    Most of these are pretty self-explanatory. isUserError evaluates to True when we use the function userError to make the exception, which is used for making exceptions from our code and equipping them with a string. For instance, you can do ioError $ userError "remote computer unplugged!", although it’s preferred you use types like Either and Maybe to express possible failure instead of throwing exceptions yourself with userError.

    So you could have a handler that looks something like this:

     handler :: IOError -> IO ()
    @@ -1268,8 +1268,8 @@ 

    Input and Output

    | isIllegalOperation e = notifyCops | otherwise = ioError e
    -

    Where notifyCops and freeSomeSpace are some I/O actions that you define. Be sure to re-throw exceptions if they don't match any of your criteria, otherwise you're causing your program to fail silently in some cases where it shouldn't.

    -

    System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can't print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn't know about anything else. A function depends only on the parameters it was called with. That's why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it's kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let's modify our program to print out the file path that's responsible for the exception occurring.

    +

    Where notifyCops and freeSomeSpace are some I/O actions that you define. Be sure to re-throw exceptions if they don’t match any of your criteria, otherwise you’re causing your program to fail silently in some cases where it shouldn’t.

    +

    System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can’t print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn’t know about anything else. A function depends only on the parameters it was called with. That’s why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it’s kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let’s modify our program to print out the file path that’s responsible for the exception occurring.

     import System.Environment
     import System.IO
    @@ -1290,14 +1290,14 @@ 

    Input and Output

    | otherwise = ioError e

    In the guard where isDoesNotExistError is True, we used a case expression to call ioeGetFileName with e and then pattern match against the Maybe value that it returned. Using case expressions is commonly used when you want to pattern match against something without bringing in a new function.

    -

    You don't have to use one handler to catch exceptions in your whole I/O part. You can just cover certain parts of your I/O code with catch or you can cover several of them with catch and use different handlers for them, like so:

    +

    You don’t have to use one handler to catch exceptions in your whole I/O part. You can just cover certain parts of your I/O code with catch or you can cover several of them with catch and use different handlers for them, like so:

     main = do toTry `catch` handler1
               thenTryThis `catch` handler2
               launchRockets
     
    -

    Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn't a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

    -

    Now you know how to deal with I/O exceptions! Throwing exceptions from pure code and dealing with them hasn't been covered here, mainly because, like we said, Haskell offers much better ways to indicate errors than reverting to I/O to catch them. Even when glueing together I/O actions that might fail, I prefer to have their type be something like IO (Either a b), meaning that they're normal I/O actions but the result that they yield when performed is of type Either a b, meaning it's either Left a or Right b.

    +

    Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn’t a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

    +

    Now you know how to deal with I/O exceptions! Throwing exceptions from pure code and dealing with them hasn’t been covered here, mainly because, like we said, Haskell offers much better ways to indicate errors than reverting to I/O to catch them. Even when glueing together I/O actions that might fail, I prefer to have their type be something like IO (Either a b), meaning that they’re normal I/O actions but the result that they yield when performed is of type Either a b, meaning it’s either Left a or Right b.

    • diff --git a/docs/introduction.html b/docs/introduction.html index 817e919..fda58d7 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -34,35 +34,35 @@

      Introduction

      About this tutorial

      Welcome to Learn You a Haskell for Great Good! -If you're reading this, chances are you want to learn Haskell. Well, you've come to the right place, but let's talk about this tutorial a bit first. +If you’re reading this, chances are you want to learn Haskell. Well, you’ve come to the right place, but let’s talk about this tutorial a bit first.

      -I decided to write this because I wanted to solidify my own knowledge of Haskell and because I thought I could help people new to Haskell learn it from my perspective. There are quite a few tutorials on Haskell floating around on the internet. When I was starting out in Haskell, I didn't learn from just one resource. The way I learned it was by reading several different tutorials and articles because each explained something in a different way than the other did. By going through several resources, I was able to put together the pieces and it all just came falling into place. So this is an attempt at adding another useful resource for learning Haskell so you have a bigger chance of finding one you like. +I decided to write this because I wanted to solidify my own knowledge of Haskell and because I thought I could help people new to Haskell learn it from my perspective. There are quite a few tutorials on Haskell floating around on the internet. When I was starting out in Haskell, I didn’t learn from just one resource. The way I learned it was by reading several different tutorials and articles because each explained something in a different way than the other did. By going through several resources, I was able to put together the pieces and it all just came falling into place. So this is an attempt at adding another useful resource for learning Haskell so you have a bigger chance of finding one you like.

      bird

      -This tutorial is aimed at people who have experience in imperative programming languages (C, C++, Java, Python …) but haven't programmed in a functional language before (Haskell, ML, OCaml …). Although I bet that even if you don't have any significant programming experience, a smart person such as yourself will be able to follow along and learn Haskell. +This tutorial is aimed at people who have experience in imperative programming languages (C, C++, Java, Python …) but haven’t programmed in a functional language before (Haskell, ML, OCaml …). Although I bet that even if you don’t have any significant programming experience, a smart person such as yourself will be able to follow along and learn Haskell.

      -The channel #haskell on the Libera.Chat network is a great place to ask questions if you're feeling stuck. People there are extremely nice, patient and understanding to newbies. +The channel #haskell on the Libera.Chat network is a great place to ask questions if you’re feeling stuck. People there are extremely nice, patient and understanding to newbies.

      -I failed to learn Haskell approximately 2 times before finally grasping it because it all just seemed too weird to me and I didn't get it. But then once it just "clicked" and after getting over that initial hurdle, it was pretty much smooth sailing. I guess what I'm trying to say is: Haskell is great and if you're interested in programming you should really learn it even if it seems weird at first. Learning Haskell is much like learning to program for the first time — it's fun! It forces you to think differently, which brings us to the next section … +I failed to learn Haskell approximately 2 times before finally grasping it because it all just seemed too weird to me and I didn’t get it. But then once it just "clicked" and after getting over that initial hurdle, it was pretty much smooth sailing. I guess what I’m trying to say is: Haskell is great and if you’re interested in programming you should really learn it even if it seems weird at first. Learning Haskell is much like learning to program for the first time — it’s fun! It forces you to think differently, which brings us to the next section …

      -

      So what's Haskell?

      +

      So what’s Haskell?

      fx Haskell is a purely functional programming language. -In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don't tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can't set a variable to something and then set it to something else later. If you say that a is 5, you can't say it's something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it's guaranteed to return the same result. That's called referential transparency and not only does it allow the compiler to reason about the program's behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together. +In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together.

      lazy -Haskell is lazy. That means that unless specifically told otherwise, Haskell won't execute functions and calculate things until it's really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you "Yeah yeah, I'll do it later!". But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end. +Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you "Yeah yeah, I’ll do it later!". But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end.

      boat -Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don't have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don't have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don't explicitly state their type, the function will work on any two parameters that act like numbers. +Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers.

      Haskell is elegant and concise. Because it uses a lot of high level concepts, Haskell programs are usually shorter than their imperative equivalents. And shorter programs are easier to maintain than longer ones and have less bugs. @@ -72,10 +72,10 @@

      About this tutorial

      What you need to dive in

      -A text editor and a Haskell compiler. You probably already have your favorite text editor installed so we won't waste time on that. For the purposes of this tutorial we'll be using GHC, the most widely used Haskell compiler. The best way to get started is to download GHCup, which is the recommended Haskell installer. +A text editor and a Haskell compiler. You probably already have your favorite text editor installed so we won’t waste time on that. For the purposes of this tutorial we’ll be using GHC, the most widely used Haskell compiler. The best way to get started is to download GHCup, which is the recommended Haskell installer.

      -GHC can take a Haskell file (they usually have a .hs extension) and compile it but it also has an interactive mode which allows you to interactively interact with files. Interactively. You can call functions from files that you load and the results are displayed immediately. For learning it's a lot easier and faster than compiling every time you make a change and then running the program from the prompt. The interactive mode is invoked by typing in ghci at your prompt. If you have defined some functions in a file called, say, myfunctions.hs, you load up those functions by typing in :l myfunctions and then you can play with them, provided myfunctions.hs is in the same folder from which ghci was invoked. If you change the .hs file, just run :l myfunctions again or do :r, which is equivalent because it reloads the current file. The usual workflow for me when playing around in stuff is defining some functions in a .hs file, loading it up and messing around with them and then changing the .hs file, loading it up again and so on. This is also what we'll be doing here. +GHC can take a Haskell file (they usually have a .hs extension) and compile it but it also has an interactive mode which allows you to interactively interact with files. Interactively. You can call functions from files that you load and the results are displayed immediately. For learning it’s a lot easier and faster than compiling every time you make a change and then running the program from the prompt. The interactive mode is invoked by typing in ghci at your prompt. If you have defined some functions in a file called, say, myfunctions.hs, you load up those functions by typing in :l myfunctions and then you can play with them, provided myfunctions.hs is in the same folder from which ghci was invoked. If you change the .hs file, just run :l myfunctions again or do :r, which is equivalent because it reloads the current file. The usual workflow for me when playing around in stuff is defining some functions in a .hs file, loading it up and messing around with them and then changing the .hs file, loading it up again and so on. This is also what we’ll be doing here.

        diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index 5e29a72..bfd0d6d 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -32,51 +32,51 @@

      Making Our Own Types and Typeclasses

      -

      In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we'll learn how to make our own and how to put them to work!

      +

      In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we’ll learn how to make our own and how to put them to work!

      Algebraic data types intro

      -

      So far, we've run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let's see how the Bool type is defined in the standard library.

      +

      So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

       data Bool = False | True
       
      -

      data means that we're defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

      +

      data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

      In a similar fashion, we can think of the Int type as being defined like this:

       data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
       
      caveman -

      The first and last value constructors are the minimum and maximum possible values of Int. It's not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so this is just for illustrative purposes.

      -

      Now, let's think about how we would represent a shape in Haskell. One way would be to use tuples. A circle could be denoted as (43.1, 55.0, 10.4) where the first and second fields are the coordinates of the circle's center and the third field is the radius. Sounds OK, but those could also represent a 3D vector or anything else. A better solution would be to make our own type to represent a shape. Let's say that a shape can be a circle or a rectangle. Here it is:

      +

      The first and last value constructors are the minimum and maximum possible values of Int. It’s not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so this is just for illustrative purposes.

      +

      Now, let’s think about how we would represent a shape in Haskell. One way would be to use tuples. A circle could be denoted as (43.1, 55.0, 10.4) where the first and second fields are the coordinates of the circle’s center and the third field is the radius. Sounds OK, but those could also represent a 3D vector or anything else. A better solution would be to make our own type to represent a shape. Let’s say that a shape can be a circle or a rectangle. Here it is:

       data Shape = Circle Float Float Float | Rectangle Float Float Float Float
       

      -Now what's this? Think of it like this. The Circle value constructor has three fields, which take floats. So when we write a value constructor, we can optionally add some types after it and those types define the values it will contain. Here, the first two fields are the coordinates of its center, the third one its radius. The Rectangle value constructor has four fields which accept floats. The first two are the coordinates to its upper left corner and the second two are coordinates to its lower right one. +Now what’s this? Think of it like this. The Circle value constructor has three fields, which take floats. So when we write a value constructor, we can optionally add some types after it and those types define the values it will contain. Here, the first two fields are the coordinates of its center, the third one its radius. The Rectangle value constructor has four fields which accept floats. The first two are the coordinates to its upper left corner and the second two are coordinates to its lower right one.

      -

      Now when I say fields, I actually mean parameters. Value constructors are actually functions that ultimately return a value of a data type. Let's take a look at the type signatures for these two value constructors.

      +

      Now when I say fields, I actually mean parameters. Value constructors are actually functions that ultimately return a value of a data type. Let’s take a look at the type signatures for these two value constructors.

       ghci> :t Circle
       Circle :: Float -> Float -> Float -> Shape
       ghci> :t Rectangle
       Rectangle :: Float -> Float -> Float -> Float -> Shape
       
      -

      Cool, so value constructors are functions like everything else. Who would have thought? Let's make a function that takes a shape and returns its surface.

      +

      Cool, so value constructors are functions like everything else. Who would have thought? Let’s make a function that takes a shape and returns its surface.

       surface :: Shape -> Float
       surface (Circle _ _ r) = pi * r ^ 2
       surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
       
      -

      The first notable thing here is the type declaration. It says that the function takes a shape and returns a float. We couldn't write a type declaration of Circle -> Float because Circle is not a type, Shape is. Just like we can't write a function with a type declaration of True -> Int. The next thing we notice here is that we can pattern match against constructors. We pattern matched against constructors before (all the time actually) when we pattern matched against values like [] or False or 5, only those values didn't have any fields. We just write a constructor and then bind its fields to names. Because we're interested in the radius, we don't actually care about the first two fields, which tell us where the circle is.

      +

      The first notable thing here is the type declaration. It says that the function takes a shape and returns a float. We couldn’t write a type declaration of Circle -> Float because Circle is not a type, Shape is. Just like we can’t write a function with a type declaration of True -> Int. The next thing we notice here is that we can pattern match against constructors. We pattern matched against constructors before (all the time actually) when we pattern matched against values like [] or False or 5, only those values didn’t have any fields. We just write a constructor and then bind its fields to names. Because we’re interested in the radius, we don’t actually care about the first two fields, which tell us where the circle is.

       ghci> surface $ Circle 10 20 10
       314.15927
       ghci> surface $ Rectangle 0 0 100 100
       10000.0
       
      -

      Yay, it works! But if we try to just print out Circle 10 20 5 in the prompt, we'll get an error. That's because Haskell doesn't know how to display our data type as a string (yet). Remember, when we try to print a value out in the prompt, Haskell first runs the show function to get the string representation of our value and then it prints that out to the terminal. To make our Shape type part of the Show typeclass, we modify it like this:

      +

      Yay, it works! But if we try to just print out Circle 10 20 5 in the prompt, we’ll get an error. That’s because Haskell doesn’t know how to display our data type as a string (yet). Remember, when we try to print a value out in the prompt, Haskell first runs the show function to get the string representation of our value and then it prints that out to the terminal. To make our Shape type part of the Show typeclass, we modify it like this:

       data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)
       
      -

      We won't concern ourselves with deriving too much for now. Let's just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

      +

      We won’t concern ourselves with deriving too much for now. Let’s just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

       ghci> Circle 10 20 5
       Circle 10.0 20.0 5.0
      @@ -88,12 +88,12 @@ 

      Making Our Own Types and Typeclasses

      ghci> map (Circle 10 20) [4,5,6,6] [Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]
      -

      Our data type is good, although it could be better. Let's make an intermediate data type that defines a point in two-dimensional space. Then we can use that to make our shapes more understandable.

      +

      Our data type is good, although it could be better. Let’s make an intermediate data type that defines a point in two-dimensional space. Then we can use that to make our shapes more understandable.

       data Point = Point Float Float deriving (Show)
       data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
       
      -

      Notice that when defining a point, we used the same name for the data type and the value constructor. This has no special meaning, although it's common to use the same name as the type if there's only one value constructor. So now the Circle has two fields, one is of type Point and the other of type Float. This makes it easier to understand what's what. Same goes for the rectangle. We have to adjust our surface function to reflect these changes.

      +

      Notice that when defining a point, we used the same name for the data type and the value constructor. This has no special meaning, although it’s common to use the same name as the type if there’s only one value constructor. So now the Circle has two fields, one is of type Point and the other of type Float. This makes it easier to understand what’s what. Same goes for the rectangle. We have to adjust our surface function to reflect these changes.

       surface :: Shape -> Float
       surface (Circle _ r) = pi * r ^ 2
      @@ -106,7 +106,7 @@ 

      Making Our Own Types and Typeclasses

      ghci> surface (Circle (Point 0 0) 24) 1809.5574
      -

      How about a function that nudges a shape? It takes a shape, the amount to move it on the x axis and the amount to move it on the y axis and then returns a new shape that has the same dimensions, only it's located somewhere else.

      +

      How about a function that nudges a shape? It takes a shape, the amount to move it on the x axis and the amount to move it on the y axis and then returns a new shape that has the same dimensions, only it’s located somewhere else.

       nudge :: Shape -> Float -> Float -> Shape
       nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
      @@ -117,7 +117,7 @@ 

      Making Our Own Types and Typeclasses

      ghci> nudge (Circle (Point 34 34) 10) 5 10 Circle (Point 39.0 44.0) 10.0
      -

      If we don't want to deal directly with points, we can make some auxiliary functions that create shapes of some size at the zero coordinates and then nudge those.

      +

      If we don’t want to deal directly with points, we can make some auxiliary functions that create shapes of some size at the zero coordinates and then nudge those.

       baseCircle :: Float -> Shape
       baseCircle r = Circle (Point 0 0) r
      @@ -141,22 +141,22 @@ 

      Making Our Own Types and Typeclasses

      , baseRect ) where
      -

      By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It's the same as writing Shape (Rectangle, Circle).

      -

      We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can't create a map by doing Map.Map [(1,2),(3,4)] because it doesn't export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

      -

      Not exporting the value constructors of a data types makes them more abstract in such a way that we hide their implementation. Also, whoever uses our module can't pattern match against the value constructors.

      +

      By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It’s the same as writing Shape (Rectangle, Circle).

      +

      We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can’t create a map by doing Map.Map [(1,2),(3,4)] because it doesn’t export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

      +

      Not exporting the value constructors of a data types makes them more abstract in such a way that we hide their implementation. Also, whoever uses our module can’t pattern match against the value constructors.

      Record syntax

      record -

      OK, we've been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I don't know about you, but that's all I ever want to know about a person. Let's give it a go!

      +

      OK, we’ve been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I don’t know about you, but that’s all I ever want to know about a person. Let’s give it a go!

       data Person = Person String String Int Float String String deriving (Show)
       
      -

      O-kay. The first field is the first name, the second is the last name, the third is the age and so on. Let's make a person.

      +

      O-kay. The first field is the first name, the second is the last name, the third is the age and so on. Let’s make a person.

       ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
       ghci> guy
       Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
       
      -

      That's kind of cool, although slightly unreadable. What if we want to create a function to get separate info from a person? A function that gets some person's first name, a function that gets some person's last name, etc. Well, we'd have to define them kind of like this.

      +

      That’s kind of cool, although slightly unreadable. What if we want to create a function to get separate info from a person? A function that gets some person’s first name, a function that gets some person’s last name, etc. Well, we’d have to define them kind of like this.

       firstName :: Person -> String
       firstName (Person firstname _ _ _ _ _) = firstname
      @@ -186,8 +186,8 @@ 

      Making Our Own Types and Typeclasses

      ghci> flavor guy "Chocolate"
      -

      There must be a better way, you say! Well no, there isn't, sorry.

      -

      Just kidding, there is. Hahaha! The makers of Haskell were very smart and anticipated this scenario. They included an alternative way to write data types. Here's how we could achieve the above functionality with record syntax.

      +

      There must be a better way, you say! Well no, there isn’t, sorry.

      +

      Just kidding, there is. Hahaha! The makers of Haskell were very smart and anticipated this scenario. They included an alternative way to write data types. Here’s how we could achieve the above functionality with record syntax.

       data Person = Person { firstName :: String
                            , lastName :: String
      @@ -203,7 +203,7 @@ 

      Making Our Own Types and Typeclasses

      ghci> :t firstName firstName :: Person -> String
      -

      There's another benefit to using record syntax. When we derive Show for the type, it displays it differently if we use record syntax to define and instantiate the type. Say we have a type that represents a car. We want to keep track of the company that made it, the model name and its year of production. Watch.

      +

      There’s another benefit to using record syntax. When we derive Show for the type, it displays it differently if we use record syntax to define and instantiate the type. Say we have a type that represents a car. We want to keep track of the company that made it, the model name and its year of production. Watch.

       data Car = Car String String Int deriving (Show)
       
      @@ -219,18 +219,18 @@

      Making Our Own Types and Typeclasses

      ghci> Car {company="Ford", model="Mustang", year=1967} Car {company = "Ford", model = "Mustang", year = 1967}
    -

    When making a new car, we don't have to necessarily put the fields in the proper order, as long as we list all of them. But if we don't use record syntax, we have to specify them in order.

    -

    Use record syntax when a constructor has several fields and it's not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it's pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn't so obvious and we greatly benefited from using record syntax.

    +

    When making a new car, we don’t have to necessarily put the fields in the proper order, as long as we list all of them. But if we don’t use record syntax, we have to specify them in order.

    +

    Use record syntax when a constructor has several fields and it’s not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it’s pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn’t so obvious and we greatly benefited from using record syntax.

    Type parameters

    -

    A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it's not that complicated. If you're familiar with templates in C++, you'll see some parallels. To get a clear picture of what type parameters work like in action, let's take a look at how a type we've already met is implemented.

    +

    A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

     data Maybe a = Nothing | Just a
     
    yeti -

    The a here is the type parameter. And because there's a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it's not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that's not a type per se, it's a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

    +

    The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that’s not a type per se, it’s a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

    So if we pass Char as the type parameter to Maybe, we get a type of Maybe Char. The value Just 'a' has a type of Maybe Char, for example.

    -

    You might not know it, but we used a type that has a type parameter before we used Maybe. That type is the list type. Although there's some syntactic sugar in play, the list type takes a parameter to produce a concrete type. Values can have an [Int] type, a [Char] type, a [[String]] type, but you can't have a value that just has a type of [].

    -

    Let's play around with the Maybe type.

    +

    You might not know it, but we used a type that has a type parameter before we used Maybe. That type is the list type. Although there’s some syntactic sugar in play, the list type takes a parameter to produce a concrete type. Values can have an [Int] type, a [Char] type, a [[String]] type, but you can’t have a value that just has a type of [].

    +

    Let’s play around with the Maybe type.

     ghci> Just "Haha"
     Just "Haha"
    @@ -246,8 +246,8 @@ 

    Making Our Own Types and Typeclasses

    Just 10.0

    Type parameters are useful because we can make different types with them depending on what kind of types we want contained in our data type. When we do :t Just "Haha", the type inference engine figures it out to be of the type Maybe [Char], because if the a in the Just a is a string, then the a in Maybe a must also be a string.

    -

    Notice that the type of Nothing is Maybe a. Its type is polymorphic. If some function requires a Maybe Int as a parameter, we can give it a Nothing, because a Nothing doesn't contain a value anyway and so it doesn't matter. The Maybe a type can act like a Maybe Int if it has to, just like 5 can act like an Int or a Double. Similarly, the type of the empty list is [a]. An empty list can act like a list of anything. That's why we can do [1,2,3] ++ [] and ["ha","ha","ha"] ++ [].

    -

    Using type parameters is very beneficial, but only when using them makes sense. Usually we use them when our data type would work regardless of the type of the value it then holds inside it, like with our Maybe a type. If our type acts as some kind of box, it's good to use them. We could change our Car data type from this:

    +

    Notice that the type of Nothing is Maybe a. Its type is polymorphic. If some function requires a Maybe Int as a parameter, we can give it a Nothing, because a Nothing doesn’t contain a value anyway and so it doesn’t matter. The Maybe a type can act like a Maybe Int if it has to, just like 5 can act like an Int or a Double. Similarly, the type of the empty list is [a]. An empty list can act like a list of anything. That’s why we can do [1,2,3] ++ [] and ["ha","ha","ha"] ++ [].

    +

    Using type parameters is very beneficial, but only when using them makes sense. Usually we use them when our data type would work regardless of the type of the value it then holds inside it, like with our Maybe a type. If our type acts as some kind of box, it’s good to use them. We could change our Car data type from this:

     data Car = Car { company :: String
                    , model :: String
    @@ -261,7 +261,7 @@ 

    Making Our Own Types and Typeclasses

    , year :: c } deriving (Show)
    -

    But would we really benefit? The answer is: probably no, because we'd just end up defining functions that only work on the Car String String Int type. For instance, given our first definition of Car, we could make a function that displays the car's properties in a nice little text.

    +

    But would we really benefit? The answer is: probably no, because we’d just end up defining functions that only work on the Car String String Int type. For instance, given our first definition of Car, we could make a function that displays the car’s properties in a nice little text.

     tellCar :: Car -> String
     tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
    @@ -276,7 +276,7 @@ 

    Making Our Own Types and Typeclasses

    tellCar :: (Show a) => Car String String a -> String tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
    -

    We'd have to force this function to take a Car type of (Show a) => Car String String a. You can see that the type signature is more complicated and the only benefit we'd actually get would be that we can use any type that's an instance of the Show typeclass as the type for c.

    +

    We’d have to force this function to take a Car type of (Show a) => Car String String a. You can see that the type signature is more complicated and the only benefit we’d actually get would be that we can use any type that’s an instance of the Show typeclass as the type for c.

     ghci> tellCar (Car "Ford" "Mustang" 1967)
     "This Ford Mustang was made in 1967"
    @@ -288,15 +288,15 @@ 

    Making Our Own Types and Typeclasses

    Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char]
    meekrat -

    In real life though, we'd end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn't really worth it. We usually use type parameters when the type that's contained inside the data type's various value constructors isn't really that important for the type to work. A list of stuff is a list of stuff and it doesn't matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn't matter what the type of that something is.

    +

    In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We usually use type parameters when the type that’s contained inside the data type’s various value constructors isn’t really that important for the type to work. A list of stuff is a list of stuff and it doesn’t matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn’t matter what the type of that something is.

    -

    Another example of a parameterized type that we've already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

    +

    Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

     data (Ord k) => Map k v = ...
     
    -

    However, it's a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don't benefit a lot, but we end up writing more class constraints, even when we don't need them. If we put or don't put the Ord k constraint in the data declaration for Map k v, we're going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don't put the constraint in the data declaration, we don't have to put (Ord k) => in the type declarations of functions that don't care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn't do any comparing of keys by order.

    -

    So don't put type constraints into data declarations even if it seems to make sense, because you'll have to put them into the function type declarations either way.

    -

    Let's implement a 3D vector type and add some operations for it. We'll be using a parameterized type because even though it will usually contain numeric types, it will still support several of them.

    +

    However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

    +

    So don’t put type constraints into data declarations even if it seems to make sense, because you’ll have to put them into the function type declarations either way.

    +

    Let’s implement a 3D vector type and add some operations for it. We’ll be using a parameterized type because even though it will usually contain numeric types, it will still support several of them.

     data Vector a = Vector a a a deriving (Show)
     
    @@ -309,8 +309,8 @@ 

    Making Our Own Types and Typeclasses

    scalarMult :: (Num t) => Vector t -> Vector t -> t (Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n
    -

    vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you'll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn't put a Num class constraint in the data declaration, because we'd have to repeat it in the functions anyway.

    -

    Once again, it's very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |'s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let's play around with our vectors.

    +

    vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

    +

    Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

     ghci> Vector 3 5 8 `vplus` Vector 9 2 8
     Vector 12 7 16
    @@ -325,9 +325,9 @@ 

    Making Our Own Types and Typeclasses

    Derived instances

    gob -

    In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That's why expressions like 4 == 4 and "foo" /= "bar" typecheck.

    -

    We also mentioned that they're often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don't make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

    -

    In the next section, we'll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let's see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

    +

    In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

    +

    We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

    +

    In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

    Consider this data type:

     data Person = Person { firstName :: String
    @@ -335,14 +335,14 @@ 

    Making Our Own Types and Typeclasses

    , age :: Int }
    -

    It describes a person. Let's assume that no two people have the same combination of first name, last name and age. Now, if we have records for two people, does it make sense to see if they represent the same person? Sure it does. We can try to equate them and see if they're equal or not. That's why it would make sense for this type to be part of the Eq typeclass. We'll derive the instance.

    +

    It describes a person. Let’s assume that no two people have the same combination of first name, last name and age. Now, if we have records for two people, does it make sense to see if they represent the same person? Sure it does. We can try to equate them and see if they’re equal or not. That’s why it would make sense for this type to be part of the Eq typeclass. We’ll derive the instance.

     data Person = Person { firstName :: String
                          , lastName :: String
                          , age :: Int
                          } deriving (Eq)
     
    -

    When we derive the Eq instance for a type and then try to compare two values of that type with == or /=, Haskell will see if the value constructors match (there's only one value constructor here though) and then it will check if all the data contained inside matches by testing each pair of fields with ==. There's only one catch though, the types of all the fields also have to be part of the Eq typeclass. But since both String and Int are, we're OK. Let's test our Eq instance.

    +

    When we derive the Eq instance for a type and then try to compare two values of that type with == or /=, Haskell will see if the value constructors match (there’s only one value constructor here though) and then it will check if all the data contained inside matches by testing each pair of fields with ==. There’s only one catch though, the types of all the fields also have to be part of the Eq typeclass. But since both String and Int are, we’re OK. Let’s test our Eq instance.

     ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
     ghci> let adRock = Person {firstName = "Adam", lastName = "Horovitz", age = 41}
    @@ -362,7 +362,7 @@ 

    Making Our Own Types and Typeclasses

    ghci> mikeD `elem` beastieBoys True
    -

    The Show and Read typeclasses are for things that can be converted to or from strings, respectively. Like with Eq, if a type's constructors have fields, their type has to be a part of Show or Read if we want to make our type an instance of them. Let's make our Person data type a part of Show and Read as well.

    +

    The Show and Read typeclasses are for things that can be converted to or from strings, respectively. Like with Eq, if a type’s constructors have fields, their type has to be a part of Show or Read if we want to make our type an instance of them. Let’s make our Person data type a part of Show and Read as well.

     data Person = Person { firstName :: String
                          , lastName :: String
    @@ -377,22 +377,22 @@ 

    Making Our Own Types and Typeclasses

    ghci> "mikeD is: " ++ show mikeD "mikeD is: Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}"
    -

    Had we tried to print a person on the terminal before making the Person data type part of Show, Haskell would have complained at us, claiming it doesn't know how to represent a person as a string. But now that we've derived a Show instance for it, it does know.

    -

    Read is pretty much the inverse typeclass of Show. Show is for converting values of our a type to a string, Read is for converting strings to values of our type. Remember though, when we use the read function, we have to use an explicit type annotation to tell Haskell which type we want to get as a result. If we don't make the type we want as a result explicit, Haskell doesn't know which type we want.

    +

    Had we tried to print a person on the terminal before making the Person data type part of Show, Haskell would have complained at us, claiming it doesn’t know how to represent a person as a string. But now that we’ve derived a Show instance for it, it does know.

    +

    Read is pretty much the inverse typeclass of Show. Show is for converting values of our a type to a string, Read is for converting strings to values of our type. Remember though, when we use the read function, we have to use an explicit type annotation to tell Haskell which type we want to get as a result. If we don’t make the type we want as a result explicit, Haskell doesn’t know which type we want.

     ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" :: Person
     Person {firstName = "Michael", lastName = "Diamond", age = 43}
     
    -

    If we use the result of our read later on in a way that Haskell can infer that it should read it as a person, we don't have to use type annotation.

    +

    If we use the result of our read later on in a way that Haskell can infer that it should read it as a person, we don’t have to use type annotation.

     ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" == mikeD
     True
     
    -

    We can also read parameterized types, but we have to fill in the type parameters. So we can't do read "Just 't'" :: Maybe a, but we can do read "Just 't'" :: Maybe Char.

    +

    We can also read parameterized types, but we have to fill in the type parameters. So we can’t do read "Just 't'" :: Maybe a, but we can do read "Just 't'" :: Maybe Char.

    We can derive instances for the Ord type class, which is for types that have values that can be ordered. If we compare two values of the same type that were made using different constructors, the value -which was made with a constructor that's defined first is considered smaller. +which was made with a constructor that’s defined first is considered smaller. For instance, consider the Bool type, which can have a value of either False or True. For the purpose of seeing how it behaves when @@ -410,7 +410,7 @@

    Making Our Own Types and Typeclasses

    ghci> True < False False
-

In the Maybe a data type, the Nothing value constructor is specified before the Just value constructor, so a value of Nothing is always smaller than a value of Just something, even if that something is minus one billion trillion. But if we compare two Just values, then it goes to compare what's inside them.

+

In the Maybe a data type, the Nothing value constructor is specified before the Just value constructor, so a value of Nothing is always smaller than a value of Just something, even if that something is minus one billion trillion. But if we compare two Just values, then it goes to compare what’s inside them.

 ghci> Nothing < Just 100
 True
@@ -421,17 +421,17 @@ 

Making Our Own Types and Typeclasses

ghci> Just 100 > Just 50 True
-

But we can't do something like Just (*3) > Just (*2), because (*3) and (*2) are functions, which aren't instances of Ord.

+

But we can’t do something like Just (*3) > Just (*2), because (*3) and (*2) are functions, which aren’t instances of Ord.

We can easily use algebraic data types to make enumerations and the Enum and Bounded typeclasses help us with that. Consider the following data type:

 data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
 
-

Because all the value constructors are nullary (take no parameters, i.e. fields), we can make it part of the Enum typeclass. The Enum typeclass is for things that have predecessors and successors. We can also make it part of the Bounded typeclass, which is for things that have a lowest possible value and highest possible value. And while we're at it, let's also make it an instance of all the other derivable typeclasses and see what we can do with it.

+

Because all the value constructors are nullary (take no parameters, i.e. fields), we can make it part of the Enum typeclass. The Enum typeclass is for things that have predecessors and successors. We can also make it part of the Bounded typeclass, which is for things that have a lowest possible value and highest possible value. And while we’re at it, let’s also make it an instance of all the other derivable typeclasses and see what we can do with it.

 data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
            deriving (Eq, Ord, Show, Read, Bounded, Enum)
 
-

Because it's part of the Show and Read typeclasses, we can convert values of this type to and from strings.

+

Because it’s part of the Show and Read typeclasses, we can convert values of this type to and from strings.

 ghci> Wednesday
 Wednesday
@@ -440,7 +440,7 @@ 

Making Our Own Types and Typeclasses

ghci> read "Saturday" :: Day Saturday
-

Because it's part of the Eq and Ord typeclasses, we can compare or equate days.

+

Because it’s part of the Eq and Ord typeclasses, we can compare or equate days.

 ghci> Saturday == Sunday
 False
@@ -451,14 +451,14 @@ 

Making Our Own Types and Typeclasses

ghci> Monday `compare` Wednesday LT
-

It's also part of Bounded, so we can get the lowest and highest day.

+

It’s also part of Bounded, so we can get the lowest and highest day.

 ghci> minBound :: Day
 Monday
 ghci> maxBound :: Day
 Sunday
 
-

It's also an instance of Enum. We can get predecessors and successors of days and we can make list ranges from them!

+

It’s also an instance of Enum. We can get predecessors and successors of days and we can make list ranges from them!

 ghci> succ Monday
 Tuesday
@@ -469,21 +469,21 @@ 

Making Our Own Types and Typeclasses

ghci> [minBound .. maxBound] :: [Day] [Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]
-

That's pretty awesome.

+

That’s pretty awesome.

Type synonyms

- Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That's implemented with type synonyms. Type synonyms don't really do anything per se, they're just about giving some types different names so that they make more sense to someone reading our code and documentation. Here's how the standard library defines String as a synonym for [Char]. + Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char].

  type String = [Char]
  
chicken

- We've introduced the type keyword. The keyword might be misleading to some, because we're not actually making anything new (we did that with the data keyword), but we're just making a synonym for an already existing type. + We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a synonym for an already existing type.

If we make a function that converts a string to uppercase and call it toUpperString or something, we can give it a type declaration of toUpperString :: [Char] -> [Char] or toUpperString :: String -> String. Both of these are essentially the same, only the latter is nicer to read.

- When we were dealing with the Data.Map module, we first represented a phonebook with an association list before converting it into a map. As we've already found out, an association list is a list of key-value pairs. Let's look at a phonebook that we had. + When we were dealing with the Data.Map module, we first represented a phonebook with an association list before converting it into a map. As we’ve already found out, an association list is a list of key-value pairs. Let’s look at a phonebook that we had.

 phoneBook :: [(String,String)]
@@ -495,11 +495,11 @@ 

Making Our Own Types and Typeclasses

,("roald","939-8282") ,("tenzing","853-2492") ]
-

We see that the type of phoneBook is [(String,String)]. That tells us that it's an association list that maps from strings to strings, but not much else. Let's make a type synonym to convey some more information in the type declaration.

+

We see that the type of phoneBook is [(String,String)]. That tells us that it’s an association list that maps from strings to strings, but not much else. Let’s make a type synonym to convey some more information in the type declaration.

 type PhoneBook = [(String,String)]
 
-

Now the type declaration for our phonebook can be phoneBook :: PhoneBook. Let's make a type synonym for String as well.

+

Now the type declaration for our phonebook can be phoneBook :: PhoneBook. Let’s make a type synonym for String as well.

 type PhoneNumber = String
 type Name = String
@@ -511,13 +511,13 @@ 

Making Our Own Types and Typeclasses

inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool inPhoneBook name pnumber pbook = (name,pnumber) `elem` pbook
-

If we decided not to use type synonyms, our function would have a type of String -> String -> [(String,String)] -> Bool. In this case, the type declaration that took advantage of type synonyms is easier to understand. However, you shouldn't go overboard with them. We introduce type synonyms either to describe what some existing type represents in our functions (and thus our type declarations become better documentation) or when something has a long-ish type that's repeated a lot (like [(String,String)]) but represents something more specific in the context of our functions.

+

If we decided not to use type synonyms, our function would have a type of String -> String -> [(String,String)] -> Bool. In this case, the type declaration that took advantage of type synonyms is easier to understand. However, you shouldn’t go overboard with them. We introduce type synonyms either to describe what some existing type represents in our functions (and thus our type declarations become better documentation) or when something has a long-ish type that’s repeated a lot (like [(String,String)]) but represents something more specific in the context of our functions.

Type synonyms can also be parameterized. If we want a type that represents an association list type but still want it to be general so it can use any type as the keys and values, we can do this:

 type AssocList k v = [(k,v)]
 

Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

-
Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we're dealin' with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don't mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don't let anybody else use your comb!
+
Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

 type IntMap v = Map Int v
@@ -527,9 +527,9 @@ 

Making Our Own Types and Typeclasses

type IntMap = Map Int

Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

-
Oh yeah. If you're going to try and implement this, you'll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you'd write type IntMap = Map.Map Int.
-

Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn't mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We're in Haskell's type portion whenever we're defining new types (so in data and type declarations) or when we're located after a ::. The :: is in type declarations or in type annotations.

-

Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it's defined:

+
Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.
+

Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

+

Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

 data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
 
@@ -544,8 +544,8 @@

Making Our Own Types and Typeclasses

ghci> :t Left True Left True :: Either Bool b
-

So far, we've seen that Maybe a was mostly used to represent the results of computations that could have either failed or not. But sometimes, Maybe a isn't good enough because Nothing doesn't really convey much information other than that something has failed. That's cool for functions that can fail in only one way or if we're just not interested in how and why they failed. A Data.Map lookup fails only if the key we were looking for wasn't in the map, so we know exactly what happened. However, when we're interested in how some function failed or why, we usually use the result type of Either a b, where a is some sort of type that can tell us something about the possible failure and b is the type of a successful computation. Hence, errors use the Left value constructor while results use Right.

-

An example: a high-school has lockers so that students have some place to put their Guns'n'Roses posters. Each locker has a code combination. When a student wants a new locker, they tell the locker supervisor which locker number they want and he gives them the code. However, if someone is already using that locker, he can't tell them the code for the locker and they have to pick a different one. We'll use a map from Data.Map to represent the lockers. It'll map from locker numbers to a pair of whether the locker is in use or not and the locker code.

+

So far, we’ve seen that Maybe a was mostly used to represent the results of computations that could have either failed or not. But sometimes, Maybe a isn’t good enough because Nothing doesn’t really convey much information other than that something has failed. That’s cool for functions that can fail in only one way or if we’re just not interested in how and why they failed. A Data.Map lookup fails only if the key we were looking for wasn’t in the map, so we know exactly what happened. However, when we’re interested in how some function failed or why, we usually use the result type of Either a b, where a is some sort of type that can tell us something about the possible failure and b is the type of a successful computation. Hence, errors use the Left value constructor while results use Right.

+

An example: a high-school has lockers so that students have some place to put their Guns’n’Roses posters. Each locker has a code combination. When a student wants a new locker, they tell the locker supervisor which locker number they want and he gives them the code. However, if someone is already using that locker, he can’t tell them the code for the locker and they have to pick a different one. We’ll use a map from Data.Map to represent the lockers. It’ll map from locker numbers to a pair of whether the locker is in use or not and the locker code.

 import qualified Data.Map as Map
 
@@ -555,7 +555,7 @@ 

Making Our Own Types and Typeclasses

type LockerMap = Map.Map Int (LockerState, Code)
-

Simple stuff. We introduce a new data type to represent whether a locker is taken or free and we make a type synonym for the locker code. We also make a type synonym for the type that maps from integers to pairs of locker state and code. And now, we're going to make a function that searches for the code in a locker map. We're going to use an Either String Code type to represent our result, because our lookup can fail in two ways — the locker can be taken, in which case we can't tell the code or the locker number might not exist at all. If the lookup fails, we're just going to use a String to tell what's happened.

+

Simple stuff. We introduce a new data type to represent whether a locker is taken or free and we make a type synonym for the locker code. We also make a type synonym for the type that maps from integers to pairs of locker state and code. And now, we’re going to make a function that searches for the code in a locker map. We’re going to use an Either String Code type to represent our result, because our lookup can fail in two ways — the locker can be taken, in which case we can’t tell the code or the locker number might not exist at all. If the lookup fails, we’re just going to use a String to tell what’s happened.

 lockerLookup :: Int -> LockerMap -> Either String Code
 lockerLookup lockerNumber map =
@@ -564,7 +564,7 @@ 

Making Our Own Types and Typeclasses

Just (state, code) -> if state /= Taken then Right code else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"
-

We do a normal lookup in the map. If we get a Nothing, we return a value of type Left String, saying that the locker doesn't exist at all. If we do find it, then we do an additional check to see if the locker is taken. If it is, return a Left saying that it's already taken. If it isn't, then return a value of type Right Code, in which we give the student the correct code for the locker. It's actually a Right String, but we introduced that type synonym to introduce some additional documentation into the type declaration. Here's an example map:

+

We do a normal lookup in the map. If we get a Nothing, we return a value of type Left String, saying that the locker doesn’t exist at all. If we do find it, then we do an additional check to see if the locker is taken. If it is, return a Left saying that it’s already taken. If it isn’t, then return a value of type Right Code, in which we give the student the correct code for the locker. It’s actually a Right String, but we introduced that type synonym to introduce some additional documentation into the type declaration. Here’s an example map:

 lockers :: LockerMap
 lockers = Map.fromList
@@ -576,7 +576,7 @@ 

Making Our Own Types and Typeclasses

,(110,(Taken,"99292")) ]
-

Now let's try looking up some locker codes.

+

Now let’s try looking up some locker codes.

 ghci> lockerLookup 101 lockers
 Right "JAH3I"
@@ -589,17 +589,17 @@ 

Making Our Own Types and Typeclasses

ghci> lockerLookup 105 lockers Right "QOTSA"
-

We could have used a Maybe a to represent the result but then we wouldn't know why we couldn't get the code. But now, we have information about the failure in our result type.

+

We could have used a Maybe a to represent the result but then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

Recursive data structures

the fonz -

As we've seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have fields that are of the same type! Using that, we can create recursive data types, where one value of some type contains values of that type, which in turn contain more values of the same type and so on.

-

Think about this list: [5]. That's just syntactic sugar for 5:[]. On the left side of the :, there's a value and on the right side, there's a list. And in this case, it's an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

+

As we’ve seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have fields that are of the same type! Using that, we can create recursive data types, where one value of some type contains values of that type, which in turn contain more values of the same type and so on.

+

Think about this list: [5]. That’s just syntactic sugar for 5:[]. On the left side of the :, there’s a value and on the right side, there’s a list. And in this case, it’s an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

We could say that a list can be an empty list or it can be an element joined together with a : with another list (that can be either the empty list or not).

-

Let's use algebraic data types to implement our own list then!

+

Let’s use algebraic data types to implement our own list then!

 data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
 
-

This reads just like our definition of lists from one of the previous paragraphs. It's either an empty list or a combination of a head with some value and a list. If you're confused about this, you might find it easier to understand in record syntax.

+

This reads just like our definition of lists from one of the previous paragraphs. It’s either an empty list or a combination of a head with some value and a list. If you’re confused about this, you might find it easier to understand in record syntax.

 data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)
 
@@ -614,13 +614,13 @@

Making Our Own Types and Typeclasses

ghci> 3 `Cons` (4 `Cons` (5 `Cons` Empty)) Cons 3 (Cons 4 (Cons 5 Empty)) -

We called our Cons constructor in an infix manner so you can see how it's just like :. Empty is like [] and 4 `Cons` (5 `Cons` Empty) is like 4:(5:[]).

-

We can define functions to be automatically infix by making them comprised of only special characters. We can also do the same with constructors, since they're just functions that return a data type. So check this out.

+

We called our Cons constructor in an infix manner so you can see how it’s just like :. Empty is like [] and 4 `Cons` (5 `Cons` Empty) is like 4:(5:[]).

+

We can define functions to be automatically infix by making them comprised of only special characters. We can also do the same with constructors, since they’re just functions that return a data type. So check this out.

 infixr 5 :-:
 data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)
 
-

First off, we notice a new syntactic construct, the fixity declarations. When we define functions as operators, we can use that to give them a fixity (but we don't have to). A fixity states how tightly the operator binds and whether it's left-associative or right-associative. For instance, *'s fixity is infixl 7 * and +'s fixity is infixl 6. That means that they're both left-associative (4 * 3 * 2 is (4 * 3) * 2) but * binds tighter than +, because it has a greater fixity, so 5 * 4 + 3 is (5 * 4) + 3.

+

First off, we notice a new syntactic construct, the fixity declarations. When we define functions as operators, we can use that to give them a fixity (but we don’t have to). A fixity states how tightly the operator binds and whether it’s left-associative or right-associative. For instance, *’s fixity is infixl 7 * and +’s fixity is infixl 6. That means that they’re both left-associative (4 * 3 * 2 is (4 * 3) * 2) but * binds tighter than +, because it has a greater fixity, so 5 * 4 + 3 is (5 * 4) + 3.

Otherwise, we just wrote a :-: (List a) instead of Cons a (List a). Now, we can write out lists in our list type like so:

 ghci> 3 :-: 4 :-: 5 :-: Empty
@@ -630,21 +630,21 @@ 

Making Our Own Types and Typeclasses

(:-:) 100 ((:-:) 3 ((:-:) 4 ((:-:) 5 Empty)))

When deriving Show for our type, Haskell will still display it as if the constructor was a prefix function, hence the parentheses around the operator (remember, 4 + 3 is (+) 4 3).

-

Let's make a function that adds two of our lists together. This is how ++ is defined for normal lists:

+

Let’s make a function that adds two of our lists together. This is how ++ is defined for normal lists:

 infixr 5  ++
 (++) :: [a] -> [a] -> [a]
 []     ++ ys = ys
 (x:xs) ++ ys = x : (xs ++ ys)
 
-

So we'll just steal that for our own list. We'll name the function .++.

+

So we’ll just steal that for our own list. We’ll name the function .++.

 infixr 5  .++
 (.++) :: List a -> List a -> List a
 Empty .++ ys = ys
 (x :-: xs) .++ ys = x :-: (xs .++ ys)
 
-

And let's see if it works ...

+

And let’s see if it works ...

 ghci> let a = 3 :-: 4 :-: 5 :-: Empty
 ghci> let b = 6 :-: 7 :-: Empty
@@ -654,14 +654,14 @@ 

Making Our Own Types and Typeclasses

Nice. Is nice. If we wanted, we could implement all of the functions that operate on lists on our own list type.

Notice how we pattern matched on (x :-: xs). That works because pattern matching is actually about matching constructors. We can match on :-: because it is a constructor for our own list type and we can also match on : because it is a constructor for the built-in list type. Same goes for []. Because pattern matching works (only) on constructors, we can match for stuff like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

binary search tree -

Now, we're going to implement a binary search tree. If you're not familiar with binary search trees from languages like C, here's what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we'd start at 5 and then because 8 is greater than 5, we'd go right. We're now at 7 and because 8 is greater than 7, we go right again. And we've found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

-

Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we'll just be implementing normal binary search trees.

-

Here's what we're going to say: a tree is either an empty tree or it's an element that contains some value and two trees. Sounds like a perfect fit for an algebraic data type!

+

Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we’d start at 5 and then because 8 is greater than 5, we’d go right. We’re now at 7 and because 8 is greater than 7, we go right again. And we’ve found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

+

Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we’ll just be implementing normal binary search trees.

+

Here’s what we’re going to say: a tree is either an empty tree or it’s an element that contains some value and two trees. Sounds like a perfect fit for an algebraic data type!

 data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
 
-

Okay, good, this is good. Instead of manually building a tree, we're going to make a function that takes a tree and an element and inserts an element. We do this by comparing the value we want to insert to the root node and then if it's smaller, we go left, if it's larger, we go right. We do the same for every subsequent node until we reach an empty tree. Once we've reached an empty tree, we just insert a node with that value instead of the empty tree.

-

In languages like C, we'd do this by modifying the pointers and values inside the tree. In Haskell, we can't really modify our tree, so we have to make a new subtree each time we decide to go left or right and in the end the insertion function returns a completely new tree, because Haskell doesn't really have a concept of pointer, just values. Hence, the type for our insertion function is going to be something like a -> Tree a -> Tree a. It takes an element and a tree and returns a new tree that has that element inside. This might seem like it's inefficient but laziness takes care of that problem.

+

Okay, good, this is good. Instead of manually building a tree, we’re going to make a function that takes a tree and an element and inserts an element. We do this by comparing the value we want to insert to the root node and then if it’s smaller, we go left, if it’s larger, we go right. We do the same for every subsequent node until we reach an empty tree. Once we’ve reached an empty tree, we just insert a node with that value instead of the empty tree.

+

In languages like C, we’d do this by modifying the pointers and values inside the tree. In Haskell, we can’t really modify our tree, so we have to make a new subtree each time we decide to go left or right and in the end the insertion function returns a completely new tree, because Haskell doesn’t really have a concept of pointer, just values. Hence, the type for our insertion function is going to be something like a -> Tree a -> Tree a. It takes an element and a tree and returns a new tree that has that element inside. This might seem like it’s inefficient but laziness takes care of that problem.

So, here are two functions. One is a utility function for making a singleton tree (a tree with just one node) and a function to insert an element into a tree.

 singleton :: a -> Tree a
@@ -674,8 +674,8 @@ 

Making Our Own Types and Typeclasses

| x < a = Node a (treeInsert x left) right | x > a = Node a left (treeInsert x right)
-

The singleton function is just a shortcut for making a node that has something and then two empty subtrees. In the insertion function, we first have the edge condition as a pattern. If we've reached an empty subtree, that means we're where we want and instead of the empty tree, we put a singleton tree with our element. If we're not inserting into an empty tree, then we have to check some things. First off, if the element we're inserting is equal to the root element, just return a tree that's the same. If it's smaller, return a tree that has the same root value, the same right subtree but instead of its left subtree, put a tree that has our value inserted into it. Same (but the other way around) goes if our value is bigger than the root element.

-

Next up, we're going to make a function that checks if some element is in the tree. First, let's define the edge condition. If we're looking for an element in an empty tree, then it's certainly not there. Okay. Notice how this is the same as the edge condition when searching for elements in lists. If we're looking for an element in an empty list, it's not there. Anyway, if we're not looking for an element in an empty tree, then we check some things. If the element in the root node is what we're looking for, great! If it's not, what then? Well, we can take advantage of knowing that all the left elements are smaller than the root node. So if the element we're looking for is smaller than the root node, check to see if it's in the left subtree. If it's bigger, check to see if it's in the right subtree.

+

The singleton function is just a shortcut for making a node that has something and then two empty subtrees. In the insertion function, we first have the edge condition as a pattern. If we’ve reached an empty subtree, that means we’re where we want and instead of the empty tree, we put a singleton tree with our element. If we’re not inserting into an empty tree, then we have to check some things. First off, if the element we’re inserting is equal to the root element, just return a tree that’s the same. If it’s smaller, return a tree that has the same root value, the same right subtree but instead of its left subtree, put a tree that has our value inserted into it. Same (but the other way around) goes if our value is bigger than the root element.

+

Next up, we’re going to make a function that checks if some element is in the tree. First, let’s define the edge condition. If we’re looking for an element in an empty tree, then it’s certainly not there. Okay. Notice how this is the same as the edge condition when searching for elements in lists. If we’re looking for an element in an empty list, it’s not there. Anyway, if we’re not looking for an element in an empty tree, then we check some things. If the element in the root node is what we’re looking for, great! If it’s not, what then? Well, we can take advantage of knowing that all the left elements are smaller than the root node. So if the element we’re looking for is smaller than the root node, check to see if it’s in the left subtree. If it’s bigger, check to see if it’s in the right subtree.

 treeElem :: (Ord a) => a -> Tree a -> Bool
 treeElem x EmptyTree = False
@@ -684,14 +684,14 @@ 

Making Our Own Types and Typeclasses

| x < a = treeElem x left | x > a = treeElem x right
-

All we had to do was write up the previous paragraph in code. Let's have some fun with our trees! Instead of manually building one (although we could), we'll use a fold to build up a tree from a list. Remember, pretty much everything that traverses a list one by one and then returns some sort of value can be implemented with a fold! We're going to start with the empty tree and then approach a list from the right and just insert element after element into our accumulator tree.

+

All we had to do was write up the previous paragraph in code. Let’s have some fun with our trees! Instead of manually building one (although we could), we’ll use a fold to build up a tree from a list. Remember, pretty much everything that traverses a list one by one and then returns some sort of value can be implemented with a fold! We’re going to start with the empty tree and then approach a list from the right and just insert element after element into our accumulator tree.

 ghci> let nums = [8,6,4,1,7,3,5]
 ghci> let numsTree = foldr treeInsert EmptyTree nums
 ghci> numsTree
 Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))

In that foldr, treeInsert was the folding function (it takes a tree and a list element and produces a new tree) and EmptyTree was the starting accumulator. nums, of course, was the list we were folding over.

-

When we print our tree to the console, it's not very readable, but if we try, we can make out its structure. We see that the root node is 5 and then it has two subtrees, one of which has the root node of 3 and the other a 7, etc.

+

When we print our tree to the console, it’s not very readable, but if we try, we can make out its structure. We see that the root node is 5 and then it has two subtrees, one of which has the root node of 3 and the other a 7, etc.

 ghci> 8 `treeElem` numsTree
 True
@@ -706,7 +706,7 @@ 

Making Our Own Types and Typeclasses

So as you can see, algebraic data structures are a really cool and powerful concept in Haskell. We can use them to make anything from boolean values and weekday enumerations to binary search trees and more!

Typeclasses 102

tweet -

So far, we've learned about some of the standard Haskell typeclasses and we've seen which types are in them. We've also learned how to automatically make our own types instances of the standard typeclasses by asking Haskell to derive the instances for us. In this section, we're going to learn how to make our own typeclasses and how to make types instances of them by hand.

+

So far, we’ve learned about some of the standard Haskell typeclasses and we’ve seen which types are in them. We’ve also learned how to automatically make our own types instances of the standard typeclasses by asking Haskell to derive the instances for us. In this section, we’re going to learn how to make our own typeclasses and how to make types instances of them by hand.

A quick recap on typeclasses: typeclasses are like interfaces. A typeclass defines some behavior (like comparing for equality, comparing for ordering, enumeration) and then types that can behave in that way are made instances of that typeclass. The behavior of typeclasses is achieved by defining functions or just type declarations that we then implement. So when we say that a type is an instance of a typeclass, we mean that we can use the functions that the typeclass defines with that type.

Typeclasses have pretty much nothing to do with classes in languages like Java or Python. This confuses many people, so I want you to forget everything you know about classes in imperative languages right now.

For example, the Eq typeclass is for stuff that can be equated. It defines the functions == and /=. If we have a type (say, Car) and comparing two cars with the equality function == makes sense, then it makes sense for Car to be an instance of Eq.

@@ -718,15 +718,15 @@

Making Our Own Types and Typeclasses

x == y = not (x /= y) x /= y = not (x == y)
-

Woah, woah, woah! Some new strange syntax and keywords there! Don't worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we're defining a new typeclass and that's called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn't have to be called a, it doesn't even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It's not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

+

Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.
-

Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn't have to do this, really, but we did and we'll see how this helps us soon.

+

Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.

So once we have a class, what can we do with it? Well, not much, really. But once we start making types instances of that class, we start getting some nice functionality. So check out this type:

 data TrafficLight = Red | Yellow | Green
 
-

It defines the states of a traffic light. Notice how we didn't derive any class instances for it. That's because we're going to write up some instances by hand, even though we could derive them for types like Eq and Show. Here's how we make it an instance of Eq.

+

It defines the states of a traffic light. Notice how we didn’t derive any class instances for it. That’s because we’re going to write up some instances by hand, even though we could derive them for types like Eq and Show. Here’s how we make it an instance of Eq.

 instance Eq TrafficLight where
     Red == Red = True
@@ -734,23 +734,23 @@ 

Making Our Own Types and Typeclasses

Yellow == Yellow = True _ == _ = False
-

We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we're making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

-

Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That's called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

+

We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we’re making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

+

Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That’s called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

 class Eq a where
     (==) :: a -> a -> Bool
     (/=) :: a -> a -> Bool
 
-

we'd have to implement both of these functions when making a type an instance of it, because Haskell wouldn't know how these two functions are related. The minimal complete definition would then be: both == and /=.

-

You can see that we implemented == simply by doing pattern matching. Since there are many more cases where two lights aren't equal, we specified the ones that are equal and then just did a catch-all pattern saying that if it's none of the previous combinations, then two lights aren't equal.

-

Let's make this an instance of Show by hand, too. To satisfy the minimal complete definition for Show, we just have to implement its show function, which takes a value and turns it into a string.

+

we’d have to implement both of these functions when making a type an instance of it, because Haskell wouldn’t know how these two functions are related. The minimal complete definition would then be: both == and /=.

+

You can see that we implemented == simply by doing pattern matching. Since there are many more cases where two lights aren’t equal, we specified the ones that are equal and then just did a catch-all pattern saying that if it’s none of the previous combinations, then two lights aren’t equal.

+

Let’s make this an instance of Show by hand, too. To satisfy the minimal complete definition for Show, we just have to implement its show function, which takes a value and turns it into a string.

 instance Show TrafficLight where
     show Red = "Red light"
     show Yellow = "Yellow light"
     show Green = "Green light"
 
-

Once again, we used pattern matching to achieve our goals. Let's see how it works in action:

+

Once again, we used pattern matching to achieve our goals. Let’s see how it works in action:

 ghci> Red == Red
 True
@@ -761,53 +761,53 @@ 

Making Our Own Types and Typeclasses

ghci> [Red, Yellow, Green] [Red light,Yellow light,Green light]
-

Nice. We could have just derived Eq and it would have had the same effect (but we didn't for educational purposes). However, deriving Show would have just directly translated the value constructors to strings. But if we want lights to appear like "Red light", then we have to make the instance declaration by hand.

-

You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here's the first part:

+

Nice. We could have just derived Eq and it would have had the same effect (but we didn’t for educational purposes). However, deriving Show would have just directly translated the value constructors to strings. But if we want lights to appear like "Red light", then we have to make the instance declaration by hand.

+

You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here’s the first part:

 class (Eq a) => Num a where
    ...  
-

As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We're essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That's all there is to subclassing really, it's just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

-

But how are the Maybe or list types made as instances of typeclasses? What makes Maybe different from, say, TrafficLight is that Maybe in itself isn't a concrete type, it's a type constructor that takes one type parameter (like Char or something) to produce a concrete type (like Maybe Char). Let's take a look at the Eq typeclass again:

+

As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We’re essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That’s all there is to subclassing really, it’s just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

+

But how are the Maybe or list types made as instances of typeclasses? What makes Maybe different from, say, TrafficLight is that Maybe in itself isn’t a concrete type, it’s a type constructor that takes one type parameter (like Char or something) to produce a concrete type (like Maybe Char). Let’s take a look at the Eq typeclass again:

 class Eq a where
     (==) :: a -> a -> Bool
     (/=) :: a -> a -> Bool
     x == y = not (x /= y)
     x /= y = not (x == y) 
-

From the type declarations, we see that the a is used as a concrete type because all the types in functions have to be concrete (remember, you can't have a function of the type a -> Maybe but you can have a function of a -> Maybe a or Maybe Int -> Maybe String). That's why we can't do something like

+

From the type declarations, we see that the a is used as a concrete type because all the types in functions have to be concrete (remember, you can’t have a function of the type a -> Maybe but you can have a function of a -> Maybe a or Maybe Int -> Maybe String). That’s why we can’t do something like

 instance Eq Maybe where
     ...  
-

Because like we've seen, the a has to be a concrete type but Maybe isn't a concrete type. It's a type constructor that takes one parameter and then produces a concrete type. It would also be tedious to write instance Eq (Maybe Int) where, instance Eq (Maybe Char) where, etc. for every type ever. So we could write it out like so:

+

Because like we’ve seen, the a has to be a concrete type but Maybe isn’t a concrete type. It’s a type constructor that takes one parameter and then produces a concrete type. It would also be tedious to write instance Eq (Maybe Int) where, instance Eq (Maybe Char) where, etc. for every type ever. So we could write it out like so:

 instance Eq (Maybe m) where
     Just x == Just y = x == y
     Nothing == Nothing = True
     _ == _ = False
       
-

This is like saying that we want to make all types of the form Maybe something an instance of Eq. We actually could have written (Maybe something), but we usually opt for single letters to be true to the Haskell style. The (Maybe m) here plays the role of the a from class Eq a where. While Maybe isn't a concrete type, Maybe m is. By specifying a type parameter (m, which is in lowercase), we said that we want all types that are in the form of Maybe m, where m is any type, to be an instance of Eq.

-

There's one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That's why we have to modify our instance declaration like this:

+

This is like saying that we want to make all types of the form Maybe something an instance of Eq. We actually could have written (Maybe something), but we usually opt for single letters to be true to the Haskell style. The (Maybe m) here plays the role of the a from class Eq a where. While Maybe isn’t a concrete type, Maybe m is. By specifying a type parameter (m, which is in lowercase), we said that we want all types that are in the form of Maybe m, where m is any type, to be an instance of Eq.

+

There’s one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That’s why we have to modify our instance declaration like this:

 instance (Eq m) => Eq (Maybe m) where
     Just x == Just y = x == y
     Nothing == Nothing = True
     _ == _ = False
       
-

We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what's contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

+

We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

-
Take into account that the type you're trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn't make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.
-

Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that's pretty cool.

+
Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.
+

Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

A yes-no typeclass

yesno

In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

-

Even though strictly using Bool for boolean semantics works better in Haskell, let's try and implement that JavaScript-ish behavior anyway. For fun! Let's start out with a class declaration.

+

Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

 class YesNo a where
     yesno :: a -> Bool
 
-

Pretty simple. The YesNo typeclass defines one function. That function takes one value of a type that can be considered to hold some concept of true-ness and tells us for sure if it's true or not. Notice that from the way we use the a in the function, a has to be a concrete type.

-

Next up, let's define some instances. For numbers, we'll assume that (like in JavaScript) any number that isn't 0 is true-ish and 0 is false-ish.

+

Pretty simple. The YesNo typeclass defines one function. That function takes one value of a type that can be considered to hold some concept of true-ness and tells us for sure if it’s true or not. Notice that from the way we use the a in the function, a has to be a concrete type.

+

Next up, let’s define some instances. For numbers, we’ll assume that (like in JavaScript) any number that isn’t 0 is true-ish and 0 is false-ish.

 instance YesNo Int where
     yesno 0 = False
@@ -819,32 +819,32 @@ 

Making Our Own Types and Typeclasses

yesno [] = False yesno _ = True
-

Notice how we just put in a type parameter a in there to make the list a concrete type, even though we don't make any assumptions about the type that's contained in the list. What else, hmm ... I know, Bool itself also holds true-ness and false-ness and it's pretty obvious which is which.

+

Notice how we just put in a type parameter a in there to make the list a concrete type, even though we don’t make any assumptions about the type that’s contained in the list. What else, hmm ... I know, Bool itself also holds true-ness and false-ness and it’s pretty obvious which is which.

 instance YesNo Bool where
     yesno = id
 
-

Huh? What's id? It's just a standard library function that takes a parameter and returns the same thing, which is what we would be writing here anyway.

-

Let's make Maybe a an instance too.

+

Huh? What’s id? It’s just a standard library function that takes a parameter and returns the same thing, which is what we would be writing here anyway.

+

Let’s make Maybe a an instance too.

 instance YesNo (Maybe a) where
     yesno (Just _) = True
     yesno Nothing = False
 
-

We didn't need a class constraint because we made no assumptions about the contents of the Maybe. We just said that it's true-ish if it's a Just value and false-ish if it's a Nothing. We still had to write out (Maybe a) instead of just Maybe because if you think about it, a Maybe -> Bool function can't exist (because Maybe isn't a concrete type), whereas a Maybe a -> Bool is fine and dandy. Still, this is really cool because now, any type of the form Maybe something is part of YesNo and it doesn't matter what that something is.

-

Previously, we defined a Tree a type, that represented a binary search tree. We can say an empty tree is false-ish and anything that's not an empty tree is true-ish.

+

We didn’t need a class constraint because we made no assumptions about the contents of the Maybe. We just said that it’s true-ish if it’s a Just value and false-ish if it’s a Nothing. We still had to write out (Maybe a) instead of just Maybe because if you think about it, a Maybe -> Bool function can’t exist (because Maybe isn’t a concrete type), whereas a Maybe a -> Bool is fine and dandy. Still, this is really cool because now, any type of the form Maybe something is part of YesNo and it doesn’t matter what that something is.

+

Previously, we defined a Tree a type, that represented a binary search tree. We can say an empty tree is false-ish and anything that’s not an empty tree is true-ish.

 instance YesNo (Tree a) where
     yesno EmptyTree = False
     yesno _ = True
 
-

Can a traffic light be a yes or no value? Sure. If it's red, you stop. If it's green, you go. If it's yellow? Eh, I usually run the yellows because I live for adrenaline.

+

Can a traffic light be a yes or no value? Sure. If it’s red, you stop. If it’s green, you go. If it’s yellow? Eh, I usually run the yellows because I live for adrenaline.

 instance YesNo TrafficLight where
     yesno Red = False
     yesno _ = True
 
-

Cool, now that we have some instances, let's go play!

+

Cool, now that we have some instances, let’s go play!

 ghci> yesno $ length []
 False
@@ -865,7 +865,7 @@ 

Making Our Own Types and Typeclasses

ghci> :t yesno yesno :: (YesNo a) => a -> Bool
-

Right, it works! Let's make a function that mimics the if statement, but it works with YesNo values.

+

Right, it works! Let’s make a function that mimics the if statement, but it works with YesNo values.

 yesnoIf :: (YesNo y) => y -> a -> a -> a
 yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult
@@ -884,21 +884,21 @@ 

Making Our Own Types and Typeclasses

"NO!"

The Functor typeclass

-

So far, we've encountered a lot of the typeclasses in the standard library. We've played with Ord, which is for stuff that can be ordered. We've palled around with Eq, which is for things that can be equated. We've seen Show, which presents an interface for types whose values can be displayed as strings. Our good friend Read is there whenever we need to convert a string to a value of some type. And now, we're going to take a look at the Functor typeclass, which is basically for things that can be mapped over. You're probably thinking about lists now, since mapping over lists is such a dominant idiom in Haskell. And you're right, the list type is part of the Functor typeclass.

-

What better way to get to know the Functor typeclass than to see how it's implemented? Let's take a peek.

+

So far, we’ve encountered a lot of the typeclasses in the standard library. We’ve played with Ord, which is for stuff that can be ordered. We’ve palled around with Eq, which is for things that can be equated. We’ve seen Show, which presents an interface for types whose values can be displayed as strings. Our good friend Read is there whenever we need to convert a string to a value of some type. And now, we’re going to take a look at the Functor typeclass, which is basically for things that can be mapped over. You’re probably thinking about lists now, since mapping over lists is such a dominant idiom in Haskell. And you’re right, the list type is part of the Functor typeclass.

+

What better way to get to know the Functor typeclass than to see how it’s implemented? Let’s take a peek.

 class Functor f where
     fmap :: (a -> b) -> f a -> f b
 
I AM FUNCTOOOOR!!! -

Alright. We see that it defines one function, fmap, and doesn't provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so far, the type variable that played the role of the type in the typeclass was a concrete type, like the a in (==) :: (Eq a) => a -> a -> Bool. But now, the f is not a concrete type (a type that a value can hold, like Int, Bool or Maybe String), but a type constructor that takes one type parameter. A quick refresher example: Maybe Int is a concrete type, but Maybe is a type constructor that takes one type as the parameter. Anyway, we see that fmap takes a function from one type to another and a functor applied with one type and returns a functor applied with another type.

-

If this sounds a bit confusing, don't worry. All will be revealed soon when we check out a few examples. Hmm, this type declaration for fmap reminds me of something. If you don't know what the type signature of map is, it's: map :: (a -> b) -> [a] -> [b].

-

Ah, interesting! It takes a function from one type to another and a list of one type and returns a list of another type. My friends, I think we have ourselves a functor! In fact, map is just a fmap that works only on lists. Here's how the list is an instance of the Functor typeclass.

+

Alright. We see that it defines one function, fmap, and doesn’t provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so far, the type variable that played the role of the type in the typeclass was a concrete type, like the a in (==) :: (Eq a) => a -> a -> Bool. But now, the f is not a concrete type (a type that a value can hold, like Int, Bool or Maybe String), but a type constructor that takes one type parameter. A quick refresher example: Maybe Int is a concrete type, but Maybe is a type constructor that takes one type as the parameter. Anyway, we see that fmap takes a function from one type to another and a functor applied with one type and returns a functor applied with another type.

+

If this sounds a bit confusing, don’t worry. All will be revealed soon when we check out a few examples. Hmm, this type declaration for fmap reminds me of something. If you don’t know what the type signature of map is, it’s: map :: (a -> b) -> [a] -> [b].

+

Ah, interesting! It takes a function from one type to another and a list of one type and returns a list of another type. My friends, I think we have ourselves a functor! In fact, map is just a fmap that works only on lists. Here’s how the list is an instance of the Functor typeclass.

 instance Functor [] where
     fmap = map
 
-

That's it! Notice how we didn't write instance Functor [a] where, because from fmap :: (a -> b) -> f a -> f b, we see that the f has to be a type constructor that takes one type. [a] is already a concrete type (of a list with any type inside it), while [] is a type constructor that takes one type and can produce types such as [Int], [String] or even [[String]].

+

That’s it! Notice how we didn’t write instance Functor [a] where, because from fmap :: (a -> b) -> f a -> f b, we see that the f has to be a type constructor that takes one type. [a] is already a concrete type (of a list with any type inside it), while [] is a type constructor that takes one type and can produce types such as [Int], [String] or even [[String]].

Since for lists, fmap is just map, we get the same results when using them on lists.

 map :: (a -> b) -> [a] -> [b]
@@ -908,14 +908,14 @@ 

Making Our Own Types and Typeclasses

[2,4,6]

What happens when we map or fmap over an empty list? Well, of course, we get an empty list. It just turns an empty list of type [a] into an empty list of type [b].

-

Types that can act like a box can be functors. You can think of a list as a box that has an infinite amount of little compartments and they can all be empty, one can be full and the others empty or a number of them can be full. So, what else has the properties of being like a box? For one, the Maybe a type. In a way, it's like a box that can either hold nothing, in which case it has the value of Nothing, or it can hold one item, like "HAHA", in which case it has a value of Just "HAHA". Here's how Maybe is a functor.

+

Types that can act like a box can be functors. You can think of a list as a box that has an infinite amount of little compartments and they can all be empty, one can be full and the others empty or a number of them can be full. So, what else has the properties of being like a box? For one, the Maybe a type. In a way, it’s like a box that can either hold nothing, in which case it has the value of Nothing, or it can hold one item, like "HAHA", in which case it has a value of Just "HAHA". Here’s how Maybe is a functor.

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

Again, notice how we wrote instance Functor Maybe where instead of instance Functor (Maybe m) where, like we did when we were dealing with Maybe and YesNo. Functor wants a type constructor that takes one type and not a concrete type. If you mentally replace the fs with Maybes, fmap acts like a (a -> b) -> Maybe a -> Maybe b for this particular type, which looks OK. But if you replace f with (Maybe m), then it would seem to act like a (a -> b) -> Maybe m a -> Maybe m b, which doesn't make any damn sense because Maybe takes just one type parameter.

-

Anyway, the fmap implementation is pretty simple. If it's an empty value of Nothing, then just return a Nothing. If we map over an empty box, we get an empty box. It makes sense. Just like if we map over an empty list, we get back an empty list. If it's not an empty value, but rather a single value packed up in a Just, then we apply the function on the contents of the Just.

+

Again, notice how we wrote instance Functor Maybe where instead of instance Functor (Maybe m) where, like we did when we were dealing with Maybe and YesNo. Functor wants a type constructor that takes one type and not a concrete type. If you mentally replace the fs with Maybes, fmap acts like a (a -> b) -> Maybe a -> Maybe b for this particular type, which looks OK. But if you replace f with (Maybe m), then it would seem to act like a (a -> b) -> Maybe m a -> Maybe m b, which doesn’t make any damn sense because Maybe takes just one type parameter.

+

Anyway, the fmap implementation is pretty simple. If it’s an empty value of Nothing, then just return a Nothing. If we map over an empty box, we get an empty box. It makes sense. Just like if we map over an empty list, we get back an empty list. If it’s not an empty value, but rather a single value packed up in a Just, then we apply the function on the contents of the Just.

 ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something serious.")
 Just "Something serious. HEY GUYS IM INSIDE THE JUST"
@@ -926,7 +926,7 @@ 

Making Our Own Types and Typeclasses

ghci> fmap (*2) Nothing Nothing
-

Another thing that can be mapped over and made an instance of Functor is our Tree a type. It can be thought of as a box in a way (holds several or no values) and the Tree type constructor takes exactly one type parameter. If you look at fmap as if it were a function made only for Tree, its type signature would look like (a -> b) -> Tree a -> Tree b. We're going to use recursion on this one. Mapping over an empty tree will produce an empty tree. Mapping over a non-empty tree will be a tree consisting of our function applied to the root value and its left and right subtrees will be the previous subtrees, only our function will be mapped over them.

+

Another thing that can be mapped over and made an instance of Functor is our Tree a type. It can be thought of as a box in a way (holds several or no values) and the Tree type constructor takes exactly one type parameter. If you look at fmap as if it were a function made only for Tree, its type signature would look like (a -> b) -> Tree a -> Tree b. We’re going to use recursion on this one. Mapping over an empty tree will produce an empty tree. Mapping over a non-empty tree will be a tree consisting of our function applied to the root value and its left and right subtrees will be the previous subtrees, only our function will be mapped over them.

 instance Functor Tree where
     fmap f EmptyTree = EmptyTree
@@ -938,45 +938,45 @@ 

Making Our Own Types and Typeclasses

ghci> fmap (*4) (foldr treeInsert EmptyTree [5,7,3,2,1,7]) Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree
-

Nice! Now how about Either a b? Can this be made a functor? The Functor typeclass wants a type constructor that takes only one type parameter but Either takes two. Hmmm! I know, we'll partially apply Either by feeding it only one parameter so that it has one free parameter. Here's how Either a is a functor in the standard libraries:

+

Nice! Now how about Either a b? Can this be made a functor? The Functor typeclass wants a type constructor that takes only one type parameter but Either takes two. Hmmm! I know, we’ll partially apply Either by feeding it only one parameter so that it has one free parameter. Here’s how Either a is a functor in the standard libraries:

 instance Functor (Either a) where
     fmap f (Right x) = Right (f x)
     fmap f (Left x) = Left x
 
-

Well well, what did we do here? You can see how we made Either a an instance instead of just Either. That's because Either a is a type constructor that takes one parameter, whereas Either takes two. If fmap was specifically for Either a, the type signature would then be (b -> c) -> Either a b -> Either a c because that's the same as (b -> c) -> (Either a) b -> (Either a) c. In the implementation, we mapped in the case of a Right value constructor, but we didn't in the case of a Left. Why is that? Well, if we look back at how the Either a b type is defined, it's kind of like:

+

Well well, what did we do here? You can see how we made Either a an instance instead of just Either. That’s because Either a is a type constructor that takes one parameter, whereas Either takes two. If fmap was specifically for Either a, the type signature would then be (b -> c) -> Either a b -> Either a c because that’s the same as (b -> c) -> (Either a) b -> (Either a) c. In the implementation, we mapped in the case of a Right value constructor, but we didn’t in the case of a Left. Why is that? Well, if we look back at how the Either a b type is defined, it’s kind of like:

 data Either a b = Left a | Right b
 
-

Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn't really work out. Also, from seeing what fmap's type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

-

This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it's empty.

-

Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

Note, the ' has no special meaning in types just like it doesn't have special meaning when naming values. It's used to denote things that are similar, only slightly changed.
+

Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

+

This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.

+

Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

Try figuring out how Map k is made an instance of Functor by yourself!

-

With the Functor typeclass, we've seen how typeclasses can represent pretty cool higher-order concepts. We've also had some more practice with partially applying types and making instances. In one of the next chapters, we'll also take a look at some laws that apply for functors.

-
Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that's not the case. We'll go over the functor laws in more detail in one of the next chapters.
+

With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

+
Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

Kinds and some type-foo

TYPE FOO MASTER -

Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We've seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we'll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don't really have to read this section to continue on your magical Haskell quest and if you don't understand it, don't worry about it. However, getting this will give you a very thorough understanding of the type system.

-

So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it's actually a really cool concept.

-

What are kinds and what are they good for? Well, let's examine the kind of a type by using the :k command in GHCI.

+

Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

+

So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

+

What are kinds and what are they good for? Well, let’s examine the kind of a type by using the :k command in GHCI.

 ghci> :k Int
 Int :: *
 
-

A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn't take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven't had to do that so far), I'd say star or just type.

-

Okay, now let's see what the kind of Maybe is.

+

A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn’t take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven’t had to do that so far), I’d say star or just type.

+

Okay, now let’s see what the kind of Maybe is.

 ghci> :k Maybe
 Maybe :: * -> *
 
-

The Maybe type constructor takes one concrete type (like Int) and then returns a concrete type like Maybe Int. And that's what this kind tells us. Just like Int -> Int means that a function takes an Int and returns an Int, * -> * means that the type constructor takes one concrete type and returns a concrete type. Let's apply the type parameter to Maybe and see what the kind of that type is.

+

The Maybe type constructor takes one concrete type (like Int) and then returns a concrete type like Maybe Int. And that’s what this kind tells us. Just like Int -> Int means that a function takes an Int and returns an Int, * -> * means that the type constructor takes one concrete type and returns a concrete type. Let’s apply the type parameter to Maybe and see what the kind of that type is.

 ghci> :k Maybe Int
 Maybe Int :: *
 
-

Just like I expected! We applied the type parameter to Maybe and got back a concrete type (that's what * -> * means). A parallel (although not equivalent, types and kinds are two different things) to this is if we do :t isUpper and :t isUpper 'A'. isUpper has a type of Char -> Bool and isUpper 'A' has a type of Bool, because its value is basically True. Both those types, however, have a kind of *.

+

Just like I expected! We applied the type parameter to Maybe and got back a concrete type (that’s what * -> * means). A parallel (although not equivalent, types and kinds are two different things) to this is if we do :t isUpper and :t isUpper 'A'. isUpper has a type of Char -> Bool and isUpper 'A' has a type of Bool, because its value is basically True. Both those types, however, have a kind of *.

We used :k on a type to get its kind, just like we can use :t on a value to get its type. Like we said, types are the labels of values and kinds are the labels of types and there are parallels between the two.

-

Let's look at another kind.

+

Let’s look at another kind.

 ghci> :k Either
 Either :: * -> * -> *
@@ -993,18 +993,18 @@ 

Making Our Own Types and Typeclasses

class Functor f where fmap :: (a -> b) -> f a -> f b
-

we see that the f type variable is used as a type that takes one concrete type to produce a concrete type. We know it has to produce a concrete type because it's used as the type of a value in a function. And from that, we can deduce that types that want to be friends with Functor have to be of kind * -> *.

-

Now, let's do some type-foo. Take a look at this typeclass that I'm just going to make up right now:

+

we see that the f type variable is used as a type that takes one concrete type to produce a concrete type. We know it has to produce a concrete type because it’s used as the type of a value in a function. And from that, we can deduce that types that want to be friends with Functor have to be of kind * -> *.

+

Now, let’s do some type-foo. Take a look at this typeclass that I’m just going to make up right now:

 class Tofu t where
     tofu :: j a -> t a j
 
-

Man, that looks weird. How would we make a type that could be an instance of that strange typeclass? Well, let's look at what its kind would have to be. Because j a is used as the type of a value that the tofu function takes as its parameter, j a has to have a kind of *. We assume * for a and so we can infer that j has to have a kind of * -> *. We see that t has to produce a concrete value too and that it takes two types. And knowing that a has a kind of * and j has a kind of * -> *, we infer that t has to have a kind of * -> (* -> *) -> *. So it takes a concrete type (a), a type constructor that takes one concrete type (j) and produces a concrete type. Wow.

-

OK, so let's make a type with a kind of * -> (* -> *) -> *. Here's one way of going about it.

+

Man, that looks weird. How would we make a type that could be an instance of that strange typeclass? Well, let’s look at what its kind would have to be. Because j a is used as the type of a value that the tofu function takes as its parameter, j a has to have a kind of *. We assume * for a and so we can infer that j has to have a kind of * -> *. We see that t has to produce a concrete value too and that it takes two types. And knowing that a has a kind of * and j has a kind of * -> *, we infer that t has to have a kind of * -> (* -> *) -> *. So it takes a concrete type (a), a type constructor that takes one concrete type (j) and produces a concrete type. Wow.

+

OK, so let’s make a type with a kind of * -> (* -> *) -> *. Here’s one way of going about it.

 data Frank a b  = Frank {frankField :: b a} deriving (Show)
 
-

How do we know this type has a kind of * -> (* -> *) - > *? Well, fields in ADTs are made to hold values, so they must be of kind *, obviously. We assume * for a, which means that b takes one type parameter and so its kind is * -> *. Now we know the kinds of both a and b and because they're parameters for Frank, we see that Frank has a kind of * -> (* -> *) -> * The first * represents a and the (* -> *) represents b. Let's make some Frank values and check out their types.

+

How do we know this type has a kind of * -> (* -> *) - > *? Well, fields in ADTs are made to hold values, so they must be of kind *, obviously. We assume * for a, which means that b takes one type parameter and so its kind is * -> *. Now we know the kinds of both a and b and because they’re parameters for Frank, we see that Frank has a kind of * -> (* -> *) -> * The first * represents a and the (* -> *) represents b. Let’s make some Frank values and check out their types.

 ghci> :t Frank {frankField = Just "HAHA"}
 Frank {frankField = Just "HAHA"} :: Frank [Char] Maybe
@@ -1025,22 +1025,22 @@ 

Making Our Own Types and Typeclasses

ghci> tofu ["HELLO"] :: Frank [Char] [] Frank {frankField = ["HELLO"]}
-

Not very useful, but we did flex our type muscles. Let's do some more type-foo. We have this data type:

+

Not very useful, but we did flex our type muscles. Let’s do some more type-foo. We have this data type:

 data Barry t k p = Barry { yabba :: p, dabba :: t k }
 
-

And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn't look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it's going to be something -> something -> something -> *. It's safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let's just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let's check that with GHCI.

+

And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn’t look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it’s going to be something -> something -> something -> *. It’s safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let’s just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let’s check that with GHCI.

 ghci> :k Barry
 Barry :: (* -> *) -> * -> * -> *
 
-

Ah, we were right. How satisfying. Now, to make this type a part of Functor we have to partially apply the first two type parameters so that we're left with * -> *. That means that the start of the instance declaration will be: instance Functor (Barry a b) where. If we look at fmap as if it was made specifically for Barry, it would have a type of fmap :: (a -> b) -> Barry c d a -> Barry c d b, because we just replace the Functor's f with Barry c d. The third type parameter from Barry will have to change and we see that it's conveniently in its own field.

+

Ah, we were right. How satisfying. Now, to make this type a part of Functor we have to partially apply the first two type parameters so that we’re left with * -> *. That means that the start of the instance declaration will be: instance Functor (Barry a b) where. If we look at fmap as if it was made specifically for Barry, it would have a type of fmap :: (a -> b) -> Barry c d a -> Barry c d b, because we just replace the Functor’s f with Barry c d. The third type parameter from Barry will have to change and we see that it’s conveniently in its own field.

 instance Functor (Barry a b) where
     fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}
 

There we go! We just mapped the f over the first field.

-

In this section, we took a good look at how type parameters work and kind of formalized them with kinds, just like we formalized function parameters with type declarations. We saw that there are interesting parallels between functions and type constructors. They are, however, two completely different things. When working on real Haskell, you usually won't have to mess with kinds and do kind inference by hand like we did now. Usually, you just have to partially apply your own type to * -> * or * when making it an instance of one of the standard typeclasses, but it's good to know how and why that actually works. It's also interesting to see that types have little types of their own. Again, you don't really have to understand everything we did here to read on, but if you understand how kinds work, chances are that you have a very solid grasp of Haskell's type system.

+

In this section, we took a good look at how type parameters work and kind of formalized them with kinds, just like we formalized function parameters with type declarations. We saw that there are interesting parallels between functions and type constructors. They are, however, two completely different things. When working on real Haskell, you usually won’t have to mess with kinds and do kind inference by hand like we did now. Usually, you just have to partially apply your own type to * -> * or * when making it an instance of one of the standard typeclasses, but it’s good to know how and why that actually works. It’s also interesting to see that types have little types of their own. Again, you don’t really have to understand everything we did here to read on, but if you understand how kinds work, chances are that you have a very solid grasp of Haskell’s type system.

  • diff --git a/docs/modules.html b/docs/modules.html index 9acbb9e..ed840ea 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -34,47 +34,47 @@

    Modules

    Loading modules

    modules -

    A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don't rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

    -

    The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There's a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we've dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we're going to examine a few useful modules and the functions that they have. But first, we're going to see how to import modules.

    -

    The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file. One script can, of course, import several modules. Just put each import statement into a separate line. Let's import the Data.List module, which has a bunch of useful functions for working with lists and use a function that it exports to create a function that tells us how many unique elements a list has.

    +

    A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

    +

    The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There’s a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we’ve dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we’re going to examine a few useful modules and the functions that they have. But first, we’re going to see how to import modules.

    +

    The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file. One script can, of course, import several modules. Just put each import statement into a separate line. Let’s import the Data.List module, which has a bunch of useful functions for working with lists and use a function that it exports to create a function that tells us how many unique elements a list has.

     import Data.List
     
     numUniques :: (Eq a) => [a] -> Int
     numUniques = length . nub
     
    -

    When you do import Data.List, all the functions that Data.List exports become available in the global namespace, meaning that you can call them from wherever in the script. nub is a function defined in Data.List that takes a list and weeds out duplicate elements. Composing length and nub by doing length . nub produces a function that's the equivalent of \xs -> length (nub xs).

    -

    You can also put the functions of modules into the global namespace when using GHCI. If you're in GHCI and you want to be able to call the functions exported by Data.List, do this:

    +

    When you do import Data.List, all the functions that Data.List exports become available in the global namespace, meaning that you can call them from wherever in the script. nub is a function defined in Data.List that takes a list and weeds out duplicate elements. Composing length and nub by doing length . nub produces a function that’s the equivalent of \xs -> length (nub xs).

    +

    You can also put the functions of modules into the global namespace when using GHCI. If you’re in GHCI and you want to be able to call the functions exported by Data.List, do this:

     ghci> :m + Data.List
     
    -

    If we want to load up the names from several modules inside GHCI, we don't have to do :m + several times, we can just load up several modules at once.

    +

    If we want to load up the names from several modules inside GHCI, we don’t have to do :m + several times, we can just load up several modules at once.

     ghci> :m + Data.List Data.Map Data.Set
     
    -

    However, if you've loaded a script that already imports a module, you don't need to use :m + to get access to it.

    -

    If you just need a couple of functions from a module, you can selectively import just those functions. If we wanted to import only the nub and sort functions from Data.List, we'd do this:

    +

    However, if you’ve loaded a script that already imports a module, you don’t need to use :m + to get access to it.

    +

    If you just need a couple of functions from a module, you can selectively import just those functions. If we wanted to import only the nub and sort functions from Data.List, we’d do this:

     import Data.List (nub, sort)
     
    -

    You can also choose to import all of the functions of a module except a few select ones. That's often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that's called nub and we want to import all the functions from Data.List except the nub function:

    +

    You can also choose to import all of the functions of a module except a few select ones. That’s often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that’s called nub and we want to import all the functions from Data.List except the nub function:

     import Data.List hiding (nub)
     
    -

    Another way of dealing with name clashes is to do qualified imports. The Data.Map module, which offers a data structure for looking up values by key, exports a bunch of functions with the same name as Prelude functions, like filter or null. So when we import Data.Map and then call filter, Haskell won't know which function to use. Here's how we solve this:

    +

    Another way of dealing with name clashes is to do qualified imports. The Data.Map module, which offers a data structure for looking up values by key, exports a bunch of functions with the same name as Prelude functions, like filter or null. So when we import Data.Map and then call filter, Haskell won’t know which function to use. Here’s how we solve this:

     import qualified Data.Map
     
    -

    This makes it so that if we want to reference Data.Map's filter function, we have to do Data.Map.filter, whereas just filter still refers to the normal filter we all know and love. But typing out Data.Map in front of every function from that module is kind of tedious. That's why we can rename the qualified import to something shorter:

    +

    This makes it so that if we want to reference Data.Map’s filter function, we have to do Data.Map.filter, whereas just filter still refers to the normal filter we all know and love. But typing out Data.Map in front of every function from that module is kind of tedious. That’s why we can rename the qualified import to something shorter:

     import qualified Data.Map as M
     
    -

    Now, to reference Data.Map's filter function, we just use M.filter.

    +

    Now, to reference Data.Map’s filter function, we just use M.filter.

    Use this handy reference to see which modules are in the standard library. A great way to pick up new Haskell knowledge is to just click through the standard library reference and explore the modules and their functions. You can also view the Haskell source code for each module. Reading the source code of some modules is a really good way to learn Haskell and get a solid feel for it.

    -

    To search for functions or to find out where they're located, use Hoogle. It's a really awesome Haskell search engine, you can search by name, module name or even type signature.

    +

    To search for functions or to find out where they’re located, use Hoogle. It’s a really awesome Haskell search engine, you can search by name, module name or even type signature.

    Data.List

    -

    The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We've already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don't have to import Data.List via a qualified import because it doesn't clash with any Prelude names except for those that Prelude already steals from Data.List. Let's take a look at some of the functions that we haven't met before.

    -

    intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here's a demonstration:

    +

    The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We’ve already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don’t have to import Data.List via a qualified import because it doesn’t clash with any Prelude names except for those that Prelude already steals from Data.List. Let’s take a look at some of the functions that we haven’t met before.

    +

    intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here’s a demonstration:

     ghci> intersperse '.' "MONKEY"
     "M.O.N.K.E.Y"
    @@ -102,7 +102,7 @@ 

    Modules

    When we transpose these three lists, the third powers are then in the first row, the second powers in the second one and so on. Mapping sum to that produces our desired result.

    shopping lists -

    foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, you might often get a stack overflow error. The culprit for that is that due to the lazy nature of the folds, the accumulator value isn't actually updated as the folding happens. What actually happens is that the accumulator kind of makes a promise that it will compute its value when asked to actually produce the result (also called a thunk). That happens for every intermediate accumulator and all those thunks overflow your stack. The strict folds aren't lazy buggers and actually compute the intermediate values as they go along instead of filling up your stack with thunks. So if you ever get stack overflow errors when doing lazy folds, try switching to their strict versions.

    +

    foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, you might often get a stack overflow error. The culprit for that is that due to the lazy nature of the folds, the accumulator value isn’t actually updated as the folding happens. What actually happens is that the accumulator kind of makes a promise that it will compute its value when asked to actually produce the result (also called a thunk). That happens for every intermediate accumulator and all those thunks overflow your stack. The strict folds aren’t lazy buggers and actually compute the intermediate values as they go along instead of filling up your stack with thunks. So if you ever get stack overflow errors when doing lazy folds, try switching to their strict versions.

    concat flattens a list of lists into just a list of elements.

     ghci> concat ["foo","bar","car"]
    @@ -159,19 +159,19 @@ 

    Modules

    ghci> let (a,b) = splitAt 3 "foobar" in b ++ a "barfoo"
    -

    takeWhile is a really useful little function. It takes elements from a list while the predicate holds and then when an element is encountered that doesn't satisfy the predicate, it's cut off. It turns out this is very useful.

    +

    takeWhile is a really useful little function. It takes elements from a list while the predicate holds and then when an element is encountered that doesn’t satisfy the predicate, it’s cut off. It turns out this is very useful.

     ghci> takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1]
     [6,5,4]
     ghci> takeWhile (/=' ') "This is a sentence"
     "This"
     
    -

    Say we wanted to know the sum of all third powers that are under 10,000. We can't map (^3) to [1..], apply a filter and then try to sum that up because filtering an infinite list never finishes. You may know that all the elements here are ascending but Haskell doesn't. That's why we can do this:

    +

    Say we wanted to know the sum of all third powers that are under 10,000. We can’t map (^3) to [1..], apply a filter and then try to sum that up because filtering an infinite list never finishes. You may know that all the elements here are ascending but Haskell doesn’t. That’s why we can do this:

     ghci> sum $ takeWhile (<10000) $ map (^3) [1..]
     53361
     
    -

    We apply (^3) to an infinite list and then once an element that's over 10,000 is encountered, the list is cut off. Now we can sum it up easily.

    +

    We apply (^3) to an infinite list and then once an element that’s over 10,000 is encountered, the list is cut off. Now we can sum it up easily.

    dropWhile is similar, only it drops all the elements while the predicate is true. Once predicate equates to False, it returns the rest of the list. An extremely useful and lovely function!

     ghci> dropWhile (/=' ') "This is a sentence"
    @@ -179,7 +179,7 @@ 

    Modules

    ghci> dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1] [3,4,5,4,3,2,1]
    -

    We're given a list that represents the value of a stock by date. The list is made of tuples whose first component is the stock value, the second is the year, the third is the month and the fourth is the date. We want to know when the stock value first exceeded one thousand dollars!

    +

    We’re given a list that represents the value of a stock by date. The list is made of tuples whose first component is the stock value, the second is the year, the third is the month and the fourth is the date. We want to know when the stock value first exceeded one thousand dollars!

     ghci> let stock = [(994.4,2008,9,1),(995.2,2008,9,2),(999.2,2008,9,3),(1001.4,2008,9,4),(998.3,2008,9,5)]
     ghci> head (dropWhile (\(val,y,m,d) -> val < 1000) stock)
    @@ -198,7 +198,7 @@ 

    Modules

    ([1,2,3],[4,5,6,7])

    When using break, the second list in the result will start with the first element that satisfies the predicate.

    -

    sort simply sorts a list. The type of the elements in the list has to be part of the Ord typeclass, because if the elements of a list can't be put in some kind of order, then the list can't be sorted.

    +

    sort simply sorts a list. The type of the elements in the list has to be part of the Ord typeclass, because if the elements of a list can’t be put in some kind of order, then the list can’t be sorted.

     ghci> sort [8,5,3,2,1,6,4,2]
     [1,2,2,3,4,5,6,8]
    @@ -215,7 +215,7 @@ 

    Modules

    ghci> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7] [(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]
    -

    inits and tails are like init and tail, only they recursively apply that to a list until there's nothing left. Observe.

    +

    inits and tails are like init and tail, only they recursively apply that to a list until there’s nothing left. Observe.

     ghci> inits "w00t"
     ["","w","w0","w00","w00t"]
    @@ -224,15 +224,15 @@ 

    Modules

    ghci> let w = "w00t" in zip (inits w) (tails w) [("","w00t"),("w","00t"),("w0","0t"),("w00","t"),("w00t","")]
    -

    Let's use a fold to implement searching a list for a sublist.

    +

    Let’s use a fold to implement searching a list for a sublist.

     search :: (Eq a) => [a] -> [a] -> Bool
     search needle haystack =
         let nlen = length needle
         in  foldl (\acc x -> if take nlen x == needle then True else acc) False (tails haystack)
     
    -

    First we call tails with the list in which we're searching. Then we go over each tail and see if it starts with what we're looking for.

    -

    With that, we actually just made a function that behaves like isInfixOf. isInfixOf searches for a sublist within a list and returns True if the sublist we're looking for is somewhere inside the target list.

    +

    First we call tails with the list in which we’re searching. Then we go over each tail and see if it starts with what we’re looking for.

    +

    With that, we actually just made a function that behaves like isInfixOf. isInfixOf searches for a sublist within a list and returns True if the sublist we’re looking for is somewhere inside the target list.

     ghci> "cat" `isInfixOf` "im a cat burglar"
     True
    @@ -252,21 +252,21 @@ 

    Modules

    ghci> "there!" `isSuffixOf` "oh hey there" False
    -

    elem and notElem check if an element is or isn't inside a list.

    -

    partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don't.

    +

    elem and notElem check if an element is or isn’t inside a list.

    +

    partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don’t.

     ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
     ("BOBMORGAN","sidneyeddy")
     ghci> partition (>3) [1,3,5,6,3,2,1,0,3,7]
     ([5,6,7],[1,3,3,2,1,0,3])
     
    -

    It's important to understand how this is different from span and break:

    +

    It’s important to understand how this is different from span and break:

     ghci> span (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
     ("BOB","sidneyMORGANeddy")
     
    -

    While span and break are done once they encounter the first element that doesn't and does satisfy the predicate, partition goes through the whole list and splits it up according to the predicate.

    -

    find takes a list and a predicate and returns the first element that satisfies the predicate. But it returns that element wrapped in a Maybe value. We'll be covering algebraic data types more in depth in the next chapter but for now, this is what you need to know: a Maybe value can either be Just something or Nothing. Much like a list can be either an empty list or a list with some elements, a Maybe value can be either no elements or a single element. And like the type of a list of, say, integers is [Int], the type of maybe having an integer is Maybe Int. Anyway, let's take our find function for a spin.

    +

    While span and break are done once they encounter the first element that doesn’t and does satisfy the predicate, partition goes through the whole list and splits it up according to the predicate.

    +

    find takes a list and a predicate and returns the first element that satisfies the predicate. But it returns that element wrapped in a Maybe value. We’ll be covering algebraic data types more in depth in the next chapter but for now, this is what you need to know: a Maybe value can either be Just something or Nothing. Much like a list can be either an empty list or a list with some elements, a Maybe value can be either no elements or a single element. And like the type of a list of, say, integers is [Int], the type of maybe having an integer is Maybe Int. Anyway, let’s take our find function for a spin.

     ghci> find (>4) [1,2,3,4,5,6]
     Just 5
    @@ -275,9 +275,9 @@ 

    Modules

    ghci> :t find find :: (a -> Bool) -> [a] -> Maybe a
    -

    Notice the type of find. Its result is Maybe a. That's kind of like having the type of [a], only a value of the type Maybe can contain either no elements or one element, whereas a list can contain no elements, one element or several elements.

    -

    Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we'd be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we'd get back a Nothing. But if there was a valid answer in that list, we'd get, say, Just (1001.4,2008,9,4).

    -

    elemIndex is kind of like elem, only it doesn't return a boolean value. It maybe returns the index of the element we're looking for. If that element isn't in our list, it returns a Nothing.

    +

    Notice the type of find. Its result is Maybe a. That’s kind of like having the type of [a], only a value of the type Maybe can contain either no elements or one element, whereas a list can contain no elements, one element or several elements.

    +

    Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we’d be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we’d get back a Nothing. But if there was a valid answer in that list, we’d get, say, Just (1001.4,2008,9,4).

    +

    elemIndex is kind of like elem, only it doesn’t return a boolean value. It maybe returns the index of the element we’re looking for. If that element isn’t in our list, it returns a Nothing.

     ghci> :t elemIndex
     elemIndex :: (Eq a) => a -> [a] -> Maybe Int
    @@ -286,7 +286,7 @@ 

    Modules

    ghci> 10 `elemIndex` [1,2,3,4,5,6] Nothing
    -

    elemIndices is like elemIndex, only it returns a list of indices, in case the element we're looking for crops up in our list several times. Because we're using a list to represent the indices, we don't need a Maybe type, because failure can be represented as the empty list, which is very much synonymous to Nothing.

    +

    elemIndices is like elemIndex, only it returns a list of indices, in case the element we’re looking for crops up in our list several times. Because we’re using a list to represent the indices, we don’t need a Maybe type, because failure can be represented as the empty list, which is very much synonymous to Nothing.

     ghci> ' ' `elemIndices` "Where are the spaces?"
     [5,9,13]
    @@ -300,14 +300,14 @@ 

    Modules

    ghci> findIndices (`elem` ['A'..'Z']) "Where Are The Caps?" [0,6,10,14]
    -

    We already covered zip and zipWith. We noted that they zip together two lists, either in a tuple or with a binary function (meaning such a function that takes two parameters). But what if we want to zip together three lists? Or zip three lists with a function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. While this may look like a hack, it works out pretty fine, because there aren't many times when you want to zip 8 lists together. There's also a very clever way for zipping infinite numbers of lists, but we're not advanced enough to cover that just yet.

    +

    We already covered zip and zipWith. We noted that they zip together two lists, either in a tuple or with a binary function (meaning such a function that takes two parameters). But what if we want to zip together three lists? Or zip three lists with a function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. While this may look like a hack, it works out pretty fine, because there aren’t many times when you want to zip 8 lists together. There’s also a very clever way for zipping infinite numbers of lists, but we’re not advanced enough to cover that just yet.

     ghci> zipWith3 (\x y z -> x + y + z) [1,2,3] [4,5,2,2] [2,2,3]
     [7,9,8]
     ghci> zip4 [2,3,3] [2,2,2] [5,5,3] [2,2,2]
     [(2,2,5,2),(3,2,5,2),(3,2,3,2)]
     
    -

    Just like with normal zipping, lists that are longer than the shortest list that's being zipped are cut down to size.

    +

    Just like with normal zipping, lists that are longer than the shortest list that’s being zipped are cut down to size.

    lines is a useful function when dealing with files or input from somewhere. It takes a string and returns every line of that string as separate element of a list.

     ghci> lines "first line\nsecond line\nthird line"
    @@ -328,7 +328,7 @@ 

    Modules

    ghci> unwords ["hey","there","mate"] "hey there mate"
    -

    We've already mentioned nub. It takes a list and weeds out the duplicate elements, returning a list whose every element is a unique snowflake! The function does have a kind of strange name. It turns out that "nub" means a small lump or essential part of something. In my opinion, they should use real words for function names instead of old-people words.

    +

    We’ve already mentioned nub. It takes a list and weeds out the duplicate elements, returning a list whose every element is a unique snowflake! The function does have a kind of strange name. It turns out that "nub" means a small lump or essential part of something. In my opinion, they should use real words for function names instead of old-people words.

     ghci> nub [1,2,3,4,3,2,1,2,3,4,3,2,1]
     [1,2,3,4]
    @@ -352,7 +352,7 @@ 

    Modules

    "Im a baby"

    Doing [1..10] \\ [2,5,9] is like doing delete 2 . delete 5 . delete 9 $ [1..10].

    -

    union also acts like a function on sets. It returns the union of two lists. It pretty much goes over every element in the second list and appends it to the first one if it isn't already in yet. Watch out though, duplicates are removed from the second list!

    +

    union also acts like a function on sets. It returns the union of two lists. It pretty much goes over every element in the second list and appends it to the first one if it isn’t already in yet. Watch out though, duplicates are removed from the second list!

     ghci> "hey man" `union` "man what's up"
     "hey manwt'sup"
    @@ -364,7 +364,7 @@ 

    Modules

    ghci> [1..7] `intersect` [5..10] [5,6,7]
    -

    insert takes an element and a list of elements that can be sorted and inserts it into the last position where it's still less than or equal to the next element. In other words, insert will start at the beginning of the list and then keep going until it finds an element that's equal to or greater than the element that we're inserting and it will insert it just before the element.

    +

    insert takes an element and a list of elements that can be sorted and inserts it into the last position where it’s still less than or equal to the next element. In other words, insert will start at the beginning of the list and then keep going until it finds an element that’s equal to or greater than the element that we’re inserting and it will insert it just before the element.

     ghci> insert 4 [3,5,1,2,8,2]
     [3,4,5,1,2,8,2]
    @@ -381,15 +381,15 @@ 

    Modules

    ghci> insert 3 [1,2,4,3,2,1] [1,2,3,4,3,2,1]
    -

    What length, take, drop, splitAt, !! and replicate have in common is that they take an Int as one of their parameters (or return an Int), even though they could be more generic and usable if they just took any type that's part of the Integral or Num typeclasses (depending on the functions). They do that for historical reasons. However, fixing that would probably break a lot of existing code. That's why Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, length has a type signature of length :: [a] -> Int. If we try to get the average of a list of numbers by doing let xs = [1..6] in sum xs / length xs, we get a type error, because you can't use / with an Int. genericLength, on the other hand, has a type signature of genericLength :: (Num a) => [b] -> a. Because a Num can act like a floating point number, getting the average by doing let xs = [1..6] in sum xs / genericLength xs works out just fine.

    +

    What length, take, drop, splitAt, !! and replicate have in common is that they take an Int as one of their parameters (or return an Int), even though they could be more generic and usable if they just took any type that’s part of the Integral or Num typeclasses (depending on the functions). They do that for historical reasons. However, fixing that would probably break a lot of existing code. That’s why Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, length has a type signature of length :: [a] -> Int. If we try to get the average of a list of numbers by doing let xs = [1..6] in sum xs / length xs, we get a type error, because you can’t use / with an Int. genericLength, on the other hand, has a type signature of genericLength :: (Num a) => [b] -> a. Because a Num can act like a floating point number, getting the average by doing let xs = [1..6] in sum xs / genericLength xs works out just fine.

    The nub, delete, union, intersect and group functions all have their more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is that the first set of functions use == to test for equality, whereas the By ones also take an equality function and then compare them by using that equality function. group is the same as groupBy (==).

    -

    For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That's where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

    +

    For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That’s where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

     ghci> let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
     ghci> groupBy (\x y -> (x > 0) == (y > 0)) values
     [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
     
    -

    From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they're both negative or if they're both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

    +

    From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they’re both negative or if they’re both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

     on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
     f `on` g = \x y -> f (g x) (g y)
    @@ -401,17 +401,17 @@ 

    Modules

    Very readable indeed! You can read it out loud: Group this by equality on whether the elements are greater than zero.

    Similarly, the sort, insert, maximum and minimum also have their more general equivalents. Functions like groupBy take a function that determines when two elements are equal. sortBy, insertBy, maximumBy and minimumBy take a function that determine if one element is greater, smaller or equal to the other. The type signature of sortBy is sortBy :: (a -> a -> Ordering) -> [a] -> [a]. If you remember from before, the Ordering type can have a value of LT, EQ or GT. sort is the equivalent of sortBy compare, because compare just takes two elements whose type is in the Ord typeclass and returns their ordering relationship.

    -

    Lists can be compared, but when they are, they are compared lexicographically. What if we have a list of lists and we want to sort it not based on the inner lists' contents but on their lengths? Well, as you've probably guessed, we'll use the sortBy function.

    +

    Lists can be compared, but when they are, they are compared lexicographically. What if we have a list of lists and we want to sort it not based on the inner lists’ contents but on their lengths? Well, as you’ve probably guessed, we’ll use the sortBy function.

     ghci> let xs = [[5,4,5,4,4],[1,2,3],[3,5,4,3],[],[2],[2,2]]
     ghci> sortBy (compare `on` length) xs
     [[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]
     
    -

    Awesome! compare `on` length ... man, that reads almost like real English! If you're not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you're dealing with By functions that take an equality function, you usually do (==) `on` something and when you're dealing with By functions that take an ordering function, you usually do compare `on` something.

    +

    Awesome! compare `on` length ... man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

    Data.Char

    lego char -

    The Data.Char module does what its name suggests. It exports functions that deal with characters. It's also helpful when filtering and mapping over strings because they're just lists of characters.

    -

    Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here's what they are:

    +

    The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

    +

    Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here’s what they are:

    isControl checks whether a character is a control character.

    isSpace checks whether a character is a white-space characters. That includes spaces, tab characters, newlines, etc.

    isLower checks whether a character is lower-cased.

    @@ -432,14 +432,14 @@

    Modules

    isLatin1 checks whether a character falls into the first 256 characters of Unicode.

    isAsciiUpper checks whether a character is ASCII and upper-case.

    isAsciiLower checks whether a character is ASCII and lower-case.

    -

    All these predicates have a type signature of Char -> Bool. Most of the time you'll use this to filter out strings or something like that. For instance, let's say we're making a program that takes a username and the username can only be comprised of alphanumeric characters. We can use the Data.List function all in combination with the Data.Char predicates to determine if the username is alright.

    +

    All these predicates have a type signature of Char -> Bool. Most of the time you’ll use this to filter out strings or something like that. For instance, let’s say we’re making a program that takes a username and the username can only be comprised of alphanumeric characters. We can use the Data.List function all in combination with the Data.Char predicates to determine if the username is alright.

     ghci> all isAlphaNum "bobby283"
     True
     ghci> all isAlphaNum "eddy the fish!"
     False
     
    -

    Kewl. In case you don't remember, all takes a predicate and a list and returns True only if that predicate holds for every element in the list.

    +

    Kewl. In case you don’t remember, all takes a predicate and a list and returns True only if that predicate holds for every element in the list.

    We can also use isSpace to simulate the Data.List function words.

     ghci> words "hey folks its me"
    @@ -448,13 +448,13 @@ 

    Modules

    ["hey"," ","folks"," ","its"," ","me"] ghci>
    -

    Hmmm, well, it kind of does what words does but we're left with elements of only spaces. Hmm, whatever shall we do? I know, let's filter that sucker.

    +

    Hmmm, well, it kind of does what words does but we’re left with elements of only spaces. Hmm, whatever shall we do? I know, let’s filter that sucker.

     ghci> filter (not . any isSpace) . groupBy ((==) `on` isSpace) $ "hey folks its me"
     ["hey","folks","its","me"]
     

    Ah.

    -

    The Data.Char also exports a datatype that's kind of like Ordering. The Ordering type can have a value of LT, EQ or GT. It's a sort of enumeration. It describes a few possible results that can arise from comparing two elements. The GeneralCategory type is also an enumeration. It presents us with a few possible categories that a character can fall into. The main function for getting the general category of a character is generalCategory. It has a type of generalCategory :: Char -> GeneralCategory. There are about 31 categories so we won't list them all here, but let's play around with the function.

    +

    The Data.Char also exports a datatype that’s kind of like Ordering. The Ordering type can have a value of LT, EQ or GT. It’s a sort of enumeration. It describes a few possible results that can arise from comparing two elements. The GeneralCategory type is also an enumeration. It presents us with a few possible categories that a character can fall into. The main function for getting the general category of a character is generalCategory. It has a type of generalCategory :: Char -> GeneralCategory. There are about 31 categories so we won’t list them all here, but let’s play around with the function.

     ghci> generalCategory ' '
     Space
    @@ -496,7 +496,7 @@ 

    Modules

    ghci> map ord "abcdefgh" [97,98,99,100,101,102,103,104]
    -

    The difference between the ord values of two characters is equal to how far apart they are in the Unicode table.

    The Caesar cipher is a primitive method of encoding messages by shifting each character in them by a fixed number of positions in the alphabet. We can easily create a sort of Caesar cipher of our own, only we won't constrict ourselves to the alphabet.

    +

    The difference between the ord values of two characters is equal to how far apart they are in the Unicode table.

    The Caesar cipher is a primitive method of encoding messages by shifting each character in them by a fixed number of positions in the alphabet. We can easily create a sort of Caesar cipher of our own, only we won’t constrict ourselves to the alphabet.

     encode :: Int -> String -> String
     encode shift msg =
    @@ -504,7 +504,7 @@ 

    Modules

    shifted = map (+ shift) ords in map chr shifted
    -

    Here, we first convert the string to a list of numbers. Then we add the shift amount to each number before converting the list of numbers back to characters. If you're a composition cowboy, you could write the body of this function as map (chr . (+ shift) . ord) msg. Let's try encoding a few messages.

    +

    Here, we first convert the string to a list of numbers. Then we add the shift amount to each number before converting the list of numbers back to characters. If you’re a composition cowboy, you could write the body of this function as map (chr . (+ shift) . ord) msg. Let’s try encoding a few messages.

     ghci> encode 3 "Heeeeey"
     "Khhhhh|"
    @@ -515,7 +515,7 @@ 

    Modules

    ghci> encode 5 "Marry Christmas! Ho ho ho!" "Rfww~%Hmwnxyrfx&%Mt%mt%mt&"
    -

    That's encoded alright. Decoding a message is basically just shifting it back by the number of places it was shifted by in the first place.

    +

    That’s encoded alright. Decoding a message is basically just shifting it back by the number of places it was shifted by in the first place.

     decode :: Int -> String -> String
     decode shift msg = encode (negate shift) msg
    @@ -529,8 +529,8 @@ 

    Modules

    "This is a sentence"

    Data.Map

    -

    Association lists (also called dictionaries) are lists that are used to store key-value pairs where ordering doesn't matter. For instance, we might use an association list to store phone numbers, where phone numbers would be the values and people's names would be the keys. We don't care in which order they're stored, we just want to get the right phone number for the right person.

    -

    The most obvious way to represent association lists in Haskell would be by having a list of pairs. The first component in the pair would be the key, the second component the value. Here's an example of an association list with phone numbers:

    +

    Association lists (also called dictionaries) are lists that are used to store key-value pairs where ordering doesn’t matter. For instance, we might use an association list to store phone numbers, where phone numbers would be the values and people’s names would be the keys. We don’t care in which order they’re stored, we just want to get the right phone number for the right person.

    +

    The most obvious way to represent association lists in Haskell would be by having a list of pairs. The first component in the pair would be the key, the second component the value. Here’s an example of an association list with phone numbers:

     phoneBook =
         [("amelia","555-2938")
    @@ -541,12 +541,12 @@ 

    Modules

    ,("tenzing","853-2492") ]
    -

    Despite this seemingly odd indentation, this is just a list of pairs of strings. The most common task when dealing with association lists is looking up some value by key. Let's make a function that looks up some value given a key.

    +

    Despite this seemingly odd indentation, this is just a list of pairs of strings. The most common task when dealing with association lists is looking up some value by key. Let’s make a function that looks up some value given a key.

     findKey :: (Eq k) => k -> [(k,v)] -> v
     findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs
     
    -

    Pretty simple. The function that takes a key and a list, filters the list so that only matching keys remain, gets the first key-value that matches and returns the value. But what happens if the key we're looking for isn't in the association list? Hmm. Here, if a key isn't in the association list, we'll end up trying to get the head of an empty list, which throws a runtime error. However, we should avoid making our programs so easy to crash, so let's use the Maybe data type. If we don't find the key, we'll return a Nothing. If we find it, we'll return Just something, where something is the value corresponding to that key.

    +

    Pretty simple. The function that takes a key and a list, filters the list so that only matching keys remain, gets the first key-value that matches and returns the value. But what happens if the key we’re looking for isn’t in the association list? Hmm. Here, if a key isn’t in the association list, we’ll end up trying to get the head of an empty list, which throws a runtime error. However, we should avoid making our programs so easy to crash, so let’s use the Maybe data type. If we don’t find the key, we’ll return a Nothing. If we find it, we’ll return Just something, where something is the value corresponding to that key.

     findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
     findKey key [] = Nothing
    @@ -555,12 +555,12 @@ 

    Modules

    else findKey key xs

    Look at the type declaration. It takes a key that can be equated, an association list and then it maybe produces a value. Sounds about right.

    -

    This is a textbook recursive function that operates on a list. Edge case, splitting a list into a head and a tail, recursive calls, they're all there. This is the classic fold pattern, so let's see how this would be implemented as a fold.

    +

    This is a textbook recursive function that operates on a list. Edge case, splitting a list into a head and a tail, recursive calls, they’re all there. This is the classic fold pattern, so let’s see how this would be implemented as a fold.

     findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
     findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
     
    -
    Note: It's usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they're easier to read and identify. Everyone knows it's a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.
    +
    Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.
     ghci> findKey "tenzing" phoneBook
     Just "853-2492"
    @@ -570,14 +570,14 @@ 

    Modules

    Nothing
    legomap -

    Works like a charm! If we have the friend's phone number, we Just get the number, otherwise we get Nothing.

    -

    We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they're internally implemented with trees) and also it provides a lot of utility functions. From now on, we'll say we're working with maps instead of association lists.

    -

    Because Data.Map exports functions that clash with the Prelude and Data.List ones, we'll do a qualified import.

    +

    Works like a charm! If we have the friend’s phone number, we Just get the number, otherwise we get Nothing.

    +

    We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they’re internally implemented with trees) and also it provides a lot of utility functions. From now on, we’ll say we’re working with maps instead of association lists.

    +

    Because Data.Map exports functions that clash with the Prelude and Data.List ones, we’ll do a qualified import.

     import qualified Data.Map as Map
     

    Put this import statement into a script and then load the script via GHCI.

    -

    Let's go ahead and see what Data.Map has in store for us! Here's the basic rundown of its functions.

    +

    Let’s go ahead and see what Data.Map has in store for us! Here’s the basic rundown of its functions.

    The fromList function takes an association list (in the form of a list) and returns a map with the same associations.

     ghci> Map.fromList [("amelia","555-2938"),("freya","452-2928"),("neil","205-2928")]
    @@ -589,14 +589,14 @@ 

    Modules

     Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v
     
    -

    It says that it takes a list of pairs of type k and v and returns a map that maps from keys of type k to type v. Notice that when we were doing association lists with normal lists, the keys only had to be equatable (their type belonging to the Eq typeclass) but now they have to be orderable. That's an essential constraint in the Data.Map module. It needs the keys to be orderable so it can arrange them in a tree.

    -

    You should always use Data.Map for key-value associations unless you have keys that aren't part of the Ord typeclass.

    +

    It says that it takes a list of pairs of type k and v and returns a map that maps from keys of type k to type v. Notice that when we were doing association lists with normal lists, the keys only had to be equatable (their type belonging to the Eq typeclass) but now they have to be orderable. That’s an essential constraint in the Data.Map module. It needs the keys to be orderable so it can arrange them in a tree.

    +

    You should always use Data.Map for key-value associations unless you have keys that aren’t part of the Ord typeclass.

    empty represents an empty map. It takes no arguments, it just returns an empty map.

     ghci> Map.empty
     fromList []
     
    -

    insert takes a key, a value and a map and returns a new map that's just like the old one, only with the key and value inserted.

    +

    insert takes a key, a value and a map and returns a new map that’s just like the old one, only with the key and value inserted.

     ghci> Map.empty
     fromList []
    @@ -612,7 +612,7 @@ 

    Modules

    fromList' :: (Ord k) => [(k,v)] -> Map.Map k v fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
    -

    It's a pretty straightforward fold. We start of with an empty map and we fold it up from the right, inserting the key value pairs into the accumulator as we go along.

    +

    It’s a pretty straightforward fold. We start of with an empty map and we fold it up from the right, inserting the key value pairs into the accumulator as we go along.

    null checks if a map is empty.

     ghci> Map.null Map.empty
    @@ -634,7 +634,7 @@ 

    Modules

    ghci> Map.insert 5 9 $ Map.singleton 3 9 fromList [(3,9),(5,9)]
    -

    lookup works like the Data.List lookup, only it operates on maps. It returns Just something if it finds something for the key and Nothing if it doesn't.

    +

    lookup works like the Data.List lookup, only it operates on maps. It returns Just something if it finds something for the key and Nothing if it doesn’t.

    member is a predicate that takes a key and a map and reports whether the key is in the map or not.

     ghci> Map.member 3 $ Map.fromList [(3,6),(4,3),(6,9)]
    @@ -655,7 +655,7 @@ 

    Modules

    [(4,3),(9,2)]

    keys and elems return lists of keys and values respectively. keys is the equivalent of map fst . Map.toList and elems is the equivalent of map snd . Map.toList.

    -

    fromListWith is a cool little function. It acts like fromList, only it doesn't discard duplicate keys but it uses a function supplied to it to decide what to do with them. Let's say that a friend can have several numbers and we have an association list set up like this.

    +

    fromListWith is a cool little function. It acts like fromList, only it doesn’t discard duplicate keys but it uses a function supplied to it to decide what to do with them. Let’s say that a friend can have several numbers and we have an association list set up like this.

     phoneBook =
         [("amelia","555-2938")
    @@ -670,7 +670,7 @@ 

    Modules

    ,("tenzing","555-2111") ]
    -

    Now if we just use fromList to put that into a map, we'll lose a few numbers! So here's what we'll do:

    +

    Now if we just use fromList to put that into a map, we’ll lose a few numbers! So here’s what we’ll do:

     phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String
     phoneBookToMap xs = Map.fromListWith (\number1 number2 -> number1 ++ ", " ++ number2) xs
    @@ -692,7 +692,7 @@ 

    Modules

    ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook ["827-9162","943-2929","493-2928"]
    -

    Pretty neat! Another use case is if we're making a map from an association list of numbers and when a duplicate key is found, we want the biggest value for the key to be kept.

    +

    Pretty neat! Another use case is if we’re making a map from an association list of numbers and when a duplicate key is found, we want the biggest value for the key to be kept.

     ghci> Map.fromListWith max [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
     fromList [(2,100),(3,29),(4,22)]
    @@ -710,14 +710,14 @@ 

    Modules

    These were just a few functions from Data.Map. You can see a complete list of functions in the documentation.

    Data.Set

    legosets -

    The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they're internally implemented with trees (much like maps in Data.Map), they're ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

    +

    The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

    Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

    Put this import statement in a script:

     import qualified Data.Set as Set
     

    And then load the script via GHCI.

    -

    Let's say we have two pieces of text. We want to find out which characters were used in both of them.

    +

    Let’s say we have two pieces of text. We want to find out which characters were used in both of them.

     text1 = "I just had an anime dream. Anime... Reality... Are they so different?"
     text2 = "The old man left his garbage can out and now his trash is all over my lawn!"
    @@ -732,12 +732,12 @@ 

    Modules

    ghci> set2 fromList " !Tabcdefghilmnorstuvwy"
    -

    As you can see, the items are ordered and each element is unique. Now let's use the intersection function to see which elements they both share.

    +

    As you can see, the items are ordered and each element is unique. Now let’s use the intersection function to see which elements they both share.

     ghci> Set.intersection set1 set2
     fromList " adefhilmnorstuy"
     
    -

    We can use the difference function to see which letters are in the first set but aren't in the second one and vice versa.

    +

    We can use the difference function to see which letters are in the first set but aren’t in the second one and vice versa.

     ghci> Set.difference set1 set2
     fromList ".?AIRj"
    @@ -749,7 +749,7 @@ 

    Modules

    ghci> Set.union set1 set2 fromList " !.?AIRTabcdefghijlmnorstuvwy"
    -

    The null, size, member, empty, singleton, insert and delete functions all work like you'd expect them to.

    +

    The null, size, member, empty, singleton, insert and delete functions all work like you’d expect them to.

     ghci> Set.null Set.empty
     True
    @@ -784,7 +784,7 @@ 

    Modules

    ghci> Set.map (+1) $ Set.fromList [3,4,5,6,7,2,3,4] fromList [3,4,5,6,7,8]
    -

    Sets are often used to weed a list of duplicates from a list by first making it into a set with fromList and then converting it back to a list with toList. The Data.List function nub already does that, but weeding out duplicates for large lists is much faster if you cram them into a set and then convert them back to a list than using nub. But using nub only requires the type of the list's elements to be part of the Eq typeclass, whereas if you want to cram elements into a set, the type of the list has to be in Ord.

    +

    Sets are often used to weed a list of duplicates from a list by first making it into a set with fromList and then converting it back to a list with toList. The Data.List function nub already does that, but weeding out duplicates for large lists is much faster if you cram them into a set and then convert them back to a list than using nub. But using nub only requires the type of the list’s elements to be part of the Eq typeclass, whereas if you want to cram elements into a set, the type of the list has to be in Ord.

     ghci> let setNub xs = Set.toList $ Set.fromList xs
     ghci> setNub "HEY WHATS CRACKALACKIN"
    @@ -792,13 +792,13 @@ 

    Modules

    ghci> nub "HEY WHATS CRACKALACKIN" "HEY WATSCRKLIN"
    -

    setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list's elements, while setNub does not.

    +

    setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

    Making our own modules

    making modules -

    We've looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it's good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

    -

    Let's see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We'll start by creating a file called Geometry.hs.

    +

    We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it’s good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

    +

    Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

    We say that a module exports functions. What that means is that when I import a module, I can use the functions that it exports. It can define functions that its functions call internally, but we can only see and use the ones that it exports.

    -

    At the beginning of a module, we specify the module name. If we have a file called Geometry.hs, then we should name our module Geometry. Then, we specify the functions that it exports and after that, we can start writing the functions. So we'll start with this.

    +

    At the beginning of a module, we specify the module name. If we have a file called Geometry.hs, then we should name our module Geometry. Then, we specify the functions that it exports and after that, we can start writing the functions. So we’ll start with this.

     module Geometry
     ( sphereVolume
    @@ -809,7 +809,7 @@ 

    Modules

    , cuboidVolume ) where
    -

    As you can see, we'll be doing areas and volumes for spheres, cubes and cuboids. Let's go ahead and define our functions then:

    +

    As you can see, we’ll be doing areas and volumes for spheres, cubes and cuboids. Let’s go ahead and define our functions then:

     module Geometry
     ( sphereVolume
    @@ -841,15 +841,15 @@ 

    Modules

    rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b
    -

    Pretty standard geometry right here. There are a few things to take note of though. Because a cube is only a special case of a cuboid, we defined its area and volume by treating it as a cuboid whose sides are all of the same length. We also defined a helper function called rectangleArea, which calculates a rectangle's area based on the lenghts of its sides. It's rather trivial because it's just multiplication. Notice that we used it in our functions in the module (namely cuboidArea and cuboidVolume) but we didn't export it! Because we want our module to just present functions for dealing with three-dimensional objects, we used rectangleArea but we didn't export it.

    -

    When making a module, we usually export only those functions that act as a sort of interface to our module so that the implementation is hidden. If someone is using our Geometry module, they don't have to concern themselves with functions that we don't export. We can decide to change those functions completely or delete them in a newer version (we could delete rectangleArea and just use * instead) and no one will mind because we weren't exporting them in the first place.

    +

    Pretty standard geometry right here. There are a few things to take note of though. Because a cube is only a special case of a cuboid, we defined its area and volume by treating it as a cuboid whose sides are all of the same length. We also defined a helper function called rectangleArea, which calculates a rectangle’s area based on the lenghts of its sides. It’s rather trivial because it’s just multiplication. Notice that we used it in our functions in the module (namely cuboidArea and cuboidVolume) but we didn’t export it! Because we want our module to just present functions for dealing with three-dimensional objects, we used rectangleArea but we didn’t export it.

    +

    When making a module, we usually export only those functions that act as a sort of interface to our module so that the implementation is hidden. If someone is using our Geometry module, they don’t have to concern themselves with functions that we don’t export. We can decide to change those functions completely or delete them in a newer version (we could delete rectangleArea and just use * instead) and no one will mind because we weren’t exporting them in the first place.

    To use our module, we just do:

     import Geometry
     
    -

    Geometry.hs has to be in the same folder that the program that's importing it is in, though.

    -

    Modules can also have a hierarchical structure. Each module can have a number of submodules and they can have submodules of their own. Let's section these functions off so that Geometry is a module that has three submodules, one for each type of object.

    -

    First, we'll make a folder called Geometry. Mind the capital G. In it, we'll place three files: Sphere.hs, Cuboid.hs, and Cube.hs. Here's what the files will contain:

    +

    Geometry.hs has to be in the same folder that the program that’s importing it is in, though.

    +

    Modules can also have a hierarchical structure. Each module can have a number of submodules and they can have submodules of their own. Let’s section these functions off so that Geometry is a module that has three submodules, one for each type of object.

    +

    First, we’ll make a folder called Geometry. Mind the capital G. In it, we’ll place three files: Sphere.hs, Cuboid.hs, and Cube.hs. Here’s what the files will contain:

    Sphere.hs

     module Geometry.Sphere
    @@ -894,19 +894,19 @@ 

    Modules

    area :: Float -> Float area side = Cuboid.area side side side
    -

    Alright! So first is Geometry.Sphere. Notice how we placed it in a folder called Geometry and then defined the module name as Geometry.Sphere. We did the same for the cuboid. Also notice how in all three submodules, we defined functions with the same names. We can do this because they're separate modules. We want to use functions from Geometry.Cuboid in Geometry.Cube but we can't just straight up do import Geometry.Cuboid because it exports functions with the same names as Geometry.Cube. That's why we do a qualified import and all is well.

    -

    So now if we're in a file that's on the same level as the Geometry folder, we can do, say:

    +

    Alright! So first is Geometry.Sphere. Notice how we placed it in a folder called Geometry and then defined the module name as Geometry.Sphere. We did the same for the cuboid. Also notice how in all three submodules, we defined functions with the same names. We can do this because they’re separate modules. We want to use functions from Geometry.Cuboid in Geometry.Cube but we can’t just straight up do import Geometry.Cuboid because it exports functions with the same names as Geometry.Cube. That’s why we do a qualified import and all is well.

    +

    So now if we’re in a file that’s on the same level as the Geometry folder, we can do, say:

     import Geometry.Sphere
     
    -

    And then we can call area and volume and they'll give us the area and volume for a sphere. And if we want to juggle two or more of these modules, we have to do qualified imports because they export functions with the same names. So we just do something like:

    +

    And then we can call area and volume and they’ll give us the area and volume for a sphere. And if we want to juggle two or more of these modules, we have to do qualified imports because they export functions with the same names. So we just do something like:

     import qualified Geometry.Sphere as Sphere
     import qualified Geometry.Cuboid as Cuboid
     import qualified Geometry.Cube as Cube
     

    And then we can call Sphere.area, Sphere.volume, Cuboid.area, etc. and each will calculate the area or volume for their corresponding object.

    -

    The next time you find yourself writing a file that's really big and has a lot of functions, try to see which functions serve some common purpose and then see if you can put them in their own module. You'll be able to just import your module the next time you're writing a program that requires some of the same functionality.

    +

    The next time you find yourself writing a file that’s really big and has a lot of functions, try to see which functions serve some common purpose and then see if you can put them in their own module. You’ll be able to just import your module the next time you’re writing a program that requires some of the same functionality.

    • diff --git a/docs/recursion.html b/docs/recursion.html index 7bb44bf..0671331 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -34,12 +34,12 @@

      Recursion

      Hello recursion!

      SOVIET RUSSIA -

      We mention recursion briefly in the previous chapter. In this chapter, we'll take a closer look at recursion, why it's important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

      -

      If you still don't know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we've now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn't defined F(0) and F(1) non recursively, you'd never get a solution any number because you'd reach 0 and then you'd go into negative numbers. All of a sudden, you'd be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn't be an end in sight!

      -

      Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That's why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

      +

      We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

      +

      If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

      +

      Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

      Maximum awesome

      -

      The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you'd implement that in an imperative fashion. You'd probably set up a variable to hold the maximum value so far and then you'd loop through the elements of a list and if an element is bigger than then the current maximum value, you'd replace it with that element. The maximum value that remains at the end is the result. Whew! That's quite a lot of words to describe such a simple algorithm!

      -

      Now let's see how we'd define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it's the maximum of the tail. That's it! Now let's implement that in Haskell.

      +

      The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

      +

      Now let’s see how we’d define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it’s the maximum of the tail. That’s it! Now let’s implement that in Haskell.

       maximum' :: (Ord a) => [a] -> a
       maximum' [] = error "maximum of empty list"
      @@ -49,29 +49,29 @@ 

      Recursion

      | otherwise = maxTail where maxTail = maximum' xs
      -

      As you can see, pattern matching goes great with recursion! Most imperative languages don't have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what's the maximum of an empty list? I don't know. The second pattern also lays out an edge condition. It says that if it's the singleton list, just give back the only element.

      +

      As you can see, pattern matching goes great with recursion! Most imperative languages don’t have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what’s the maximum of an empty list? I don’t know. The second pattern also lays out an edge condition. It says that if it’s the singleton list, just give back the only element.

      Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

      -

      Let's take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won't match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that's the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

      -

      An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here's how we could rewrite maximum' by using max:

      +

      Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

      +

      An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here’s how we could rewrite maximum' by using max:

       maximum' :: (Ord a) => [a] -> a
       maximum' [] = error "maximum of empty list"
       maximum' [x] = x
       maximum' (x:xs) = max x (maximum' xs)
       
      -

      How's that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

      +

      How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

      max

      A few more recursive functions

      -

      Now that we know how to generally think recursively, let's implement a few functions using recursion. First off, we'll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let's think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn't really make sense.

      +

      Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

       replicate' :: (Num i, Ord i) => i -> a -> [a]
       replicate' n x
           | n <= 0    = []
           | otherwise = x:replicate' (n-1) x
       
      -

      We used guards here instead of patterns because we're testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

      -
      Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren't ordered. So that's why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.
      -

      Next up, we'll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let's write that out:

      +

      We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

      +
      Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.
      +

      Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

       take' :: (Num i, Ord i) => i -> [a] -> [a]
       take' n _
      @@ -80,21 +80,21 @@ 

      Recursion

      take' n (x:xs) = x : take' (n-1) xs
      painter -

      The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we're using _ to match the list because we don't really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

      -

      reverse simply reverses a list. Think about the edge condition. What is it? Come on ... it's the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

      +

      The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

      +

      reverse simply reverses a list. Think about the edge condition. What is it? Come on ... it’s the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

       reverse' :: [a] -> [a]
       reverse' [] = []
       reverse' (x:xs) = reverse' xs ++ [x]
       

      There we go!

      -

      Because Haskell supports infinite lists, our recursion doesn't really have to have an edge condition. But if it doesn't have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. repeat takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.

      +

      Because Haskell supports infinite lists, our recursion doesn’t really have to have an edge condition. But if it doesn’t have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. repeat takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.

       repeat' :: a -> [a]
       repeat' x = x:repeat' x
       
      -

      Calling repeat 3 will give us a list that starts with 3 and then has an infinite amount of 3's as a tail. So calling repeat 3 would evaluate like 3:repeat 3, which is 3:(3:repeat 3), which is 3:(3:(3:repeat 3)), etc. repeat 3 will never finish evaluating, whereas take 5 (repeat 3) will give us a list of five 3's. So essentially it's like doing replicate 5 3.

      -

      zip takes two lists and zips them together. zip [1,2,3] [2,3] returns [(1,2),(2,3)], because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there's our edge condition. However, zip takes two lists as parameters, so there are actually two edge conditions.

      +

      Calling repeat 3 will give us a list that starts with 3 and then has an infinite amount of 3’s as a tail. So calling repeat 3 would evaluate like 3:repeat 3, which is 3:(3:repeat 3), which is 3:(3:(3:repeat 3)), etc. repeat 3 will never finish evaluating, whereas take 5 (repeat 3) will give us a list of five 3’s. So essentially it’s like doing replicate 5 3.

      +

      zip takes two lists and zips them together. zip [1,2,3] [2,3] returns [(1,2),(2,3)], because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there’s our edge condition. However, zip takes two lists as parameters, so there are actually two edge conditions.

       zip' :: [a] -> [b] -> [(a,b)]
       zip' _ [] = []
      @@ -102,7 +102,7 @@ 

      Recursion

      zip' (x:xs) (y:ys) = (x,y):zip' xs ys

      First two patterns say that if the first list or second list is empty, we get an empty list. The third one says that two lists zipped are equal to pairing up their heads and then tacking on the zipped tails. Zipping [1,2,3] and ['a','b'] will eventually try to zip [3] with []. The edge condition patterns kick in and so the result is (1,'a'):(2,'b'):[], which is exactly the same as [(1,'a'),(2,'b')].

      -

      Let's implement one more standard library function — elem. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn't have the droids we're looking for.

      +

      Let’s implement one more standard library function — elem. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn’t have the droids we’re looking for.

       elem' :: (Eq a) => a -> [a] -> Bool
       elem' a [] = False
      @@ -110,11 +110,11 @@ 

      Recursion

      | a == x = True | otherwise = a `elem'` xs
      -

      Pretty simple and expected. If the head isn't the element then we check the tail. If we reach an empty list, the result is False.

      +

      Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

      Quick, sort!

      -

      We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There's a very cool algorithm for sorting called quicksort. It's a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let's implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

      +

      We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

      quickman -

      So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they're also sorted). Notice that we said sorted two times in this definition, so we'll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that .... That's the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let's dive in and define this function.

      +

      So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that .... That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

       quicksort :: (Ord a) => [a] -> [a]
       quicksort [] = []
      @@ -123,22 +123,22 @@ 

      Recursion

      biggerSorted = quicksort [a | a <- xs, a > x] in smallerSorted ++ [x] ++ biggerSorted
      -

      Let's give it a small test run to see if it appears to behave correctly.

      +

      Let’s give it a small test run to see if it appears to behave correctly.

       ghci> quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]
       [1,2,2,3,3,4,4,5,6,7,8,9,10]
       ghci> quicksort "the quick brown fox jumps over the lazy dog"
       "        abcdeeefghhijklmnoooopqrrsttuuvwxyz"
       
      -

      Booyah! That's what I'm talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you'll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we'll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here's an illustration:

      +

      Booyah! That’s what I’m talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

      quicksort -

      An element that is in place and won't move anymore is represented in orange. If you read them from left to right, you'll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They're in green here. We chose the head because it's easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

      +

      An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They’re in green here. We chose the head because it’s easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

      Thinking recursively

      -

      We did quite a bit of recursion so far and as you've probably noticed, there's a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn't matter if it's a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera ...

      +

      We did quite a bit of recursion so far and as you’ve probably noticed, there’s a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn’t matter if it’s a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera ...

      brain -

      Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn't make sense. When dealing with lists, the edge case is most often the empty list. If you're dealing with trees, the edge case is usually a node that doesn't have any children.

      -

      It's similar when you're dealing with numbers recursively. Usually it has to do with some number and the function applied to that number modified. We did the factorial function earlier and it's the product of a number and the factorial of that number minus one. Such a recursive application doesn't make sense with zero, because factorials are defined only for positive integers. Often the edge case value turns out to be an identity. The identity for multiplication is 1 because if you multiply something by 1, you get that something back. Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the identity for addition. In quicksort, the edge case is the empty list and the identity is also the empty list, because if you add an empty list to a list, you just get the original list back.

      -

      So when trying to think of a recursive way to solve a problem, try to think of when a recursive solution doesn't apply and see if you can use that as an edge case, think about identities and think about whether you'll break apart the parameters of the function (for instance, lists are usually broken into a head and a tail via pattern matching) and on which part you'll use the recursive call.

      +

      Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn’t make sense. When dealing with lists, the edge case is most often the empty list. If you’re dealing with trees, the edge case is usually a node that doesn’t have any children.

      +

      It’s similar when you’re dealing with numbers recursively. Usually it has to do with some number and the function applied to that number modified. We did the factorial function earlier and it’s the product of a number and the factorial of that number minus one. Such a recursive application doesn’t make sense with zero, because factorials are defined only for positive integers. Often the edge case value turns out to be an identity. The identity for multiplication is 1 because if you multiply something by 1, you get that something back. Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the identity for addition. In quicksort, the edge case is the empty list and the identity is also the empty list, because if you add an empty list to a list, you just get the original list back.

      +

      So when trying to think of a recursive way to solve a problem, try to think of when a recursive solution doesn’t apply and see if you can use that as an edge case, think about identities and think about whether you’ll break apart the parameters of the function (for instance, lists are usually broken into a head and a tail via pattern matching) and on which part you’ll use the recursive call.

        diff --git a/docs/starting-out.html b/docs/starting-out.html index 1a8a625..cd85542 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -35,17 +35,17 @@

        Starting Out

        Ready, set, go!

        egg -Alright, let's get started! If you're the sort of horrible person who doesn't read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we're going to load functions. The first thing we're going to do is run ghc's interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this. +Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we’re going to load functions. The first thing we’re going to do is run ghc’s interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this.

         GHCi, version 9.2.4: https://www.haskell.org/ghc/  :? for help
         ghci>
         

        -Congratulations, you're in GHCI! +Congratulations, you’re in GHCI!

        -Here's some simple arithmetic. +Here’s some simple arithmetic.

         ghci> 2 + 15
        @@ -68,7 +68,7 @@ 

        Starting Out

        ghci> 50 * (100 - 4999) -244950

        -Pretty cool, huh? Yeah, I know it's not but bear with me. A little pitfall to watch out for here is negating numbers. If we want to have a negative number, it's always best to surround it with parentheses. Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine. +Pretty cool, huh? Yeah, I know it’s not but bear with me. A little pitfall to watch out for here is negating numbers. If we want to have a negative number, it’s always best to surround it with parentheses. Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine.

        Boolean algebra is also pretty straightforward. As you probably know, && means a boolean and, || means a boolean or. not negates a True or a False. @@ -107,20 +107,20 @@

        Starting Out

        In an equation for ‘it’: it = 5 + "llama"

    -Yikes! What GHCI is telling us here is that "llama" is not a number and so it doesn't know how to add it to 5. Even if it wasn't "llama" but "four" or "4", Haskell still wouldn't consider it to be a number. + expects its left and right side to be numbers. If we tried to do True == 5, GHCI would tell us that the types don't match. Whereas + works only on things that are considered numbers, == works on any two things that can be compared. But the catch is that they both have to be the same type of thing. You can't compare apples and oranges. We'll take a closer look at types a bit later. Note: you can do 5 + 4.0 because 5 is sneaky and can act like an integer or a floating-point number. 4.0 can't act like an integer, so 5 is the one that has to adapt. +Yikes! What GHCI is telling us here is that "llama" is not a number and so it doesn’t know how to add it to 5. Even if it wasn’t "llama" but "four" or "4", Haskell still wouldn’t consider it to be a number. + expects its left and right side to be numbers. If we tried to do True == 5, GHCI would tell us that the types don’t match. Whereas + works only on things that are considered numbers, == works on any two things that can be compared. But the catch is that they both have to be the same type of thing. You can’t compare apples and oranges. We’ll take a closer look at types a bit later. Note: you can do 5 + 4.0 because 5 is sneaky and can act like an integer or a floating-point number. 4.0 can’t act like an integer, so 5 is the one that has to adapt.

    -You may not have known it but we've been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you've seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren't used with numbers are prefix functions. Let's take a look at them. +You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them.

    phoen -Functions are usually prefix, so from now on we won't explicitly state that a function is of the prefix form, we'll just assume it. In most imperative languages, functions are called by writing the function name and then writing its parameters in parentheses, usually separated by commas. In Haskell, functions are called by writing the function name, a space and then the parameters, separated by spaces. For a start, we'll try calling one of the most boring functions in Haskell. +Functions are usually prefix, so from now on we won’t explicitly state that a function is of the prefix form, we’ll just assume it. In most imperative languages, functions are called by writing the function name and then writing its parameters in parentheses, usually separated by commas. In Haskell, functions are called by writing the function name, a space and then the parameters, separated by spaces. For a start, we’ll try calling one of the most boring functions in Haskell.

     ghci> succ 8
     9 

    -The succ function takes anything that has a defined successor and returns that successor. As you can see, we just separate the function name from the parameter with a space. Calling a function with several parameters is also simple. The functions min and max take two things that can be put in an order (like integers!). min returns the one that's lesser and max returns the one that's greater. See for yourself: +The succ function takes anything that has a defined successor and returns that successor. As you can see, we just separate the function name from the parameter with a space. Calling a function with several parameters is also simple. The functions min and max take two things that can be put in an order (like integers!). min returns the one that’s lesser and max returns the one that’s greater. See for yourself:

     ghci> min 9 10
    @@ -138,18 +138,18 @@ 

    Starting Out

    16

    -However, if we wanted to get the successor of the product of numbers 9 and 10, we couldn't write succ 9 * 10 because that would get the successor of 9, which would then be multiplied by 10. So 100. We'd have to write succ (9 * 10) to get 91. +However, if we wanted to get the successor of the product of numbers 9 and 10, we couldn’t write succ 9 * 10 because that would get the successor of 9, which would then be multiplied by 10. So 100. We’d have to write succ (9 * 10) to get 91.

    -

    If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks. For instance, the div function takes two integers and does integral division between them. Doing div 92 10 results in a 9. But when we call it like that, there may be some confusion as to which number is doing the division and which one is being divided. So we can call it as an infix function by doing 92 `div` 10 and suddenly it's much clearer.

    -

    Lots of people who come from imperative languages tend to stick to the notion that parentheses should denote function application. For example, in C, you use parentheses to call functions like foo(), bar(1) or baz(3, "haha"). Like we said, spaces are used for function application in Haskell. So those functions in Haskell would be foo, bar 1 and baz 3 "haha". So if you see something like bar (bar 3), it doesn't mean that bar is called with bar and 3 as parameters. It means that we first call the function bar with 3 as the parameter to get some number and then we call bar again with that number. In C, that would be something like bar(bar(3)).

    -

    Baby's first functions

    +

    If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks. For instance, the div function takes two integers and does integral division between them. Doing div 92 10 results in a 9. But when we call it like that, there may be some confusion as to which number is doing the division and which one is being divided. So we can call it as an infix function by doing 92 `div` 10 and suddenly it’s much clearer.

    +

    Lots of people who come from imperative languages tend to stick to the notion that parentheses should denote function application. For example, in C, you use parentheses to call functions like foo(), bar(1) or baz(3, "haha"). Like we said, spaces are used for function application in Haskell. So those functions in Haskell would be foo, bar 1 and baz 3 "haha". So if you see something like bar (bar 3), it doesn’t mean that bar is called with bar and 3 as parameters. It means that we first call the function bar with 3 as the parameter to get some number and then we call bar again with that number. In C, that would be something like bar(bar(3)).

    +

    Baby’s first functions

    -In the previous section we got a basic feel for calling functions. Now let's try making our own! Open up your favorite text editor and punch in this function that takes a number and multiplies it by two. +In the previous section we got a basic feel for calling functions. Now let’s try making our own! Open up your favorite text editor and punch in this function that takes a number and multiplies it by two.

     doubleMe x = x + x

    -Functions are defined in a similar way that they are called. The function name is followed by parameters separated by spaces. But when defining functions, there's a = and after that we define what the function does. Save this as baby.hs or something. Now navigate to where it's saved and run ghci from there. Once inside GHCI, do :l baby. Now that our script is loaded, we can play with the function that we defined. +Functions are defined in a similar way that they are called. The function name is followed by parameters separated by spaces. But when defining functions, there’s a = and after that we define what the function does. Save this as baby.hs or something. Now navigate to where it’s saved and run ghci from there. Once inside GHCI, do :l baby. Now that our script is loaded, we can play with the function that we defined.

     ghci> :l baby
    @@ -160,7 +160,7 @@ 

    Starting Out

    ghci> doubleMe 8.3 16.6

    -Because + works on integers as well as on floating-point numbers (anything that can be considered a number, really), our function also works on any number. Let's make a function that takes two numbers and multiplies each by two and then adds them together. +Because + works on integers as well as on floating-point numbers (anything that can be considered a number, really), our function also works on any number. Let’s make a function that takes two numbers and multiplies each by two and then adds them together.

     doubleUs x y = x*2 + y*2 
    @@ -183,8 +183,8 @@

    Starting Out

    This is a very simple example of a common pattern you will see throughout Haskell. Making basic functions that are obviously correct and then combining them into more complex functions. This way you also avoid repetition. What if some mathematicians figured out that 2 is actually 3 and you had to change your program? You could just redefine doubleMe to be x + x + x and since doubleUs calls doubleMe, it would automatically work in this strange new world where 2 is 3.

    -

    Functions in Haskell don't have to be in any particular order, so it doesn't matter if you define doubleMe first and then doubleUs or if you do it the other way around.

    -

    Now we're going to make a function that multiplies a number by 2 but only if that number is smaller than or equal to 100 because numbers bigger than 100 are big enough as it is! +

    Functions in Haskell don’t have to be in any particular order, so it doesn’t matter if you define doubleMe first and then doubleUs or if you do it the other way around.

    +

    Now we’re going to make a function that multiplies a number by 2 but only if that number is smaller than or equal to 100 because numbers bigger than 100 are big enough as it is!

     doubleSmallNumber x = if x > 100
    @@ -192,26 +192,26 @@ 

    Starting Out

    else x*2
    this is you

    -Right here we introduced Haskell's if statement. You're probably familiar with if statements from other languages. The difference between Haskell's if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn't satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that's why it's an expression. If we wanted to add one to every number that's produced in our previous function, we could have written its body like this. +Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this.

     doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
     

    -Had we omitted the parentheses, it would have added one only if x wasn't greater than 100. Note the ' at the end of the function name. That apostrophe doesn't have any special meaning in Haskell's syntax. It's a valid character to use in a function name. We usually use ' to either denote a strict version of a function (one that isn't lazy) or a slightly modified version of a function or a variable. Because ' is a valid character in functions, we can make a function like this. +Had we omitted the parentheses, it would have added one only if x wasn’t greater than 100. Note the ' at the end of the function name. That apostrophe doesn’t have any special meaning in Haskell’s syntax. It’s a valid character to use in a function name. We usually use ' to either denote a strict version of a function (one that isn’t lazy) or a slightly modified version of a function or a variable. Because ' is a valid character in functions, we can make a function like this.

     conanO'Brien = "It's a-me, Conan O'Brien!" 

    -There are two noteworthy things here. The first is that in the function name we didn't capitalize Conan's name. That's because functions can't begin with uppercase letters. We'll see why a bit later. The second thing is that this function doesn't take any parameters. When a function doesn't take any parameters, we usually say it's a definition (or a name). Because we can't change what names (and functions) mean once we've defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably. +There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably.

    An intro to lists

    BUY A DOG -Much like shopping lists in the real world, lists in Haskell are very useful. It's the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this section we'll look at the basics of lists, strings (which are lists) and list comprehensions. +Much like shopping lists in the real world, lists in Haskell are very useful. It’s the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this section we’ll look at the basics of lists, strings (which are lists) and list comprehensions.

    -In Haskell, lists are a homogenous data structure. They store several elements of the same type. That means that we can have a list of integers or a list of characters but we can't have a list that has a few integers and then a few characters. And now, a list! +In Haskell, lists are a homogenous data structure. They store several elements of the same type. That means that we can have a list of integers or a list of characters but we can’t have a list that has a few integers and then a few characters. And now, a list!

     ghci> lostNumbers = [4,8,15,16,23,42]
    @@ -231,7 +231,7 @@ 

    Starting Out

    "woot"

    -Watch out when repeatedly using the ++ operator on long strings. When you put together two lists (even if you append a singleton list to a list, for instance: [1,2,3] ++ [4]), internally, Haskell has to walk through the whole list on the left side of ++. That's not a problem when dealing with lists that aren't too big. But putting something at the end of a list that's fifty million entries long is going to take a while. However, putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous. +Watch out when repeatedly using the ++ operator on long strings. When you put together two lists (even if you append a singleton list to a list, for instance: [1,2,3] ++ [4]), internally, Haskell has to walk through the whole list on the left side of ++. That’s not a problem when dealing with lists that aren’t too big. But putting something at the end of a list that’s fifty million entries long is going to take a while. However, putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous.

     ghci> 'A':" SMALL CAT"
    @@ -240,7 +240,7 @@ 

    Starting Out

    [5,1,2,3,4,5]

    -Notice how : takes a number and a list of numbers or a character and a list of characters, whereas ++ takes two lists. Even if you're adding an element to the end of a list with ++, you have to surround it with square brackets so it becomes a list.

    +Notice how : takes a number and a list of numbers or a character and a list of characters, whereas ++ takes two lists. Even if you’re adding an element to the end of a list with ++, you have to surround it with square brackets so it becomes a list.

    [1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on.

    @@ -252,7 +252,7 @@

    Starting Out

    ghci> [9.4,33.2,96.2,11.2,23.25] !! 1 33.2
    -

    But if you try to get the sixth element from a list that only has four elements, you'll get an error so be careful!

    +

    But if you try to get the sixth element from a list that only has four elements, you’ll get an error so be careful!

    Lists can also contain lists. They can also contain lists that contain lists that contain lists …

    @@ -266,7 +266,7 @@

    Starting Out

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

The lists within a list can be of different lengths but they can't be of different types. Just like you can't have a list that has some characters and some numbers, you can't have a list that has some lists of characters and some lists of numbers.

+

The lists within a list can be of different lengths but they can’t be of different types. Just like you can’t have a list that has some characters and some numbers, you can’t have a list that has some lists of characters and some lists of numbers.

Lists can be compared if the stuff they contain can be compared. When using <, <=, > and >= to compare lists, they are compared in lexicographical order. First the heads are compared. If they are equal then the second elements are compared, etc.

 ghci> [3,2,1] > [2,1,0]
@@ -288,7 +288,7 @@ 

Starting Out

 ghci> head [5,4,3,2,1]
 5 
-

tail takes a list and returns its tail. In other words, it chops off a list's head.

+

tail takes a list and returns its tail. In other words, it chops off a list’s head.

 ghci> tail [5,4,3,2,1]
 [4,3,2,1] 
@@ -300,13 +300,13 @@

Starting Out

 ghci> init [5,4,3,2,1]
 [5,4,3,2] 
-

If we think of a list as a monster, here's what's what.

+

If we think of a list as a monster, here’s what’s what.

list monster

But what happens if we try to get the head of an empty list?

 ghci> head []
 *** Exception: Prelude.head: empty list
-

Oh my! It all blows up in our face! If there's no monster, it doesn't have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it's always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list.

+

Oh my! It all blows up in our face! If there’s no monster, it doesn’t have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it’s always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list.

length takes a list and returns its length, obviously.

 ghci> length [5,4,3,2,1]
@@ -354,20 +354,20 @@ 

Starting Out

24 ghci> product [1,2,5,6,7,9,2,0] 0
-

elem takes a thing and a list of things and tells us if that thing is an element of the list. It's usually called as an infix function because it's easier to read that way.

+

elem takes a thing and a list of things and tells us if that thing is an element of the list. It’s usually called as an infix function because it’s easier to read that way.

 ghci> 4 `elem` [3,4,5,6]
 True
 ghci> 10 `elem` [3,4,5,6]
 False
 
-

Those were a few basic functions that operate on lists. We'll take a look at more list functions later.

+

Those were a few basic functions that operate on lists. We’ll take a look at more list functions later.

Texas ranges

draw -What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that's not a solution for gentlemen who demand excellence from their programming languages. Instead, we'll use ranges. Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers can be enumerated. One, two, three, four, etc. Characters can also be enumerated. The alphabet is an enumeration of characters from A to Z. Names can't be enumerated. What comes after "John"? I don't know. +What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that’s not a solution for gentlemen who demand excellence from their programming languages. Instead, we’ll use ranges. Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers can be enumerated. One, two, three, four, etc. Characters can also be enumerated. The alphabet is an enumeration of characters from A to Z. Names can’t be enumerated. What comes after "John"? I don’t know.

-

To make a list containing all the natural numbers from 1 to 20, you just write [1..20]. That is the equivalent of writing [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and there's no difference between writing one or the other except that writing out long enumeration sequences manually is stupid.

+

To make a list containing all the natural numbers from 1 to 20, you just write [1..20]. That is the equivalent of writing [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and there’s no difference between writing one or the other except that writing out long enumeration sequences manually is stupid.

 ghci> [1..20]
 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
@@ -383,9 +383,9 @@ 

Starting Out

[2,4,6,8,10,12,14,16,18,20] ghci> [3,6..20] [3,6,9,12,15,18]
-

It's simply a matter of separating the first two elements with a comma and then specifying what the upper limit is. While pretty smart, ranges with steps aren't as smart as some people expect them to be. You can't do [1,2,4,8,16..100] and expect to get all the powers of 2. Firstly because you can only specify one step. And secondly because some sequences that aren't arithmetic are ambiguous if given only by a few of their first terms. +

It’s simply a matter of separating the first two elements with a comma and then specifying what the upper limit is. While pretty smart, ranges with steps aren’t as smart as some people expect them to be. You can’t do [1,2,4,8,16..100] and expect to get all the powers of 2. Firstly because you can only specify one step. And secondly because some sequences that aren’t arithmetic are ambiguous if given only by a few of their first terms.

-

To make a list with all the numbers from 20 to 1, you can't just do [20..1], you have to do [20,19..1].

+

To make a list with all the numbers from 20 to 1, you can’t just do [20..1], you have to do [20,19..1].

Watch out when using floating point numbers in ranges! Because they are not completely precise (by definition), their use in ranges can yield some pretty funky results.

 ghci> [0.1, 0.3 .. 1]
@@ -393,7 +393,7 @@ 

Starting Out

My advice is not to use them in list ranges.

-You can also use ranges to make infinite lists by just not specifying an upper limit. Later we'll go into more detail on infinite lists. For now, let's examine how you would get the first 24 multiples of 13. Sure, you could do [13,26..24*13]. But there's a better way: take 24 [13,26..]. Because Haskell is lazy, it won't try to evaluate the infinite list immediately because it would never finish. It'll wait to see what you want to get out of that infinite lists. And here it sees you just want the first 24 elements and it gladly obliges. +You can also use ranges to make infinite lists by just not specifying an upper limit. Later we’ll go into more detail on infinite lists. For now, let’s examine how you would get the first 24 multiples of 13. Sure, you could do [13,26..24*13]. But there’s a better way: take 24 [13,26..]. Because Haskell is lazy, it won’t try to evaluate the infinite list immediately because it would never finish. It’ll wait to see what you want to get out of that infinite lists. And here it sees you just want the first 24 elements and it gladly obliges.

A handful of functions that produce infinite lists:

cycle takes a list and cycles it into an infinite list. If you just try to display the result, it will go on forever so you have to slice it off somewhere.

@@ -402,23 +402,23 @@

Starting Out

[1,2,3,1,2,3,1,2,3,1] ghci> take 12 (cycle "LOL ") "LOL LOL LOL "
-

repeat takes an element and produces an infinite list of just that element. It's like cycling a list with only one element.

+

repeat takes an element and produces an infinite list of just that element. It’s like cycling a list with only one element.

 ghci> take 10 (repeat 5)
 [5,5,5,5,5,5,5,5,5,5]
 
-

Although it's simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

-

I'm a list comprehension

+

Although it’s simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

+

I’m a list comprehension

frog -If you've ever taken a course in mathematics, you've probably run into set comprehensions. They're normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate. +If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate.

-

If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn't want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We'll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here's that comprehension in action.

+

If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn’t want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We’ll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here’s that comprehension in action.

 ghci> [x*2 | x <- [1..10]]
 [2,4,6,8,10,12,14,16,18,20]
 
-

As you can see, we get the desired results. Now let's add a condition (or a predicate) to that comprehension. Predicates go after the binding parts and are separated from them by a comma. Let's say we want only the elements which, doubled, are greater than or equal to 12. +

As you can see, we get the desired results. Now let’s add a condition (or a predicate) to that comprehension. Predicates go after the binding parts and are separated from them by a comma. Let’s say we want only the elements which, doubled, are greater than or equal to 12.

 ghci> [x*2 | x <- [1..10], x*2 >= 12]
@@ -428,17 +428,17 @@ 

Starting Out

 ghci> [ x | x <- [50..100], x `mod` 7 == 3]
 [52,59,66,73,80,87,94] 
-

Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let's say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that's less than 10 with "BOOM!". If a number isn't odd, we throw it out of our list. For convenience, we'll put that comprehension inside a function so we can easily reuse it.

+

Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let’s say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that’s less than 10 with "BOOM!". If a number isn’t odd, we throw it out of our list. For convenience, we’ll put that comprehension inside a function so we can easily reuse it.

 boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] 

The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True.

 ghci> boomBangs [7..13]
 ["BOOM!","BOOM!","BANG!","BANG!"] 
-

We can include several predicates. If we wanted all numbers from 10 to 20 that are not 13, 15 or 19, we'd do:

+

We can include several predicates. If we wanted all numbers from 10 to 20 that are not 13, 15 or 19, we’d do:

 ghci> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19]
 [10,11,12,14,16,17,18,20]
-

Not only can we have multiple predicates in list comprehensions (an element must satisfy all the predicates to be included in the resulting list), we can also draw from several lists. When drawing from several lists, comprehensions produce all combinations of the given lists and then join them by the output function we supply. A list produced by a comprehension that draws from two lists of length 4 will have a length of 16, provided we don't filter them. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible combinations between numbers in those lists, here's what we'd do.

+

Not only can we have multiple predicates in list comprehensions (an element must satisfy all the predicates to be included in the resulting list), we can also draw from several lists. When drawing from several lists, comprehensions produce all combinations of the given lists and then join them by the output function we supply. A list produced by a comprehension that draws from two lists of length 4 will have a length of 16, provided we don’t filter them. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible combinations between numbers in those lists, here’s what we’d do.

 ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
 [16,20,22,40,50,55,80,100,110] 
@@ -453,11 +453,11 @@

Starting Out

ghci> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns] ["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog", "grouchy pope","scheming hobo","scheming frog","scheming pope"]
-

I know! Let's write our own version of length! We'll call it length'.

+

I know! Let’s write our own version of length! We’ll call it length'.

 length' xs = sum [1 | _ <- xs] 
-

_ means that we don't care what we'll draw from the list anyway so instead of writing a variable name that we'll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.

-

Just a friendly reminder: because strings are lists, we can use list comprehensions to process and produce strings. Here's a function that takes a string and removes everything except uppercase letters from it.

+

_ means that we don’t care what we’ll draw from the list anyway so instead of writing a variable name that we’ll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.

+

Just a friendly reminder: because strings are lists, we can use list comprehensions to process and produce strings. Here’s a function that takes a string and removes everything except uppercase letters from it.

 removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] 

@@ -468,18 +468,18 @@

Starting Out

"HA" ghci> removeNonUppercase "IdontLIKEFROGS" "ILIKEFROGS"
-

The predicate here does all the work. It says that the character will be included in the new list only if it's an element of the list ['A'..'Z']. Nested list comprehensions are also possible if you're operating on lists that contain lists. A list contains several lists of numbers. Let's remove all odd numbers without flattening the list.

+

The predicate here does all the work. It says that the character will be included in the new list only if it’s an element of the list ['A'..'Z']. Nested list comprehensions are also possible if you’re operating on lists that contain lists. A list contains several lists of numbers. Let’s remove all odd numbers without flattening the list.

 ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
 ghci> [ [ x | x <- xs, even x ] | xs <- xxs]
 [[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
 
-

You can write list comprehensions across several lines. So if you're not in GHCI, it's better to split longer list comprehensions across multiple lines, especially if they're nested.

+

You can write list comprehensions across several lines. So if you’re not in GHCI, it’s better to split longer list comprehensions across multiple lines, especially if they’re nested.

Tuples

tuples -

In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That's its type and it doesn't matter if it has only one number in it or an infinite amount of numbers. Tuples, however, are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.

-

Another key difference is that they don't have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

-

Think about how we'd represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it's still a list of lists with numbers, but it kind of doesn't make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can't have a couple of pairs in it and then a triple (a tuple of size three), so let's use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we'd get this error:

+

In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That’s its type and it doesn’t matter if it has only one number in it or an infinite amount of numbers. Tuples, however, are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.

+

Another key difference is that they don’t have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

+

Think about how we’d represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it’s still a list of lists with numbers, but it kind of doesn’t make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can’t have a couple of pairs in it and then a triple (a tuple of size three), so let’s use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we’d get this error:

     • Couldn't match expected type ‘(a, b)’
                   with actual type ‘(a0, b0, c0)’
@@ -489,10 +489,10 @@ 

Starting Out

• Relevant bindings include it :: [(a, b)] (bound at <interactive>:1:1)
-

It's telling us that we tried to use a pair and a triple in the same list, which is not supposed to happen. You also couldn't make a list like [(1,2),("One",2)] because the first element of the list is a pair of numbers and the second element is a pair consisting of a string and a number. Tuples can also be used to represent a wide variety of data. For instance, if we wanted to represent someone's name and age in Haskell, we could use a triple: ("Christopher", "Walken", 55). As seen in this example, tuples can also contain lists.

-

Use tuples when you know in advance how many components some piece of data should have. Tuples are much more rigid because each different size of tuple is its own type, so you can't write a general function to append an element to a tuple — you'd have to write a function for appending to a pair, one function for appending to a triple, one function for appending to a 4-tuple, etc.

-

While there are singleton lists, there's no such thing as a singleton tuple. It doesn't really make much sense when you think about it. A singleton tuple would just be the value it contains and as such would have no benefit to us.

-

Like lists, tuples can be compared with each other if their components can be compared. Only you can't compare two tuples of different sizes, whereas you can compare two lists of different sizes. Two useful functions that operate on pairs:

+

It’s telling us that we tried to use a pair and a triple in the same list, which is not supposed to happen. You also couldn’t make a list like [(1,2),("One",2)] because the first element of the list is a pair of numbers and the second element is a pair consisting of a string and a number. Tuples can also be used to represent a wide variety of data. For instance, if we wanted to represent someone’s name and age in Haskell, we could use a triple: ("Christopher", "Walken", 55). As seen in this example, tuples can also contain lists.

+

Use tuples when you know in advance how many components some piece of data should have. Tuples are much more rigid because each different size of tuple is its own type, so you can’t write a general function to append an element to a tuple — you’d have to write a function for appending to a pair, one function for appending to a triple, one function for appending to a 4-tuple, etc.

+

While there are singleton lists, there’s no such thing as a singleton tuple. It doesn’t really make much sense when you think about it. A singleton tuple would just be the value it contains and as such would have no benefit to us.

+

Like lists, tuples can be compared with each other if their components can be compared. Only you can’t compare two tuples of different sizes, whereas you can compare two lists of different sizes. Two useful functions that operate on pairs:

fst takes a pair and returns its first component.

 ghci> fst (8,11)
@@ -505,15 +505,15 @@ 

Starting Out

11 ghci> snd ("Wow", False) False
-
Note: these functions operate only on pairs. They won't work on triples, 4-tuples, 5-tuples, etc. We'll go over extracting data from tuples in different ways a bit later.
-

A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It's a really simple function but it has loads of uses. It's especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here's a demonstration.

+
Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.
+

A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.

 ghci> zip [1,2,3,4,5] [5,5,5,5,5]
 [(1,5),(2,5),(3,5),(4,5),(5,5)]
 ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"]
 [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
 
-

It pairs up the elements and produces a new list. The first element goes with the first, the second with the second, etc. Notice that because pairs can have different types in them, zip can take two lists that contain different types and zip them up. What happens if the lengths of the lists don't match?

+

It pairs up the elements and produces a new list. The first element goes with the first, the second with the second, etc. Notice that because pairs can have different types in them, zip can take two lists that contain different types and zip them up. What happens if the lengths of the lists don’t match?

 ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["im","a","turtle"]
 [(5,"im"),(3,"a"),(2,"turtle")]
@@ -524,18 +524,18 @@ 

Starting Out

[(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
look at meee -

Here's a problem that combines tuples and list comprehensions: which right triangle that has integers for all sides and all sides equal to or smaller than 10 has a perimeter of 24? First, let's try generating all triangles with sides equal to or smaller than 10:

+

Here’s a problem that combines tuples and list comprehensions: which right triangle that has integers for all sides and all sides equal to or smaller than 10 has a perimeter of 24? First, let’s try generating all triangles with sides equal to or smaller than 10:

ghci> triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ] 
-

We're just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out triangles in GHCI, you'll get a list of all possible triangles with sides under or equal to 10. Next, we'll add a condition that they all have to be right triangles. We'll also modify this function by taking into consideration that side b isn't larger than the hypothenuse and that side a isn't larger than side b.

+

We’re just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out triangles in GHCI, you’ll get a list of all possible triangles with sides under or equal to 10. Next, we’ll add a condition that they all have to be right triangles. We’ll also modify this function by taking into consideration that side b isn’t larger than the hypothenuse and that side a isn’t larger than side b.

 ghci> rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2] 
-

We're almost done. Now, we just modify the function by saying that we want the ones where the perimeter is 24.

+

We’re almost done. Now, we just modify the function by saying that we want the ones where the perimeter is 24.

 ghci> rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
 ghci> rightTriangles'
 [(6,8,10)]
 
-

And there's our answer! This is a common pattern in functional programming. You take a starting set of solutions and then you apply transformations to those solutions and filter them until you get the right ones.

+

And there’s our answer! This is a common pattern in functional programming. You take a starting set of solutions and then you apply transformations to those solutions and filter them until you get the right ones.

-If you look at monadic values as values with a context and return as taking a value and putting it in a default +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 @@ -1892,8 +1892,8 @@

Left identity

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: +because, well, we know what the value is. Here’s some +return usage with Maybe:

@@ -1919,8 +1919,8 @@ 

Left identity

-We said that for IO, using return makes an I/O action that has no side-effects but +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.

@@ -1928,8 +1928,8 @@

Left identity

Right identity

-The second law states that if we have a monadic value and we use >>= to feed it to return, +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:

@@ -1940,8 +1940,8 @@

Right identity

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 +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 @@ -1977,8 +1977,8 @@

Right identity

-Left identity and right identity are basically laws that describe how return should behave. It’s an important function for +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.

@@ -1997,13 +1997,13 @@

Associativity

-Hmmm, now what’s going on here? We have one monadic value, m and two monadic functions f +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 +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.

@@ -2048,8 +2048,8 @@

Associativity

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, +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. @@ -2058,8 +2058,8 @@

Associativity

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: +composing two functions, f and +g. Composing two functions is implemented like so:

@@ -2070,8 +2070,8 @@ 

Associativity

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 +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 diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index f7ee7d5..d712edf 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -37,8 +37,8 @@

For a Few Monads More

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 +functions and how using >>= or +do notation allows us to focus on the values themselves while the context gets handled for us.

@@ -59,12 +59,12 @@

For a Few Monads More

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

@@ -72,15 +72,15 @@

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 +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 +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. @@ -124,8 +124,7 @@

Writer? I hardly know her!

(True,"Compared gang size to 9.")
-when you have to poop, poop, don’t talk +when you have to poop, poop, don’t talk

So far so good. isBigGang takes a normal value and @@ -140,17 +139,17 @@

Writer? I hardly know her!

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

@@ -159,8 +158,8 @@

Writer? I hardly know her!

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

@@ -174,13 +173,13 @@

Writer? I hardly know her!

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, +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 +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 ++ @@ -225,8 +224,8 @@

Monoids to the rescue

-Right now, applyLog takes values of type (a,String), but is there a reason that the log has to be a +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: @@ -275,8 +274,8 @@

Monoids to the rescue

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

@@ -295,8 +294,8 @@

Monoids to the rescue

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 +track of how many cents something costs. Just a reminder, doing +mappend with Sum results in the wrapped values getting added together:

@@ -307,12 +306,12 @@

Monoids to the rescue

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

@@ -336,8 +335,8 @@

Monoids to the rescue

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

@@ -349,8 +348,8 @@

Monoids to the rescue

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 +And if we use applyLog to feed that to +addDrink, we get another beer and the result is ("beer", Sum 65).

@@ -360,8 +359,8 @@

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 +the Writer w a type along with its +Monad instance and some useful functions for dealing with values of this type.

@@ -401,15 +400,15 @@

The Writer type

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 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. +Writer constructor so that our result is a +Writer value instead of just an unwrapped tuple.

@@ -417,11 +416,11 @@

The Writer type

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 +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 +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 @@ -440,20 +439,20 @@

The Writer type

-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 +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. +for fail, so if a pattern match fails in +do notation, error is called.

Using do notation with Writer

@@ -484,15 +483,15 @@

Using do notation with Writer

-logNumber takes a number and makes a Writer value out of it. For the monoid, we use a list of +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 +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 +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:

@@ -507,10 +506,10 @@

Using do notation with Writer

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. +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: @@ -529,9 +528,9 @@

Using do notation with Writer

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

@@ -608,9 +607,9 @@

Adding logging to programs

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 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 @@ -627,17 +626,17 @@

Adding logging to programs

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

@@ -674,8 +673,8 @@

Adding logging to programs

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). +application to >>= (or +do expressions if it increases readability).

Inefficient list construction

@@ -750,8 +749,8 @@

Inefficient list construction

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

@@ -773,8 +772,8 @@

Difference lists

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

@@ -787,8 +786,8 @@

Difference lists

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

@@ -854,8 +853,8 @@

Difference lists

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

@@ -892,9 +891,9 @@

Difference lists

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

@@ -977,20 +976,19 @@

Comparing Performance

Reader? Ugh, not this joke again.

-bang youre dead +bang youre 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 +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: +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:

@@ -1014,10 +1012,10 @@ 

Reader? Ugh, not this joke again.

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 +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 @@ -1095,8 +1093,8 @@

Reader? Ugh, not this joke again.

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, +(*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: @@ -1109,8 +1107,8 @@

Reader? Ugh, not this joke again.

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 +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 @@ -1138,8 +1136,7 @@

Reader? Ugh, not this joke again.

Tasteful stateful computations

-don’t jest with texas +don’t jest with texas

Haskell is a pure language and because of that, our programs are made of @@ -1173,8 +1170,8 @@

Tasteful stateful computations

-It took a generator gen and then random gen returned a Bool value +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 @@ -1202,15 +1199,15 @@

Tasteful stateful computations

-s is the type of the state and a the result of the stateful computations. +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 +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 @@ -1239,11 +1236,11 @@

Stacks and stones

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

@@ -1281,16 +1278,16 @@

Stacks and stones

-We take a stack and then we do push 3 stack, which results in a tuple. The first part of +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 +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 +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:

@@ -1329,8 +1326,8 @@

Stacks and stones

The State monad

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

@@ -1345,8 +1342,8 @@ 

The State monad

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: +thought of as values with contexts, let’s check out their +Monad instance:

@@ -1361,8 +1358,8 @@ 

The State monad

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 +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 @@ -1379,8 +1376,8 @@

The State monad

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: +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 @@ -1391,8 +1388,8 @@

The State monad

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 +apply that stateful computation g to the +newState. The result is a tuple of final result and final state!

@@ -1444,8 +1441,8 @@

The State monad

-We didn’t have to bind the second pop to a because we didn’t use that a +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:

@@ -1460,8 +1457,8 @@

The State monad

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 +stack and stop but if it isn’t 5, we push +3 and 8 back on? Well, here’s the code:

@@ -1487,8 +1484,8 @@

The State monad

Remember, do expressions result in monadic values and -with the State monad, a single do expression is also a stateful function. Because +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. @@ -1513,9 +1510,9 @@

The State monad

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: +useful functions, namely get and +put. For State, the +get function is implemented like this:

@@ -1560,8 +1557,8 @@ 

The State monad

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: +state has to stay the same. Now why is that? Well, for instance, for +Maybe, >>= has this type:

@@ -1571,8 +1568,8 @@ 

The State monad

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

@@ -1631,8 +1628,8 @@

Randomness and the state monad

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, +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: @@ -1664,9 +1661,9 @@

Error error on the wall

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

@@ -1687,8 +1684,8 @@

Error error on the wall

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

@@ -1703,15 +1700,15 @@ 

Error error on the wall

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. +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 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. @@ -1726,8 +1723,8 @@

Error error on the wall

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 +Error instance is, well, the +String type! In the case of String, the strMsg function just returns the string that it got:

@@ -1757,8 +1754,8 @@

Error error on the wall

-When we use >>= to feed a Left value to a function, +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 @@ -1839,9 +1836,9 @@

liftM and friends

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 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. @@ -1849,8 +1846,8 @@

liftM and friends

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 +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: @@ -1894,11 +1891,11 @@

liftM and friends

-We already know quite well how fmap works with Maybe values. And liftM does the +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 +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 @@ -1932,8 +1929,8 @@

liftM and friends

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

@@ -1951,8 +1948,8 @@

liftM and friends

-Using this applicative style makes things pretty easy. <$> is just fmap and +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:

@@ -1966,18 +1963,18 @@

liftM and friends

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

@@ -2034,8 +2031,8 @@ 

liftM and friends

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

@@ -2053,8 +2050,8 @@

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

@@ -2065,8 +2062,8 @@

The join function

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: +value, so it sort of flattens it. Here it is with some +Maybe values:

@@ -2082,10 +2079,10 @@ 

The join function

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 +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 @@ -2116,8 +2113,8 @@

The join function

The outer monoid value -"bbb" comes first and then to it "aaa" is appended. Intuitively speaking, when you want to +"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. @@ -2151,12 +2148,12 @@

The join function

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 +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 on to the top. +then push 10 gets carried out, pushing a +10 on to the top.

@@ -2175,8 +2172,8 @@

The join function

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

@@ -2187,21 +2184,20 @@

The join function

m
-im a cop too as well also +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 +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 +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).

@@ -2228,19 +2224,19 @@

filterM

-The predicate takes an element of the list and returns a Bool value. Now, what if the Bool value that it returned +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 +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. +some context attached as well, otherwise the context that each +Bool came with would be lost.

@@ -2253,8 +2249,8 @@

filterM

-The predicate returns a monadic value whose result is a Bool, but because it’s a monadic value, its context can be +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.

@@ -2272,8 +2268,8 @@

filterM

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: +provides a log of what it did. Of course, we’ll be using the +Writer monad for this:

@@ -2290,9 +2286,9 @@ 

filterM

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. +predicate. Sounds fancy, doesn’t it? If the number is smaller than +4 we report that we’re keeping it and then +return True.

@@ -2332,8 +2328,8 @@

filterM

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

@@ -2350,8 +2346,8 @@ 

filterM

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. +is like the original set, only we excluded the number +1.

@@ -2360,8 +2356,8 @@

filterM

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: +both keeps and drops every element from the list. Here’s our +powerset function:

@@ -2390,8 +2386,8 @@ 

filterM

foldM

-The monadic counterpart to foldl is foldM. If you remember your folds from the folds section, you know that foldl takes a binary +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 @@ -2423,10 +2419,10 @@

foldM

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

@@ -2437,8 +2433,8 @@

foldM

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 +possibility of failure, let’s make our binary function return a +Maybe accumulator instead of a normal one. Here’s the binary function:

@@ -2451,8 +2447,8 @@

foldM

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: +normal foldl, but we have to use +foldM. Here goes:

@@ -2463,17 +2459,16 @@ 

foldM

-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 +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! +i’ve found yellow!

When we were solving the problem of implementing a RPN calculator, @@ -2485,8 +2480,8 @@

Making a safe RPN calculator

-We implemented our RPN calculator by taking a string like "1 3 + 2 *", breaking it up into words to get something +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 @@ -2582,15 +2577,15 @@

Making a safe RPN calculator

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

@@ -2625,18 +2620,18 @@

Making a safe RPN calculator

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) +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 +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. +the result of the RPN calculation as the result of the final +Maybe value.

@@ -2708,8 +2703,8 @@

Composing monadic functions

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 <=< +have to use a foldM over a +foldr or anything because the <=< function makes sure that composition happens in a monadic fashion.

@@ -2738,8 +2733,8 @@

Composing monadic functions

-Using monadic function composition, we can make a function like in3, only instead of generating all the positions that the +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 @@ -2939,10 +2934,10 @@

Making monads

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

@@ -3010,9 +3005,9 @@

Making monads

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 +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 diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index 5ef5a4d..cf8596a 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -604,8 +604,8 @@

Functors, Applicative Functors and Monoids

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: +about how we might write the data declaration for our +ZipList a type. One way would be to do it like so:

@@ -714,10 +714,10 @@ 

Functors, Applicative Functors and Monoids

-It takes a [Char] value, such as "my sharona" -and returns a CharList value. From the above examples where we used +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 @@ -774,8 +774,8 @@

Using newtype to make type class instances

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 +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 @@ -1069,11 +1069,11 @@

type vs. newtype vs. <

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 +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. +and then moved on to more interesting ones, like +Functor and Applicative.

@@ -1087,11 +1087,11 @@

type vs. newtype vs. <

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 +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 +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 @@ -1111,8 +1111,8 @@

type vs. newtype vs. <

-It seems that both * together with 1 +It seems that both * together with +1 and ++ along with [] share some common properties:

@@ -1159,11 +1159,11 @@

type vs. newtype vs. < 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 +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: @@ -1177,8 +1177,7 @@

type vs. newtype vs. < mconcat = foldr mappend mempty -woof dee do!!! +woof dee do!!!

The Monoid type class is defined in @@ -1197,8 +1196,8 @@

type vs. newtype vs. <

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 +of like minBound from +Bounded. mempty represents the identity value for a particular monoid.

@@ -1208,11 +1207,11 @@

type vs. newtype vs. < 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 +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. @@ -1280,8 +1279,8 @@

Lists are monoids

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 +instance Monoid [], because +Monoid requires a concrete type for an instance.

@@ -1608,9 +1607,9 @@

The Ordering monoid

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 +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 @@ -1621,9 +1620,9 @@

The Ordering monoid

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 +parameter is kept unless it’s EQ, +LT `mappend` GT will result in +LT, whereas GT `mappend` LT will result in GT:

@@ -1640,8 +1639,8 @@

The Ordering monoid

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

@@ -1704,8 +1703,8 @@

The Ordering monoid

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 +has by first filtering it only for letters that are in the string +"aeiou" and then applying length to that.

@@ -1764,15 +1763,15 @@

Maybe the monoid

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

@@ -1843,8 +1842,8 @@

Maybe the monoid

-First is useful when we have a bunch of Maybe values +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:

@@ -1858,11 +1857,11 @@

Maybe the monoid

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 the Last a type, which works like First a, only the last non-Nothing -value is kept when mappending and using mconcat: +provides a the Last a type, which works like +First a, only the last +non-Nothing +value is kept when mappending and using +mconcat:

@@ -1950,8 +1949,8 @@ 

Using monoids to fold data structures

-Remember the tree data structure from the Making Our +Remember the tree data structure from the +Making Our Own Types and Typeclasses chapter? We defined it like this:

@@ -2062,10 +2061,10 @@

Using monoids to fold data structures

It has 5 at its root and then its left node is 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, +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:

diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index e1cfc9b..df643c5 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -394,8 +394,8 @@

Making Our Own Types and Typeclasses

values of the same type that were made using different constructors, the value which was made with a constructor that’s defined first is considered smaller. For instance, consider the Bool -type, which can have a value of either False or True. For the purpose of seeing how it behaves when +type, which can have a value of either False or +True. For the purpose of seeing how it behaves when compared, we can think of it as being implemented like this:

diff --git a/docs/zippers.html b/docs/zippers.html
index b77346d..ce975aa 100644
--- a/docs/zippers.html
+++ b/docs/zippers.html
@@ -136,16 +136,16 @@ 

Taking a walk

x (that’s becomes the 'P' in the root) and its left subtree l. Instead of giving a name to its right subtree, we further pattern match on it. We continue this -pattern matching until we reach the subtree whose root is our 'W'. Once we’ve done this, we rebuild the tree, only the +pattern matching until we reach the subtree whose root is our +'W'. Once we’ve done this, we rebuild the tree, only the subtree that contained the 'W' at its root now has a 'P'.

Is there a better way of doing this? How about we make our function take a tree -along with a list of directions. The directions will be either L or R, representing left and +along with a list of directions. The directions will be either +L or R, representing left and right respectively, and we’ll change the element that we arrive at if we follow the supplied directions. Here it is:

@@ -161,8 +161,8 @@

Taking a walk

-If the first element in our list of directions is L, we construct a new tree that’s like the old tree, only +If the first element in our list of directions is +L, we construct a new tree that’s like the old tree, only its left subtree has an element changed to 'P'. When we recursively call changeToP, we give it only the tail of the list of directions, because we already took a left. We do the same @@ -187,8 +187,8 @@

Taking a walk

This function is actually quite similar to changeToP, only instead of remembering stuff along the way and reconstructing the tree, it -ignores everything except its destination. Here we change the 'W' to a 'P' and see if the +ignores everything except its destination. Here we change the +'W' to a 'P' and see if the change in our new tree sticks:

@@ -236,8 +236,8 @@

A trail of breadcrumbs

-To represent our breadcrumbs, we’ll also use a list of Direction (which is either L +To represent our breadcrumbs, we’ll also use a list of +Direction (which is either L or R), only instead of calling it Directions, we’ll call it Breadcrumbs , because our directions will now be reversed since we’re leaving them as @@ -291,8 +291,8 @@

A trail of breadcrumbs

-To make walking along our tree clearer, we can use the -: function that we defined like so: +To make walking along our tree clearer, we can use the +-: function that we defined like so:

@@ -336,8 +336,8 @@ 

Going back up

Let’s modify our breadcrumbs so that they also contain information about everything -that we previously ignored when moving left and right. Instead of Direction, we’ll make a new data type: +that we previously ignored when moving left and right. Instead of +Direction, we’ll make a new data type:

@@ -345,10 +345,10 @@ 

Going back up

-Now, instead of just L, we have a LeftCrumb that also contains the element in the node that we -moved from and the right tree that we didn’t visit. Instead of R, we have RightCrumb, which +Now, instead of just L, we have a +LeftCrumb that also contains the element in the node that we +moved from and the right tree that we didn’t visit. Instead of +R, we have RightCrumb, which contains the element in the node that we moved from and the left tree that we didn’t visit.

@@ -495,8 +495,8 @@

Manipulating trees under focus

We go left, then right and then modify the root element by replacing it with a -'P'. This reads even better if we use -:: +'P'. This reads even better if we use +-::

@@ -576,8 +576,8 @@ 

I’m going straight to the top, oh yeah, up where the air is fresh and clea

If our trail of beefed up breadcrumbs is empty, this means that we’re already at the root of our tree, so we just return the current focus. Otherwise, we go up -to get the focus of the parent node and then recursively apply topMost to that. So now we can walk around our tree, going +to get the focus of the parent node and then recursively apply +topMost to that. So now we can walk around our tree, going left and right and up, applying modify and attach as we go along and then when we’re done with our modifications, we use topMost to focus on the @@ -637,14 +637,14 @@

Focusing on lists

paths that we didn’t take either. It seems that all we have to remember is the previous element. If we have a list like [3,4,5] and we know that the previous element was 2, we can go -back by just putting that element at the head of our list, getting [2,3,4,5]. +back by just putting that element at the head of our list, getting +[2,3,4,5].

Because a single breadcrumb here is just the element, we don’t really have to -put it inside a data type, like we did when we made the Crumb data type for tree zippers: +put it inside a data type, like we did when we made the +Crumb data type for tree zippers:

@@ -798,8 +798,8 @@ 

A zipper for our file system

focus on the file "dijon_poupon.doc", what should the breadcrumb that we leave look like? Well, it should contain the name of its parent folder along with the items that come before the file that we’re focusing -on and the items that come after it. So all we need is a Name and two lists of items. By keeping separate lists for +on and the items that come after it. So all we need is a +Name and two lists of items. By keeping separate lists for the items that come before the item that we’re focusing and for the items that come after it, we know exactly where to place it once we move back up. So this way, we know where the hole is. @@ -834,9 +834,9 @@

A zipper for our file system

Because our breadcrumb knew what the parent folder’s name was, as well as the -items that came before our focused item in the folder (that’s ls) and the ones that came after (that’s rs), moving up was easy. +items that came before our focused item in the folder (that’s +ls) and the ones that came after (that’s +rs), moving up was easy.

@@ -1144,8 +1144,8 @@

Watch your step

-We used return to put a zipper in a Just and then used >>= to +We used return to put a zipper in a +Just and then used >>= to feed that to our goRight function. First, we made a tree that has on its left an empty subtree and on its right a node that has two empty subtrees. When we try to go right once, the result is a success, because From dc9ac9074609f8aa2659847cb681c6d32a7dd00b Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Wed, 23 Nov 2022 14:41:54 +0900 Subject: [PATCH 07/27] Replace single quotes with ' in code blocks --- docs/a-fistful-of-monads.html | 24 ++--- docs/for-a-few-monads-more.html | 42 ++++---- ...tors-applicative-functors-and-monoids.html | 26 ++--- docs/higher-order-functions.html | 92 ++++++++-------- docs/input-and-output.html | 102 +++++++++--------- .../making-our-own-types-and-typeclasses.html | 18 ++-- docs/modules.html | 58 +++++----- docs/recursion.html | 58 +++++----- docs/starting-out.html | 24 ++--- docs/syntax-in-functions.html | 80 +++++++------- docs/types-and-typeclasses.html | 36 +++---- docs/zippers.html | 54 +++++----- 12 files changed, 307 insertions(+), 307 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 6e31c17..22a6b67 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -1461,8 +1461,8 @@

The list monad

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

The list monad

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

The list monad

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

@@ -1553,7 +1553,7 @@

The list monad

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

The list monad

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

The list monad

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

The list monad

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

A knight’s quest

 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',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')
+    guard (c' `elem` [1..8] && r' `elem` [1..8])
+    return (c',r')
 

diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index d712edf..1233ec1 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -390,7 +390,7 @@

The Writer type

 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')
+    (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')
 
@@ -552,10 +552,10 @@

Adding logging to programs

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

@@ -573,7 +573,7 @@

Adding logging to programs

-ghci> gcd' 8 3
+ghci> gcd' 8 3
 1
 
@@ -585,7 +585,7 @@

Adding logging to programs

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

@@ -595,14 +595,14 @@

Adding logging to programs

 import Control.Monad.Writer
 
-gcd' :: Int -> Int -> Writer [String] Int
-gcd' a b
+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)
+        gcd' b (a `mod` b)
 

@@ -649,7 +649,7 @@

Adding logging to programs

-ghci> fst $ runWriter (gcd' 8 3)
+ghci> fst $ runWriter (gcd' 8 3)
 1
 
@@ -659,7 +659,7 @@

Adding logging to programs

-ghci> mapM_ putStrLn $ snd $ runWriter (gcd' 8 3)
+ghci> mapM_ putStrLn $ snd $ runWriter (gcd' 8 3)
 8 mod 3 = 2
 3 mod 2 = 1
 2 mod 1 = 0
@@ -862,13 +862,13 @@ 

Difference lists

 import Control.Monad.Writer
 
-gcd' :: Int -> Int -> Writer (DiffList String) Int
-gcd' a b
+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)
+        result <- gcd' b (a `mod` b)
         tell (toDiffList [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)])
         return result
 
@@ -1164,8 +1164,8 @@

Tasteful stateful computations

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

Error error on the wall

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 + 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)
@@ -2948,8 +2948,8 @@

Making monads

 thisSituation :: Prob (Prob Char)
 thisSituation = Prob
-    [( Prob [('a',1%2),('b',1%2)] , 1%4 )
-    ,( Prob [('c',1%2),('d',1%2)] , 3%4)
+    [( Prob [('a',1%2),('b',1%2)] , 1%4 )
+    ,( Prob [('c',1%2),('d',1%2)] , 3%4)
     ]
 
diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index cf8596a..e968007 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -55,9 +55,9 @@

Functors, Applicative Functors and Monoids

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!"
+          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:

@@ -74,7 +74,7 @@ 

Functors, Applicative Functors and Monoids

import Data.Char import Data.List -main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine +main = do line <- fmap (intersperse '-' . reverse . map toUpper) getLine putStrLn line
@@ -225,8 +225,8 @@ 

Functors, Applicative Functors and Monoids

 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 (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]
@@ -444,7 +444,7 @@ 

Functors, Applicative Functors and Monoids

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')] +[('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.

@@ -1833,12 +1833,12 @@

Maybe the monoid

-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'
+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'
 

diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index c81e931..ecbcd46 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -78,7 +78,7 @@

Higher Order Functions

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'])
+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?

@@ -86,10 +86,10 @@

Higher Order Functions

ghci> multThree 3 4 <interactive>:1:0: No instance for (Show (t -> t)) - arising from a use of `print' at <interactive>:1:0-12 + 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 + 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

@@ -117,23 +117,23 @@

Higher Order Functions

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
+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]
+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]
+ghci> zipWith' max [6,3,2,1] [7,3,1,5]
 [7,3,2,5]
-ghci> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
+ghci> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
 ["foo fighters","bar hoppers","baz aldrin"]
-ghci> zipWith' (*) (replicate 5 2) [1..]
+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]]
+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]]
 

@@ -141,20 +141,20 @@

Higher Order Functions

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
+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
+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]
+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

@@ -196,9 +196,9 @@

Higher Order Functions

[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" +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" +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.

@@ -297,8 +297,8 @@

Higher Order Functions

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

@@ -308,33 +308,33 @@

Higher Order Functions

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
+sum' :: (Num a) => [a] -> a
+sum' xs = foldl (\acc x -> acc + x) 0 xs
 

Testing, one two three:

-ghci> sum' [3,5,2,1]
+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
+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
+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
+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.

@@ -344,23 +344,23 @@

Higher Order Functions

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)
+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) []
+reverse' :: [a] -> [a]
+reverse' = foldl (\acc x -> x : acc) []
 
-product' :: (Num a) => [a] -> a
-product' = foldr1 (*)
+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) []
+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)
+head' :: [a] -> a
+head' = foldr1 (\x _ -> x)
 
-last' :: [a] -> a
-last' = foldl1 (\_ 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].

@@ -437,8 +437,8 @@

Higher Order Functions

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
+sum' :: (Num a) => [a] -> a
+sum' xs = foldl (+) 0 xs
 

The xs is exposed on both right sides. 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?

diff --git a/docs/input-and-output.html b/docs/input-and-output.html
index e743ed0..6fcd5cf 100644
--- a/docs/input-and-output.html
+++ b/docs/input-and-output.html
@@ -70,7 +70,7 @@ 

Input and Output

Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

 main = do
-    putStrLn "Hello, what's your name?"
+    putStrLn "Hello, what's your name?"
     name <- getLine
     putStrLn ("Hey " ++ name ++ ", you rock!")
 
@@ -86,7 +86,7 @@

Input and Output

When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

 main = do
-    putStrLn "Hello, what's your name?"
+    putStrLn "Hello, what's your name?"
     name <- getLine
     putStrLn $ "Read this carefully, because this is your future: " ++ tellFortune name
 
@@ -99,7 +99,7 @@

Input and Output

Every I/O action that gets performed has a result encapsulated within it. That’s why our previous example program could also have been written like this:

 main = do
-    foo <- putStrLn "Hello, what's your name?"
+    foo <- putStrLn "Hello, what's your name?"
     name <- getLine
     putStrLn ("Hey " ++ name ++ ", you rock!")
 
@@ -122,9 +122,9 @@

Input and Output

import Data.Char main = do - putStrLn "What's your first name?" + putStrLn "What's your first name?" firstName <- getLine - putStrLn "What's your last name?" + putStrLn "What's your last name?" lastName <- getLine let bigFirstName = map toUpper firstName bigLastName = map toUpper lastName @@ -187,19 +187,19 @@

Input and Output

putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

 main = do   putStr "Hey, "
-            putStr "I'm "
+            putStr "I'm "
             putStrLn "Andy!"
 
 $ runhaskell putstr_test.hs
-Hey, I'm Andy!
+Hey, I'm Andy!
 

Its type signature is putStr :: String -> IO (), so the result encapsulated within the resulting I/O action is the unit. A dud value, so it doesn’t make sense to bind it.

putChar takes a character and returns an I/O action that will print it out to the terminal.

-main = do   putChar 't'
-            putChar 'e'
-            putChar 'h'
+main = do   putChar 't'
+            putChar 'e'
+            putChar 'h'
 
 $ runhaskell putchar_test.hs
@@ -246,7 +246,7 @@ 

Input and Output

 main = do
     c <- getChar
-    if c /= ' '
+    if c /= ' '
         then do
             putChar c
             main
@@ -265,7 +265,7 @@ 

Input and Output

main = do c <- getChar - when (c /= ' ') $ do + when (c /= ' ') $ do putChar c main
@@ -356,9 +356,9 @@

Input and Output

getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: "Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!".

getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

-I'm a lil' teapot
-What's with that airplane food, huh?
-It's so small, tasteless
+I'm a lil' teapot
+What's with that airplane food, huh?
+It's so small, tasteless
 

Yeah, the haiku sucks, what of it? If anyone knows of any good haiku tutorials, let me know.

Now, recall the little program we wrote when we were introducing the forever function. It prompted the user for a line, returned it to him in CAPSLOCK and then did that all over again, indefinitely. Just so you don’t have to scroll all the way back, here it is again:

@@ -377,13 +377,13 @@

Input and Output

[1 of 1] Compiling Main ( capslocker.hs, capslocker.o ) Linking capslocker ... $ cat haiku.txt -I'm a lil' teapot -What's with that airplane food, huh? -It's so small, tasteless +I'm a lil' teapot +What's with that airplane food, huh? +It's so small, tasteless $ cat haiku.txt | ./capslocker -I'M A LIL' TEAPOT -WHAT'S WITH THAT AIRPLANE FOOD, HUH? -IT'S SO SMALL, TASTELESS +I'M A LIL' TEAPOT +WHAT'S WITH THAT AIRPLANE FOOD, HUH? +IT'S SO SMALL, TASTELESS capslocker <stdin>: hGetLine: end of file

As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

@@ -398,9 +398,9 @@

Input and Output

We run the getContents I/O action and name the string it produces contents. Then, we map toUpper over that string and print that to the terminal. Keep in mind that because strings are basically lists, which are lazy, and getContents is I/O lazy, it won’t try to read the whole content at once and store it into memory before printing out the capslocked version. Rather, it will print out the capslocked version as it reads it, because it will only read a line from the input when it really needs to.

 $ cat haiku.txt | ./capslocker
-I'M A LIL' TEAPOT
-WHAT'S WITH THAT AIRPLANE FOOD, HUH?
-IT'S SO SMALL, TASTELESS
+I'M A LIL' TEAPOT
+WHAT'S WITH THAT AIRPLANE FOOD, HUH?
+IT'S SO SMALL, TASTELESS
 

Cool, it works. What if we just run capslocker and try to type in the lines ourselves?

@@ -427,10 +427,10 @@ 

Input and Output

We’ve made our I/O part of the program as short as possible. Because our program is supposed to take some input and print out some output based on the input, we can implement it by reading the input contents, running a function on them and then printing out what the function gave back.

The shortLinesOnly function works like this: it takes a string, like "short\nlooooooooooooooong\nshort again". That string has three lines, two of them are short and the middle one is long. It runs the lines function on that string, which converts it to ["short", "looooooooooooooong", "short again"], which we then bind to the name allLines. That list of string is then filtered so that only those lines that are shorter than 10 characters remain in the list, producing ["short", "short again"]. And finally, unlines joins that list into a single newline delimited string, giving "short\nshort again". Let’s give it a go.

-i'm short
+i'm short
 so am i
 i am a loooooooooong line!!!
-yeah i'm long so what hahahaha!!!!!!
+yeah i'm long so what hahahaha!!!!!!
 short line
 loooooooooooooooooooooooooooong
 short
@@ -440,7 +440,7 @@ 

Input and Output

[1 of 1] Compiling Main ( shortlinesonly.hs, shortlinesonly.o ) Linking shortlinesonly ... $ cat shortlines.txt | ./shortlinesonly -i'm short +i'm short so am i short
@@ -509,7 +509,7 @@

Input and Output

We’ll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne’s #1 hit Girlfriend, and just prints it out to the terminal. Here’s girlfriend.txt:

 Hey! Hey! You! You!
-I don't like your girlfriend!
+I don't like your girlfriend!
 No way! No way!
 I think you need a new one!
 
@@ -527,7 +527,7 @@

Input and Output

 $ runhaskell girlfriend.hs
 Hey! Hey! You! You!
-I don't like your girlfriend!
+I don't like your girlfriend!
 No way! No way!
 I think you need a new one!
 
@@ -558,8 +558,8 @@

Input and Output

As you can see, it’s very similar to the previous piece of code. (\handle -> ... ) is the function that takes a handle and returns an I/O action and it’s usually done like this, with a lambda. The reason it has to take a function that returns an I/O action instead of just taking an I/O action to do and then close the file is because the I/O action that we’d pass to it wouldn’t know on which file to operate. This way, withFile opens the file and then passes the handle to the function we gave it. It gets an I/O action back from that function and then makes an I/O action that’s just like it, only it closes the file afterwards. Here’s how we can make our own withFile function:

-withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
-withFile' path mode f = do
+withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
+withFile' path mode f = do
     handle <- openFile path mode
     result <- f handle
     hClose handle
@@ -602,7 +602,7 @@ 

Input and Output

$ runhaskell girlfriendtocaps.hs $ cat girlfriendcaps.txt HEY! HEY! YOU! YOU! -I DON'T LIKE YOUR GIRLFRIEND! +I DON'T LIKE YOUR GIRLFRIEND! NO WAY! NO WAY! I THINK YOU NEED A NEW ONE!
@@ -889,8 +889,8 @@

Input and Output

 <interactive>:1:0:
-    Ambiguous type variable `a' in the constraint:
-      `Random a' arising from a use of `random' at <interactive>:1:0-20
+    Ambiguous type variable `a' in the constraint:
+      `Random a' arising from a use of `random' at <interactive>:1:0-20
     Probable fix: add a type signature that fixes these type variable(s)
 

What’s this? Ah, right, the random function can return a value of any type that’s part of the Random typeclass, so we have to inform Haskell what kind of type we want. Also let’s not forget that it returns a random value and a random generator in a pair.

@@ -923,8 +923,8 @@

Input and Output

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

We call random with the generator we got as a parameter to get a coin and a new generator. Then we call it again, only this time with our new generator, to get the second coin. We do the same for the third coin. Had we called it with the same generator every time, all the coins would have had the same value and we’d only be able to get (False, False, False) or (True, True, True) as a result.

@@ -950,8 +950,8 @@

Input and Output

Why doesn’t randoms return a new generator as well as a list? We could implement the randoms function very easily like this:

-randoms' :: (RandomGen g, Random a) => g -> [a]
-randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
+randoms' :: (RandomGen g, Random a) => g -> [a]
+randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
 

A recursive definition. We get a random value and a new generator from the current generator and then make a list that has the value as its head and random numbers based on the new generator as its tail. Because we have to be able to potentially generate an infinite amount of numbers, we can’t give the new random generator back.

We could make a function that generates a finite stream of numbers and a new generator like this:

@@ -973,7 +973,7 @@

Input and Output

There’s also randomRs, which produces a stream of random values within our defined ranges. Check this out:

-ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
+ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
 "ndkxbvmomg"
 

Nice, looks like a super secret password or something.

@@ -984,7 +984,7 @@

Input and Output

main = do gen <- getStdGen - putStr $ take 20 (randomRs ('a','z') gen) + putStr $ take 20 (randomRs ('a','z') gen)
 $ runhaskell random_string.hs
@@ -1002,9 +1002,9 @@ 

Input and Output

main = do gen <- getStdGen - putStrLn $ take 20 (randomRs ('a','z') gen) + putStrLn $ take 20 (randomRs ('a','z') gen) gen2 <- getStdGen - putStr $ take 20 (randomRs ('a','z') gen2) + putStr $ take 20 (randomRs ('a','z') gen2)

you will get the same string printed out twice! One way to get two different strings of length 20 is to set up an infinite stream and then take the first 20 characters and print them out in one line and then take the second set of 20 characters and print them out in the second line. For this, we can use the splitAt function from Data.List, which splits a list at some index and returns a tuple that has the first part as the first component and the second part as the second component.

@@ -1013,7 +1013,7 @@ 

Input and Output

main = do gen <- getStdGen - let randomChars = randomRs ('a','z') gen + let randomChars = randomRs ('a','z') gen (first20, rest) = splitAt 20 randomChars (second20, _) = splitAt 20 rest putStrLn first20 @@ -1025,9 +1025,9 @@

Input and Output

main = do gen <- getStdGen - putStrLn $ take 20 (randomRs ('a','z') gen) - gen' <- newStdGen - putStr $ take 20 (randomRs ('a','z') gen') + putStrLn $ take 20 (randomRs ('a','z') gen) + gen' <- newStdGen + putStr $ take 20 (randomRs ('a','z') gen')

Not only do we get a new random generator when we bind newStdGen to something, the global one gets updated as well, so if we do getStdGen again and bind it to something, we’ll get a generator that’s not the same as gen.

Here’s a little program that will make the user guess which number it’s thinking of.

@@ -1123,12 +1123,12 @@

Input and Output

 ghci> B.cons 85 $ B.pack [80,81,82,84]
 Chunk "U" (Chunk "PQRT" Empty)
-ghci> B.cons' 85 $ B.pack [80,81,82,84]
+ghci> B.cons' 85 $ B.pack [80,81,82,84]
 Chunk "UPQRT" Empty
 ghci> foldr B.cons B.empty [50..60]
 Chunk "2" (Chunk "3" (Chunk "4" (Chunk "5" (Chunk "6" (Chunk "7" (Chunk "8" (Chunk "9" (Chunk ":" (Chunk ";" (Chunk "<"
 Empty))))))))))
-ghci> foldr B.cons' B.empty [50..60]
+ghci> foldr B.cons' B.empty [50..60]
 Chunk "23456789:;<" Empty
 

As you can see, empty makes an empty bytestring. See the difference between cons and cons'? With the foldr, we started with an empty bytestring and then went over the list of numbers from the right, adding each number to the beginning of the bytestring. When we used cons, we ended up with one chunk for every byte, which kind of defeats the purpose.

@@ -1194,7 +1194,7 @@

Input and Output

if fileExists then do contents <- readFile fileName putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!" - else do putStrLn "The file doesn't exist!" + else do putStrLn "The file doesn't exist!"

We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

Another solution here would be to use exceptions. It’s perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

@@ -1242,7 +1242,7 @@

Input and Output

handler :: IOError -> IO () handler e - | isDoesNotExistError e = putStrLn "The file doesn't exist!" + | isDoesNotExistError e = putStrLn "The file doesn't exist!" | otherwise = ioError e

Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

@@ -1263,7 +1263,7 @@

Input and Output

 handler :: IOError -> IO ()
 handler e
-    | isDoesNotExistError e = putStrLn "The file doesn't exist!"
+    | isDoesNotExistError e = putStrLn "The file doesn't exist!"
     | isFullError e = freeSomeSpace
     | isIllegalOperation e = notifyCops
     | otherwise = ioError e
diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html
index df643c5..c6de9b0 100644
--- a/docs/making-our-own-types-and-typeclasses.html
+++ b/docs/making-our-own-types-and-typeclasses.html
@@ -539,8 +539,8 @@ 

Making Our Own Types and Typeclasses

Right 20 ghci> Left "w00t" Left "w00t" -ghci> :t Right 'a' -Right 'a' :: Either a Char +ghci> :t Right 'a' +Right 'a' :: Either a Char ghci> :t Left True Left True :: Either Bool b
@@ -560,7 +560,7 @@

Making Our Own Types and Typeclasses

lockerLookup :: Int -> LockerMap -> Either String Code lockerLookup lockerNumber map = case Map.lookup lockerNumber map of - Nothing -> Left $ "Locker number " ++ show lockerNumber ++ " doesn't exist!" + Nothing -> Left $ "Locker number " ++ show lockerNumber ++ " doesn't exist!" Just (state, code) -> if state /= Taken then Right code else Left $ "Locker " ++ show lockerNumber ++ " is already taken!" @@ -583,7 +583,7 @@

Making Our Own Types and Typeclasses

ghci> lockerLookup 100 lockers Left "Locker 100 is already taken!" ghci> lockerLookup 102 lockers -Left "Locker number 102 doesn't exist!" +Left "Locker number 102 doesn't exist!" ghci> lockerLookup 110 lockers Left "Locker 110 is already taken!" ghci> lockerLookup 105 lockers @@ -950,7 +950,7 @@

Making Our Own Types and Typeclasses

Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.

-

Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.
+

Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

Try figuring out how Map k is made an instance of Functor by yourself!

With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.
@@ -1008,8 +1008,8 @@

Making Our Own Types and Typeclasses

 ghci> :t Frank {frankField = Just "HAHA"}
 Frank {frankField = Just "HAHA"} :: Frank [Char] Maybe
-ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
-Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
+ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
+Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
 ghci> :t Frank {frankField = "YES"}
 Frank {frankField = "YES"} :: Frank Char []
 
@@ -1020,8 +1020,8 @@

Making Our Own Types and Typeclasses

tofu x = Frank x
-ghci> tofu (Just 'a') :: Frank Char Maybe
-Frank {frankField = Just 'a'}
+ghci> tofu (Just 'a') :: Frank Char Maybe
+Frank {frankField = Just 'a'}
 ghci> tofu ["HELLO"] :: Frank [Char] []
 Frank {frankField = ["HELLO"]}
 
diff --git a/docs/modules.html b/docs/modules.html index 98cd8ab..6f35c07 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -76,7 +76,7 @@

Modules

The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We’ve already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don’t have to import Data.List via a qualified import because it doesn’t clash with any Prelude names except for those that Prelude already steals from Data.List. Let’s take a look at some of the functions that we haven’t met before.

intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here’s a demonstration:

-ghci> intersperse '.' "MONKEY"
+ghci> intersperse '.' "MONKEY"
 "M.O.N.K.E.Y"
 ghci> intersperse 0 [1,2,3,4,5,6]
 [1,0,2,0,3,0,4,0,5,0,6]
@@ -136,9 +136,9 @@ 

Modules

True ghci> all (>4) [6,9,10] True -ghci> all (`elem` ['A'..'Z']) "HEYGUYSwhatsup" +ghci> all (`elem` ['A'..'Z']) "HEYGUYSwhatsup" False -ghci> any (`elem` ['A'..'Z']) "HEYGUYSwhatsup" +ghci> any (`elem` ['A'..'Z']) "HEYGUYSwhatsup" True

iterate takes a function and a starting value. It applies the function to the starting value, then it applies that function to the result, then it applies the function to that result again, etc. It returns all the results in the form of an infinite list.

@@ -163,7 +163,7 @@

Modules

 ghci> takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1]
 [6,5,4]
-ghci> takeWhile (/=' ') "This is a sentence"
+ghci> takeWhile (/=' ') "This is a sentence"
 "This"
 

Say we wanted to know the sum of all third powers that are under 10,000. We can’t map (^3) to [1..], apply a filter and then try to sum that up because filtering an infinite list never finishes. You may know that all the elements here are ascending but Haskell doesn’t. That’s why we can do this:

@@ -174,7 +174,7 @@

Modules

We apply (^3) to an infinite list and then once an element that’s over 10,000 is encountered, the list is cut off. Now we can sum it up easily.

dropWhile is similar, only it drops all the elements while the predicate is true. Once predicate equates to False, it returns the rest of the list. An extremely useful and lovely function!

-ghci> dropWhile (/=' ') "This is a sentence"
+ghci> dropWhile (/=' ') "This is a sentence"
 " is a sentence"
 ghci> dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1]
 [3,4,5,4,3,2,1]
@@ -187,7 +187,7 @@ 

Modules

span is kind of like takeWhile, only it returns a pair of lists. The first list contains everything the resulting list from takeWhile would contain if it were called with the same predicate and the same list. The second list contains the part of the list that would have been dropped.

-ghci> let (fw, rest) = span (/=' ') "This is a sentence" in "First word:" ++ fw ++ ", the rest:" ++ rest
+ghci> let (fw, rest) = span (/=' ') "This is a sentence" in "First word:" ++ fw ++ ", the rest:" ++ rest
 "First word: This, the rest: is a sentence"
 

Whereas span spans the list while the predicate is true, break breaks it when the predicate is first true. Doing break p is the equivalent of doing span (not . p).

@@ -255,14 +255,14 @@

Modules

elem and notElem check if an element is or isn’t inside a list.

partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don’t.

-ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
+ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
 ("BOBMORGAN","sidneyeddy")
 ghci> partition (>3) [1,3,5,6,3,2,1,0,3,7]
 ([5,6,7],[1,3,3,2,1,0,3])
 

It’s important to understand how this is different from span and break:

-ghci> span (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
+ghci> span (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
 ("BOB","sidneyMORGANeddy")
 

While span and break are done once they encounter the first element that doesn’t and does satisfy the predicate, partition goes through the whole list and splits it up according to the predicate.

@@ -288,7 +288,7 @@

Modules

elemIndices is like elemIndex, only it returns a list of indices, in case the element we’re looking for crops up in our list several times. Because we’re using a list to represent the indices, we don’t need a Maybe type, because failure can be represented as the empty list, which is very much synonymous to Nothing.

-ghci> ' ' `elemIndices` "Where are the spaces?"
+ghci> ' ' `elemIndices` "Where are the spaces?"
 [5,9,13]
 

findIndex is like find, but it maybe returns the index of the first element that satisfies the predicate. findIndices returns the indices of all elements that satisfy the predicate in the form of a list.

@@ -297,7 +297,7 @@

Modules

Just 5 ghci> findIndex (==7) [5,3,2,1,6,4] Nothing -ghci> findIndices (`elem` ['A'..'Z']) "Where Are The Caps?" +ghci> findIndices (`elem` ['A'..'Z']) "Where Are The Caps?" [0,6,10,14]

We already covered zip and zipWith. We noted that they zip together two lists, either in a tuple or with a binary function (meaning such a function that takes two parameters). But what if we want to zip together three lists? Or zip three lists with a function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. While this may look like a hack, it works out pretty fine, because there aren’t many times when you want to zip 8 lists together. There’s also a very clever way for zipping infinite numbers of lists, but we’re not advanced enough to cover that just yet.

@@ -337,11 +337,11 @@

Modules

delete takes an element and a list and deletes the first occurrence of that element in the list.

-ghci> delete 'h' "hey there ghang!"
+ghci> delete 'h' "hey there ghang!"
 "ey there ghang!"
-ghci> delete 'h' . delete 'h' $ "hey there ghang!"
+ghci> delete 'h' . delete 'h' $ "hey there ghang!"
 "ey tere ghang!"
-ghci> delete 'h' . delete 'h' . delete 'h' $ "hey there ghang!"
+ghci> delete 'h' . delete 'h' . delete 'h' $ "hey there ghang!"
 "ey tere gang!"
 

\\ is the list difference function. It acts like a set difference, basically. For every element in the right-hand list, it removes a matching element in the left one.

@@ -354,8 +354,8 @@

Modules

Doing [1..10] \\ [2,5,9] is like doing delete 2 . delete 5 . delete 9 $ [1..10].

union also acts like a function on sets. It returns the union of two lists. It pretty much goes over every element in the second list and appends it to the first one if it isn’t already in yet. Watch out though, duplicates are removed from the second list!

-ghci> "hey man" `union` "man what's up"
-"hey manwt'sup"
+ghci> "hey man" `union` "man what's up"
+"hey manwt'sup"
 ghci> [1..7] `union` [5..10]
 [1,2,3,4,5,6,7,8,9,10]
 
@@ -376,7 +376,7 @@

Modules

 ghci> insert 4 [1,2,3,5,6,7]
 [1,2,3,4,5,6,7]
-ghci> insert 'g' $ ['a'..'f'] ++ ['h'..'z']
+ghci> insert 'g' $ ['a'..'f'] ++ ['h'..'z']
 "abcdefghijklmnopqrstuvwxyz"
 ghci> insert 3 [1,2,4,3,2,1]
 [1,2,3,4,3,2,1]
@@ -456,15 +456,15 @@ 

Modules

Ah.

The Data.Char also exports a datatype that’s kind of like Ordering. The Ordering type can have a value of LT, EQ or GT. It’s a sort of enumeration. It describes a few possible results that can arise from comparing two elements. The GeneralCategory type is also an enumeration. It presents us with a few possible categories that a character can fall into. The main function for getting the general category of a character is generalCategory. It has a type of generalCategory :: Char -> GeneralCategory. There are about 31 categories so we won’t list them all here, but let’s play around with the function.

-ghci> generalCategory ' '
+ghci> generalCategory ' '
 Space
-ghci> generalCategory 'A'
+ghci> generalCategory 'A'
 UppercaseLetter
-ghci> generalCategory 'a'
+ghci> generalCategory 'a'
 LowercaseLetter
-ghci> generalCategory '.'
+ghci> generalCategory '.'
 OtherPunctuation
-ghci> generalCategory '9'
+ghci> generalCategory '9'
 DecimalNumber
 ghci> map generalCategory " \t\nA9?|"
 [Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol]
@@ -483,16 +483,16 @@ 

Modules

intToDigit is the inverse function of digitToInt. It takes an Int in the range of 0..15 and converts it to a lower-case character.

 ghci> intToDigit 15
-'f'
+'f'
 ghci> intToDigit 5
-'5'
+'5'
 

The ord and chr functions convert characters to their corresponding numbers and vice versa:

-ghci> ord 'a'
+ghci> ord 'a'
 97
 ghci> chr 97
-'a'
+'a'
 ghci> map ord "abcdefgh"
 [97,98,99,100,101,102,103,104]
 
@@ -609,8 +609,8 @@

Modules

We can implement our own fromList by using the empty map, insert and a fold. Watch:

-fromList' :: (Ord k) => [(k,v)] -> Map.Map k v
-fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
+fromList' :: (Ord k) => [(k,v)] -> Map.Map k v
+fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
 

It’s a pretty straightforward fold. We start of with an empty map and we fold it up from the right, inserting the key value pairs into the accumulator as we go along.

null checks if a map is empty.

@@ -646,8 +646,8 @@

Modules

 ghci> Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]
 fromList [(1,100),(2,400),(3,900)]
-ghci> Map.filter isUpper $ Map.fromList [(1,'a'),(2,'A'),(3,'b'),(4,'B')]
-fromList [(2,'A'),(4,'B')]
+ghci> Map.filter isUpper $ Map.fromList [(1,'a'),(2,'A'),(3,'b'),(4,'B')]
+fromList [(2,'A'),(4,'B')]
 

toList is the inverse of fromList.

diff --git a/docs/recursion.html b/docs/recursion.html
index b4a745a..d24ff31 100644
--- a/docs/recursion.html
+++ b/docs/recursion.html
@@ -41,74 +41,74 @@ 

Recursion

The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

Now let’s see how we’d define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it’s the maximum of the tail. That’s it! Now let’s implement that in Haskell.

-maximum' :: (Ord a) => [a] -> a
-maximum' [] = error "maximum of empty list"
-maximum' [x] = x
-maximum' (x:xs)
+maximum' :: (Ord a) => [a] -> a
+maximum' [] = error "maximum of empty list"
+maximum' [x] = x
+maximum' (x:xs)
     | x > maxTail = x
     | otherwise = maxTail
-    where maxTail = maximum' xs
+    where maxTail = maximum' xs
 

As you can see, pattern matching goes great with recursion! Most imperative languages don’t have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what’s the maximum of an empty list? I don’t know. The second pattern also lays out an edge condition. It says that if it’s the singleton list, just give back the only element.

Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here’s how we could rewrite maximum' by using max:

-maximum' :: (Ord a) => [a] -> a
-maximum' [] = error "maximum of empty list"
-maximum' [x] = x
-maximum' (x:xs) = max x (maximum' xs)
+maximum' :: (Ord a) => [a] -> a
+maximum' [] = error "maximum of empty list"
+maximum' [x] = x
+maximum' (x:xs) = max x (maximum' xs)
 

How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

max

A few more recursive functions

Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

-replicate' :: (Num i, Ord i) => i -> a -> [a]
-replicate' n x
+replicate' :: (Num i, Ord i) => i -> a -> [a]
+replicate' n x
     | n <= 0    = []
-    | otherwise = x:replicate' (n-1) x
+    | otherwise = x:replicate' (n-1) x
 

We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.

Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

-take' :: (Num i, Ord i) => i -> [a] -> [a]
-take' n _
+take' :: (Num i, Ord i) => i -> [a] -> [a]
+take' n _
     | n <= 0   = []
-take' _ []     = []
-take' n (x:xs) = x : take' (n-1) xs
+take' _ []     = []
+take' n (x:xs) = x : take' (n-1) xs
 
painter

The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

reverse simply reverses a list. Think about the edge condition. What is it? Come on … it’s the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

-reverse' :: [a] -> [a]
-reverse' [] = []
-reverse' (x:xs) = reverse' xs ++ [x]
+reverse' :: [a] -> [a]
+reverse' [] = []
+reverse' (x:xs) = reverse' xs ++ [x]
 

There we go!

Because Haskell supports infinite lists, our recursion doesn’t really have to have an edge condition. But if it doesn’t have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. repeat takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.

-repeat' :: a -> [a]
-repeat' x = x:repeat' x
+repeat' :: a -> [a]
+repeat' x = x:repeat' x
 

Calling repeat 3 will give us a list that starts with 3 and then has an infinite amount of 3’s as a tail. So calling repeat 3 would evaluate like 3:repeat 3, which is 3:(3:repeat 3), which is 3:(3:(3:repeat 3)), etc. repeat 3 will never finish evaluating, whereas take 5 (repeat 3) will give us a list of five 3’s. So essentially it’s like doing replicate 5 3.

zip takes two lists and zips them together. zip [1,2,3] [2,3] returns [(1,2),(2,3)], because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there’s our edge condition. However, zip takes two lists as parameters, so there are actually two edge conditions.

-zip' :: [a] -> [b] -> [(a,b)]
-zip' _ [] = []
-zip' [] _ = []
-zip' (x:xs) (y:ys) = (x,y):zip' xs ys
+zip' :: [a] -> [b] -> [(a,b)]
+zip' _ [] = []
+zip' [] _ = []
+zip' (x:xs) (y:ys) = (x,y):zip' xs ys
 

First two patterns say that if the first list or second list is empty, we get an empty list. The third one says that two lists zipped are equal to pairing up their heads and then tacking on the zipped tails. Zipping [1,2,3] and ['a','b'] will eventually try to zip [3] with []. The edge condition patterns kick in and so the result is (1,'a'):(2,'b'):[], which is exactly the same as [(1,'a'),(2,'b')].

Let’s implement one more standard library function — elem. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn’t have the droids we’re looking for.

-elem' :: (Eq a) => a -> [a] -> Bool
-elem' a [] = False
-elem' a (x:xs)
+elem' :: (Eq a) => a -> [a] -> Bool
+elem' a [] = False
+elem' a (x:xs)
     | a == x    = True
-    | otherwise = a `elem'` xs
+    | otherwise = a `elem'` xs
 

Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

Quick, sort!

diff --git a/docs/starting-out.html b/docs/starting-out.html index 47f6056..e506063 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -195,13 +195,13 @@

Starting Out

Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this.

-doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
+doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
 

Had we omitted the parentheses, it would have added one only if x wasn’t greater than 100. Note the ' at the end of the function name. That apostrophe doesn’t have any special meaning in Haskell’s syntax. It’s a valid character to use in a function name. We usually use ' to either denote a strict version of a function (one that isn’t lazy) or a slightly modified version of a function or a variable. Because ' is a valid character in functions, we can make a function like this.

-conanO'Brien = "It's a-me, Conan O'Brien!" 
+conanO'Brien = "It's a-me, Conan O'Brien!"

There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably.

@@ -227,14 +227,14 @@

Starting Out

[1,2,3,4,9,10,11,12] ghci> "hello" ++ " " ++ "world" "hello world" -ghci> ['w','o'] ++ ['o','t'] +ghci> ['w','o'] ++ ['o','t'] "woot"

Watch out when repeatedly using the ++ operator on long strings. When you put together two lists (even if you append a singleton list to a list, for instance: [1,2,3] ++ [4]), internally, Haskell has to walk through the whole list on the left side of ++. That’s not a problem when dealing with lists that aren’t too big. But putting something at the end of a list that’s fifty million entries long is going to take a while. However, putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous.

-ghci> 'A':" SMALL CAT"
+ghci> 'A':" SMALL CAT"
 "A SMALL CAT"
 ghci> 5:[1,2,3,4,5]
 [5,1,2,3,4,5]
@@ -248,7 +248,7 @@ 

Starting Out

If you want to get an element out of a list by index, use !!. The indices start at 0.

 ghci> "Steve Buscemi" !! 6
-'B'
+'B'
 ghci> [9.4,33.2,96.2,11.2,23.25] !! 1
 33.2
 
@@ -371,9 +371,9 @@

Starting Out

 ghci> [1..20]
 [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
-ghci> ['a'..'z']
+ghci> ['a'..'z']
 "abcdefghijklmnopqrstuvwxyz"
-ghci> ['K'..'Z']
+ghci> ['K'..'Z']
 "KLMNOPQRSTUVWXYZ" 

Ranges are cool because you can also specify a step. What if we want all even numbers between 1 and 20? Or every third number between 1 and 20? @@ -455,11 +455,11 @@

Starting Out

"grouchy pope","scheming hobo","scheming frog","scheming pope"]

I know! Let’s write our own version of length! We’ll call it length'.

-length' xs = sum [1 | _ <- xs] 
+length' xs = sum [1 | _ <- xs]

_ means that we don’t care what we’ll draw from the list anyway so instead of writing a variable name that we’ll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.

Just a friendly reminder: because strings are lists, we can use list comprehensions to process and produce strings. Here’s a function that takes a string and removes everything except uppercase letters from it.

-removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] 
+removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]

Testing it out:

@@ -481,7 +481,7 @@

Starting Out

Another key difference is that they don’t have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

Think about how we’d represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it’s still a list of lists with numbers, but it kind of doesn’t make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can’t have a couple of pairs in it and then a triple (a tuple of size three), so let’s use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we’d get this error:

-    • Couldn't match expected type ‘(a, b)’
+    • Couldn't match expected type ‘(a, b)’
                   with actual type ‘(a0, b0, c0)’
     • In the expression: (8, 11, 5)
       In the expression: [(1, 2), (8, 11, 5), (4, 5)]
@@ -531,8 +531,8 @@ 

Starting Out

ghci> rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]

We’re almost done. Now, we just modify the function by saying that we want the ones where the perimeter is 24.

-ghci> rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
-ghci> rightTriangles'
+ghci> rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
+ghci> rightTriangles'
 [(6,8,10)]
 

And there’s our answer! This is a common pattern in functional programming. You take a starting set of solutions and then you apply transformations to those solutions and filter them until you get the right ones.

diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index a690f69..5c52355 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -39,7 +39,7 @@

Syntax in Functions

 lucky :: (Integral a) => a -> String
 lucky 7 = "LUCKY NUMBER SEVEN!"
-lucky x = "Sorry, you're out of luck, pal!" 
+lucky x = "Sorry, you're out of luck, pal!"

When you call lucky, the patterns will be checked from top to bottom and when it conforms to a pattern, the corresponding function body will be used. The only way a number can conform to the first pattern here is if it is 7. If it’s not, it falls through to the second pattern, which matches anything and binds it to x. This function could have also been implemented by using an if statement. But what if we wanted a function that says the numbers from 1 to 5 and says "Not between 1 and 5" for any other number? Without pattern matching, we’d have to make a pretty convoluted if then else tree. However, with it:

 sayMe :: (Integral a) => a -> String
@@ -64,17 +64,17 @@ 

Syntax in Functions

 charName :: Char -> String
-charName 'a' = "Albert"
-charName 'b' = "Broseph"
-charName 'c' = "Cecil"
+charName 'a' = "Albert"
+charName 'b' = "Broseph"
+charName 'c' = "Cecil"
 

and then try to call it with an input that we didn’t expect, this is what happens:

-ghci> charName 'a'
+ghci> charName 'a'
 "Albert"
-ghci> charName 'b'
+ghci> charName 'b'
 "Broseph"
-ghci> charName 'h'
+ghci> charName 'h'
 "*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in function charName
 

@@ -116,16 +116,16 @@

Syntax in Functions

If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.

Now that we know how to pattern match against list, let’s make our own implementation of the head function.

-head' :: [a] -> a
-head' [] = error "Can't call head on an empty list, dummy!"
-head' (x:_) = x
+head' :: [a] -> a
+head' [] = error "Can't call head on an empty list, dummy!"
+head' (x:_) = x
 

Checking if it works:

-ghci> head' [4,5,6]
+ghci> head' [4,5,6]
 4
-ghci> head' "Hello"
-'H'
+ghci> head' "Hello"
+'H'
 

Nice! Notice that if you want to bind to several variables (even if one of them is just _ and doesn’t actually bind at all), we have to surround them in parentheses. Also notice the error function that we used. It takes a string and generates a runtime error, using that string as information about what kind of error occurred. It causes the program to crash, so it’s not good to use it too much. But calling head on an empty list doesn’t make sense. @@ -141,16 +141,16 @@

Syntax in Functions

This function is safe because it takes care of the empty list, a singleton list, a list with two elements and a list with more than two elements. Note that (x:[]) and (x:y:[]) could be rewritten as [x] and [x,y] (because its syntactic sugar, we don’t need the parentheses). We can’t rewrite (x:y:_) with square brackets because it matches any list of length 2 or more.

We already implemented our own length function using list comprehension. Now we’ll do it by using pattern matching and a little recursion:

-length' :: (Num b) => [a] -> b
-length' [] = 0
-length' (_:xs) = 1 + length' xs
+length' :: (Num b) => [a] -> b +length' [] = 0 +length' (_:xs) = 1 + length' xs

This is similar to the factorial function we wrote earlier. First we defined the result of a known input — the empty list. This is also known as the edge condition. Then in the second pattern we take the list apart by splitting it into a head and a tail. We say that the length is equal to 1 plus the length of the tail. We use _ to match the head because we don’t actually care what it is. Also note that we’ve taken care of all possible patterns of a list. The first pattern matches an empty list and the second one matches anything that isn’t an empty list.

Let’s see what happens if we call length' on "ham". First, it will check if it’s an empty list. Because it isn’t, it falls through to the second pattern. It matches on the second pattern and there it says that the length is 1 + length' "am", because we broke it into a head and a tail and discarded the head. O-kay. The length' of "am" is, similarly, 1 + length' "m". So right now we have 1 + (1 + length' "m"). length' "m" is 1 + length' "" (could also be written as 1 + length' []). And we’ve defined length' [] to be 0. So in the end we have 1 + (1 + (1 + 0)).

Let’s implement sum. We know that the sum of an empty list is 0. We write that down as a pattern. And we also know that the sum of a list is the head plus the sum of the rest of the list. So if we write that down, we get:

-sum' :: (Num a) => [a] -> a
-sum' [] = 0
-sum' (x:xs) = x + sum' xs
+sum' :: (Num a) => [a] -> a
+sum' [] = 0
+sum' (x:xs) = x + sum' xs
 

There’s also a thing called as patterns. Those are a handy way of breaking something up according to a pattern and binding it to names whilst still keeping a reference to the whole thing. You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again. Here’s a quick and dirty example:

@@ -173,9 +173,9 @@ 

Syntax in Functions

 densityTell :: (RealFloat a) => a -> String
 densityTell density
-    | density < 1.2 = "Wow! You're going for a ride in the sky!"
+    | density < 1.2 = "Wow! You're going for a ride in the sky!"
     | density <= 1000.0 = "Have fun swimming, but watch out for sharks!"
-    | otherwise   = "If it's sink or swim, you're going to sink."
+    | otherwise   = "If it's sink or swim, you're going to sink."
 

Guards are indicated by pipes that follow a function’s name and its parameters. Usually, they’re indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that’s smaller than or equal to 1.2. Because it isn’t, it falls through to the next guard. The check is carried out with the second guard and because 24.3 is less than 1000.0, the second string is returned.

This is very reminiscent of a big if else tree in imperative languages, only this is far better and more readable. While big if else trees are usually frowned upon, sometimes a problem is defined in such a discrete way that you can’t get around them. Guards are a very nice alternative for this.

@@ -184,9 +184,9 @@

Syntax in Functions

 densityTell :: (RealFloat a) => a -> a -> String
 densityTell mass volume
-    | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
+    | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
     | mass / volume <= 1000.0 = "Have fun swimming, but watch out for sharks!"
-    | otherwise   = "If it's sink or swim, you're going to sink."
+    | otherwise   = "If it's sink or swim, you're going to sink."
 

Let’s see if cat food will float …

@@ -197,15 +197,15 @@ 

Syntax in Functions

Note that there’s no = right after the function name and its parameters, before the first guard. Many newbies get syntax errors because they sometimes put it there.

Another very simple example: let’s implement our own max function. If you remember, it takes two things that can be compared and returns the larger of them.

-max' :: (Ord a) => a -> a -> a
-max' a b
+max' :: (Ord a) => a -> a -> a
+max' a b
     | a > b     = a
     | otherwise = b
 

Guards can also be written inline, although I’d advise against that because it’s less readable, even for very short functions. But to demonstrate, we could write max' like this:

-max' :: (Ord a) => a -> a -> a
-max' a b | a > b = a | otherwise = b
+max' :: (Ord a) => a -> a -> a
+max' a b | a > b = a | otherwise = b
 

Ugh! Not very readable at all! Moving on: let’s implement our own compare by using guards.

@@ -226,26 +226,26 @@

Syntax in Functions

 densityTell :: (RealFloat a) => a -> a -> String
 densityTell mass volume
-    | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
+    | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
     | mass / volume <= 1000.0 = "Have fun swimming, but watch out for sharks!"
-    | otherwise   = "If it's sink or swim, you're going to sink."
+    | otherwise   = "If it's sink or swim, you're going to sink."
 

Notice that we repeat ourselves here two times. We repeat ourselves two times. Repeating yourself (two times) while programming is about as desirable as getting kicked inna head. Since we repeat the same expression twice, it would be ideal if we could calculate it once, bind it to a name and then use that name instead of the expression. Well, we can modify our function like this:

 densityTell :: (RealFloat a) => a -> a -> String
 densityTell mass volume
-    | density < 1.2 = "Wow! You're going for a ride in the sky!"
+    | density < 1.2 = "Wow! You're going for a ride in the sky!"
     | density <= 1000.0 = "Have fun swimming, but watch out for sharks!"
-    | otherwise   = "If it's sink or swim, you're going to sink."
+    | otherwise   = "If it's sink or swim, you're going to sink."
     where density = mass / volume
 

We put the keyword where after the guards (usually it’s best to indent it as much as the pipes are indented) and then we define several names or functions. These names are visible across the guards and give us the advantage of not having to repeat ourselves. If we decide that we want to calculate density a bit differently, we only have to change it once. It also improves readability by giving names to things and can make our programs faster since stuff like our density variable here is calculated only once. We could go a bit overboard and present our function like this:

 densityTell :: (RealFloat a) => a -> a -> String
 densityTell mass volume
-    | density < air = "Wow! You're going for a ride in the sky!"
+    | density < air = "Wow! You're going for a ride in the sky!"
     | density <= water = "Have fun swimming, but watch out for sharks!"
-    | otherwise   = "If it's sink or swim, you're going to sink."
+    | otherwise   = "If it's sink or swim, you're going to sink."
     where density = mass / volume
           air = 1.2
           water = 1000.0
@@ -289,7 +289,7 @@ 

Syntax in Functions

The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs. Remember when we did the if statement and it was explained that an if else statement is an expression and you can cram it in almost anywhere?

-ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
+ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
 ["Woo", "Bar"]
 ghci> 4 * (if 10 > 5 then 10 else 0) + 2
 42
@@ -333,7 +333,7 @@ 

Syntax in Functions

ghci> let boot x y z = x * y + z in boot 3 4 2 14 ghci> boot -<interactive>:1:0: Not in scope: `boot' +<interactive>:1:0: Not in scope: `boot'

If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

Case expressions

@@ -341,13 +341,13 @@

Syntax in Functions

Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for specific values of that variable and then maybe including a catch-all block of code in case the variable has some value for which we didn’t set up a case.

Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

-head' :: [a] -> a
-head' [] = error "No head for empty lists!"
-head' (x:_) = x
+head' :: [a] -> a
+head' [] = error "No head for empty lists!"
+head' (x:_) = x
 
-head' :: [a] -> a
-head' xs = case xs of [] -> error "No head for empty lists!"
+head' :: [a] -> a
+head' xs = case xs of [] -> error "No head for empty lists!"
                       (x:_) -> x
 

As you can see, the syntax for case expressions is pretty simple:

diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 80fdb75..ce62f98 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -39,14 +39,14 @@

Types and Typeclasses

A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

Now we’ll use GHCI to examine the types of some expressions. We’ll do that by using the :t command which, followed by any valid expression, tells us its type. Let’s give it a whirl.

-ghci> :t 'a'
-'a' :: Char
+ghci> :t 'a'
+'a' :: Char
 ghci> :t True
 True :: Bool
 ghci> :t "HELLO!"
 "HELLO!" :: [Char]
-ghci> :t (True, 'a')
-(True, 'a') :: (Bool, Char)
+ghci> :t (True, 'a')
+(True, 'a') :: (Bool, Char)
 ghci> :t 4 == 5
 4 == 5 :: Bool
 
@@ -59,7 +59,7 @@

Types and Typeclasses

 removeNonUppercase :: [Char] -> [Char]
-removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
+removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
 

removeNonUppercase has a type of [Char] -> [Char], meaning that it maps from a string to a string. That’s because it takes one string as a parameter and returns another as a result. The [Char] type is synonymous with String so it’s clearer if we write removeNonUppercase :: String -> String. We didn’t have to give this function a type declaration because the compiler can infer by itself that it’s a function from a string to a string but we did anyway. But how do we write out the type of a function that takes several parameters? Here’s a simple function that takes three integers and adds them together: @@ -96,11 +96,11 @@

Types and Typeclasses

Double is a real floating point with double the precision!

-circumference' :: Double -> Double
-circumference' r = 2 * pi * r
+circumference' :: Double -> Double
+circumference' r = 2 * pi * r
 
-ghci> circumference' 4.0
+ghci> circumference' 4.0
 25.132741228718345
 

@@ -151,7 +151,7 @@

Types and Typeclasses

True ghci> 5 /= 5 False -ghci> 'a' == 'a' +ghci> 'a' == 'a' True ghci> "Ho Ho" == "Ho Ho" True @@ -201,8 +201,8 @@

Types and Typeclasses

 ghci> read "4"
 <interactive>:1:0:
-    Ambiguous type variable `a' in the constraint:
-      `Read a' arising from a use of `read' at <interactive>:1:0-7
+    Ambiguous type variable `a' in the constraint:
+      `Read a' arising from a use of `read' at <interactive>:1:0-7
     Probable fix: add a type signature that fixes these type variable(s)
 

What GHCI is telling us here is that it doesn’t know what we want in return. Notice that in the previous uses of read we did something with the result afterwards. That way, GHCI could infer what kind of result we wanted out of our read. If we used it as a boolean, it knew it had to return a Bool. But now, it knows we want some type that is part of the Read class, it just doesn’t know which one. Let’s take a look at the type signature of read.

@@ -222,27 +222,27 @@

Types and Typeclasses

20.0 ghci> read "[1,2,3,4]" :: [Int] [1,2,3,4] -ghci> read "(3, 'a')" :: (Int, Char) -(3, 'a') +ghci> read "(3, 'a')" :: (Int, Char) +(3, 'a')

Most expressions are such that the compiler can infer what their type is by itself. But sometimes, the compiler doesn’t know whether to return a value of type Int or Float for an expression like read "5". To see what the type is, Haskell would have to actually evaluate read "5". But since Haskell is a statically typed language, it has to know all the types before the code is compiled (or in the case of GHCI, evaluated). So we have to tell Haskell: "Hey, this expression should have this type, in case you don’t know!".

Enum members are sequentially ordered types — they can be enumerated. The main advantage of the Enum typeclass is that we can use its types in list ranges. They also have defined successors and predecessors, which you can get with the succ and pred functions. Types in this class: (), Bool, Char, Ordering, Int, Integer, Float and Double.

-ghci> ['a'..'e']
+ghci> ['a'..'e']
 "abcde"
 ghci> [LT .. GT]
 [LT,EQ,GT]
 ghci> [3 .. 5]
 [3,4,5]
-ghci> succ 'B'
-'C'
+ghci> succ 'B'
+'C'
 

Bounded members have an upper and a lower bound.

 ghci> minBound :: Int
 -2147483648
 ghci> maxBound :: Char
-'\1114111'
+'\1114111'
 ghci> maxBound :: Bool
 True
 ghci> minBound :: Bool
@@ -252,7 +252,7 @@ 

Types and Typeclasses

All tuples are also part of Bounded if the components are also in it.

 ghci> maxBound :: (Bool, Int, Char)
-(True,2147483647,'\1114111')
+(True,2147483647,'\1114111')
 

Num is a numeric typeclass. Its members have the property of being able to act like numbers. Let’s examine the type of a number.

diff --git a/docs/zippers.html b/docs/zippers.html
index ce975aa..7ceb08d 100644
--- a/docs/zippers.html
+++ b/docs/zippers.html
@@ -88,25 +88,25 @@ 

Taking a walk

 freeTree :: Tree Char
 freeTree =
-    Node 'P'
-        (Node 'O'
-            (Node 'L'
-                (Node 'N' Empty Empty)
-                (Node 'T' Empty Empty)
+    Node 'P'
+        (Node 'O'
+            (Node 'L'
+                (Node 'N' Empty Empty)
+                (Node 'T' Empty Empty)
             )
-            (Node 'Y'
-                (Node 'S' Empty Empty)
-                (Node 'A' Empty Empty)
+            (Node 'Y'
+                (Node 'S' Empty Empty)
+                (Node 'A' Empty Empty)
             )
         )
-        (Node 'L'
-            (Node 'W'
-                (Node 'C' Empty Empty)
-                (Node 'R' Empty Empty)
+        (Node 'L'
+            (Node 'W'
+                (Node 'C' Empty Empty)
+                (Node 'R' Empty Empty)
             )
-            (Node 'A'
-                (Node 'A' Empty Empty)
-                (Node 'C' Empty Empty)
+            (Node 'A'
+                (Node 'A' Empty Empty)
+                (Node 'C' Empty Empty)
             )
         )
 
@@ -127,7 +127,7 @@

Taking a walk

 changeToP :: Tree Char -> Tree Char
-changeToP (Node x l (Node y (Node _ m n) r)) = Node x l (Node y (Node 'P' m n) r)
+changeToP (Node x l (Node y (Node _ m n) r)) = Node x l (Node y (Node 'P' m n) r)
 

@@ -157,7 +157,7 @@

Taking a walk

changeToP :: Directions-> Tree Char -> Tree Char changeToP (L:ds) (Node x l r) = Node x (changeToP ds l) r changeToP (R:ds) (Node x l r) = Node x l (changeToP ds r) -changeToP [] (Node _ l r) = Node 'P' l r +changeToP [] (Node _ l r) = Node 'P' l r

@@ -195,7 +195,7 @@

Taking a walk

 ghci> let newTree = changeToP [R,L] freeTree
 ghci> elemAt [R,L] newTree
-'P'
+'P'
 

@@ -277,7 +277,7 @@

A trail of breadcrumbs

 ghci> goLeft (goRight (freeTree, []))
-(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
+(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
 
almostthere @@ -309,7 +309,7 @@

A trail of breadcrumbs

 ghci> (freeTree, []) -: goRight -: goLeft
-(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
+(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
 

Going back up

@@ -490,7 +490,7 @@

Manipulating trees under focus

-ghci> let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))
+ghci> let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))
 

@@ -500,7 +500,7 @@

Manipulating trees under focus

-ghci> let newFocus = (freeTree,[]) -: goLeft -: goRight -: modify (\_ -> 'P')
+ghci> let newFocus = (freeTree,[]) -: goLeft -: goRight -: modify (\_ -> 'P')
 

@@ -509,7 +509,7 @@

Manipulating trees under focus

-ghci> let newFocus2 = modify (\_ -> 'X') (goUp newFocus)
+ghci> let newFocus2 = modify (\_ -> 'X') (goUp newFocus)
 

@@ -517,7 +517,7 @@

Manipulating trees under focus

-ghci> let newFocus2 = newFocus -: goUp -: modify (\_ -> 'X')
+ghci> let newFocus2 = newFocus -: goUp -: modify (\_ -> 'X')
 

@@ -549,7 +549,7 @@

Manipulating trees under focus

 ghci> let farLeft = (freeTree,[]) -: goLeft -: goLeft -: goLeft -: goLeft
-ghci> let newFocus = farLeft -: attach (Node 'Z' Empty Empty)
+ghci> let newFocus = farLeft -: attach (Node 'Z' Empty Empty)
 

@@ -1067,8 +1067,8 @@

Watch your step

 ghci> goLeft (Empty, [])
 Nothing
-ghci> goLeft (Node 'A' Empty Empty, [])
-Just (Empty,[LeftCrumb 'A' Empty])
+ghci> goLeft (Node 'A' Empty Empty, [])
+Just (Empty,[LeftCrumb 'A' Empty])
 

From 5e065a38b209be8f241ccfcd1dd0ac4eaed355ea Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Wed, 23 Nov 2022 14:45:38 +0900 Subject: [PATCH 08/27] Replace double quotes with " in code blocks --- docs/a-fistful-of-monads.html | 66 ++--- docs/for-a-few-monads-more.html | 152 +++++----- docs/functionally-solving-problems.html | 46 +-- ...tors-applicative-functors-and-monoids.html | 156 +++++----- docs/higher-order-functions.html | 26 +- docs/input-and-output.html | 272 ++++++++--------- .../making-our-own-types-and-typeclasses.html | 168 +++++------ docs/modules.html | 280 +++++++++--------- docs/recursion.html | 8 +- docs/starting-out.html | 68 ++--- docs/syntax-in-functions.html | 108 +++---- docs/types-and-typeclasses.html | 38 +-- docs/zippers.html | 42 +-- 13 files changed, 715 insertions(+), 715 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 22a6b67..93039bf 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -118,7 +118,7 @@

A Fistful of Monads

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

Getting our feet wet with Maybe

-ghci> fmap (++"!") (Just "wisdom")
-Just "wisdom!"
-ghci> fmap (++"!") Nothing
+ghci> fmap (++"!") (Just "wisdom")
+Just "wisdom!"
+ghci> fmap (++"!") Nothing
 Nothing
 
@@ -232,7 +232,7 @@

Getting our feet wet with Maybe

 ghci> Just (+3) <*> Just 3
 Just 6
-ghci> Nothing <*> Just "greed"
+ghci> Nothing <*> Just "greed"
 Nothing
 ghci> Just ord <*> Nothing
 Nothing
@@ -318,11 +318,11 @@ 

Getting our feet wet with Maybe

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

The Monad type class

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

do notation

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

@@ -1006,8 +1006,8 @@

do notation

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

@@ -1020,8 +1020,8 @@

do notation

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

@@ -1031,11 +1031,11 @@

do notation

-ghci> Nothing >>= (\x -> Just "!" >>= (\y -> Just (show x ++ y)))
+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))
+ghci> Just 3 >>= (\x -> Just "!" >>= (\y -> Nothing))
 Nothing
 
@@ -1059,7 +1059,7 @@

do notation

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

do notation

foo :: Maybe String foo = do x <- Just 3 - y <- Just "!" + y <- Just "!" Just (show x ++ y)
@@ -1254,7 +1254,7 @@

do notation

 justH :: Maybe Char
 justH = do
-    (x:xs) <- Just "hello"
+    (x:xs) <- Just "hello"
     return x
 
@@ -1307,7 +1307,7 @@

do notation

 wopwop :: Maybe Char
 wopwop = do
-    (x:xs) <- Just ""
+    (x:xs) <- Just ""
     return x
 
@@ -1439,7 +1439,7 @@

The list monad

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

The list monad

-ghci> guard (5 > 2) >> return "cool" :: [String]
-["cool"]
-ghci> guard (1 > 2) >> return "cool" :: [String]
+ghci> guard (5 > 2) >> return "cool" :: [String]
+["cool"]
+ghci> guard (1 > 2) >> return "cool" :: [String]
 []
 
@@ -1912,10 +1912,10 @@

Left identity

-ghci> return "WoM" >>= (\x -> [x,x,x])
-["WoM","WoM","WoM"]
-ghci> (\x -> [x,x,x]) "WoM"
-["WoM","WoM","WoM"]
+ghci> return "WoM" >>= (\x -> [x,x,x])
+["WoM","WoM","WoM"]
+ghci> (\x -> [x,x,x]) "WoM"
+["WoM","WoM","WoM"]
 

@@ -1951,11 +1951,11 @@

Right identity

-ghci> Just "move on up" >>= (\x -> return x)
-Just "move on up"
+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)
+ghci> putStrLn "Wah!" >>= (\x -> return x)
 Wah!
 
diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index 1233ec1..28fad6a 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -107,7 +107,7 @@

Writer? I hardly know her!

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

@@ -119,9 +119,9 @@

Writer? I hardly know her!

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

Writer? I hardly know her!

-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")
+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")
 

@@ -205,10 +205,10 @@

Writer? I hardly know her!

-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")
+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")
 

@@ -256,7 +256,7 @@

Monoids to the rescue

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) +Chunk "chi" (Chunk "huahua" Empty)

@@ -286,9 +286,9 @@

Monoids to the rescue

type Price = Sum Int addDrink :: Food -> (Food,Price) -addDrink "beans" = ("milk", Sum 25) -addDrink "jerky" = ("whiskey", Sum 99) -addDrink _ = ("beer", Sum 30) +addDrink "beans" = ("milk", Sum 25) +addDrink "jerky" = ("whiskey", Sum 99) +addDrink _ = ("beer", Sum 30)

@@ -316,12 +316,12 @@

Monoids to the rescue

-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})
+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})
 

@@ -341,8 +341,8 @@

Monoids to the rescue

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

@@ -431,7 +431,7 @@

The Writer type

 ghci> runWriter (return 3 :: Writer String Int)
-(3,"")
+(3,"")
 ghci> runWriter (return 3 :: Writer (Sum Int) Int)
 (3,Sum {getSum = 0})
 ghci> runWriter (return 3 :: Writer (Product Int) Int)
@@ -473,7 +473,7 @@ 

Using do notation with Writer

import Control.Monad.Writer logNumber :: Int -> Writer [String] Int -logNumber x = Writer (x, ["Got number: " ++ show x]) +logNumber x = Writer (x, ["Got number: " ++ show x]) multWithLog :: Writer [String] Int multWithLog = do @@ -498,7 +498,7 @@

Using do notation with Writer

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

@@ -520,7 +520,7 @@

Using do notation with Writer

multWithLog = do a <- logNumber 3 b <- logNumber 5 - tell ["Gonna multiply these two"] + tell ["Gonna multiply these two"] return (a*b)
@@ -536,7 +536,7 @@

Using do notation with Writer

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

Adding logging to programs

@@ -598,10 +598,10 @@

Adding logging to programs

gcd' :: Int -> Int -> Writer [String] Int gcd' a b | b == 0 = do - tell ["Finished with " ++ show a] + tell ["Finished with " ++ show a] return a | otherwise = do - tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)] + tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)] gcd' b (a `mod` b)
@@ -619,7 +619,7 @@

Adding logging to programs

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

@@ -725,11 +725,11 @@

Inefficient list construction

gcdReverse :: Int -> Int -> Writer [String] Int gcdReverse a b | b == 0 = do - tell ["Finished with " ++ show a] + 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)] + tell [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)] return result
@@ -793,7 +793,7 @@

Difference lists

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

@@ -865,11 +865,11 @@

Difference lists

gcd' :: Int -> Int -> Writer (DiffList String) Int gcd' a b | b == 0 = do - tell (toDiffList ["Finished with " ++ show a]) + 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)]) + tell (toDiffList [show a ++ " mod " ++ show b ++ " = " ++ show (a `mod` b)]) return result
@@ -909,7 +909,7 @@

Comparing Performance

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

Comparing Performance

 finalCountDown :: Int -> Writer [String] ()
 finalCountDown 0 = do
-    tell ["0"]
+    tell ["0"]
 finalCountDown x = do
     finalCountDown (x-1)
     tell [show x]
@@ -1672,8 +1672,8 @@ 

Error error on the wall

 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
+ghci> :t Left "out of cheese error"
+Left "out of cheese error" :: Either [Char] b
 

@@ -1731,8 +1731,8 @@

Error error on the wall

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

@@ -1747,10 +1747,10 @@

Error error on the wall

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

@@ -1880,10 +1880,10 @@

liftM and friends

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> 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] @@ -2107,8 +2107,8 @@

The join function

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

@@ -2128,10 +2128,10 @@

The join function

 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"
+ghci> join (Right (Left "error")) :: Either String Int
+Left "error"
+ghci> join (Left "error") :: Either String Int
+Left "error"
 
@@ -2276,10 +2276,10 @@

filterM

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

Making a safe RPN calculator

 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 (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
 
@@ -2547,7 +2547,7 @@

Making a safe RPN calculator

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

Making a safe RPN calculator

-ghci> readMaybe "1" :: Maybe Int
+ghci> readMaybe "1" :: Maybe Int
 Just 1
-ghci> readMaybe "GO TO HELL" :: Maybe Int
+ghci> readMaybe "GO TO HELL" :: Maybe Int
 Nothing
 
@@ -2569,9 +2569,9 @@

Making a safe RPN calculator

 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 (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)
 
@@ -2590,15 +2590,15 @@

Making a safe RPN calculator

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

Making a safe RPN calculator

-ghci> solveRPN "1 2 * 4 +"
+ghci> solveRPN "1 2 * 4 +"
 Just 6.0
-ghci> solveRPN "1 2 * 4 + 5 *"
+ghci> solveRPN "1 2 * 4 + 5 *"
 Just 30.0
-ghci> solveRPN "1 2 * 4"
+ghci> solveRPN "1 2 * 4"
 Nothing
-ghci> solveRPN "1 8 wharglbllargh"
+ghci> solveRPN "1 8 wharglbllargh"
 Nothing
 
diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index b1161bc..1e2ebe9 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -68,9 +68,9 @@

Functionally Solving Problems

 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
+    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 on to the stack. If the item is not "*", the pattern matching will fall through and "+" will be checked, and so on.

@@ -79,17 +79,17 @@

Functionally Solving Problems

For the list of items ["2","3","+"], our function will start folding from the left. The intial 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 * -"
+ghci> solveRPN "10 4 3 + 2 * -"
 -4
-ghci> solveRPN "2 3 +"
+ghci> solveRPN "2 3 +"
 5
-ghci> solveRPN "90 34 12 33 55 66 + * - +"
+ghci> solveRPN "90 34 12 33 55 66 + * - +"
 -3947
-ghci> solveRPN "90 34 12 33 55 66 + * - + -"
+ghci> solveRPN "90 34 12 33 55 66 + * - + -"
 4037
-ghci> solveRPN "90 34 12 33 55 66 + * - + -"
+ghci> solveRPN "90 34 12 33 55 66 + * - + -"
 4037
-ghci> solveRPN "90 3 -"
+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.

@@ -99,29 +99,29 @@

Functionally Solving Problems

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] + 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"
+ghci> solveRPN "2.7 ln"
 0.9932518
-ghci> solveRPN "10 10 10 10 sum 4 /"
+ghci> solveRPN "10 10 10 10 sum 4 /"
 10.0
-ghci> solveRPN "10 10 10 10 10 sum 4 /"
+ghci> solveRPN "10 10 10 10 10 sum 4 /"
 12.5
-ghci> solveRPN "10 2 ^"
+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 ^"
+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.

@@ -264,8 +264,8 @@

Functionally Solving Problems

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

diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index e968007..98301a8 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -56,14 +56,14 @@

Functors, Applicative Functors and Monoids

 main = do line <- getLine
           let line' = reverse line
-          putStrLn $ "You said " ++ line' ++ " backwards!"
-          putStrLn $ "Yes, you really said" ++ line' ++ " backwards!"
+          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!"
+          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.

@@ -112,7 +112,7 @@

Functors, Applicative Functors and Monoids

ghci> (*3) . (+100) $ 1 303 ghci> fmap (show . (*3)) (*100) 1 -"300" +"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.

@@ -136,12 +136,12 @@

Functors, Applicative Functors and Monoids

[[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) (Right "blah") +Right ["blah","blah","blah"] ghci> fmap (replicate 3) Nothing Nothing -ghci> fmap (replicate 3) (Left "foo") -Left "foo" +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.

@@ -181,12 +181,12 @@

Functors, Applicative Functors and Monoids

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

Functors, Applicative Functors and Monoids

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

@@ -223,12 +223,12 @@

Functors, Applicative Functors and Monoids

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 (++) (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 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]
 
@@ -271,9 +271,9 @@

Functors, Applicative Functors and Monoids

Just 13 ghci> pure (+3) <*> Just 9 Just 12 -ghci> Just (++"hahah") <*> Nothing +ghci> Just (++"hahah") <*> Nothing Nothing -ghci> Nothing <*> Just "woot" +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.

@@ -298,13 +298,13 @@

Functors, Applicative Functors and Monoids

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"
+ghci> (++) <$> Just "johntra" <*> Just "volta"
+Just "johntravolta"
 

Before we see how this happens, compare the above line with this:

-ghci> (++) "johntra" "volta"
-"johntravolta"
+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.

@@ -317,10 +317,10 @@

Functors, Applicative Functors and Monoids

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

@@ -335,8 +335,8 @@ 

Functors, Applicative Functors and Monoids

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."]
+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.

@@ -388,7 +388,7 @@

Functors, Applicative Functors and Monoids

 main = do
     a <- (++) <$> getLine <*> getLine
-    putStrLn $ "The two lines concatenated turn out to be: " ++ a
+    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.

@@ -400,12 +400,12 @@

Functors, Applicative Functors and Monoids

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"
+ghci> (pure 3) "blah"
 3
 

Because of currying, function application is left-associative, so we can omit the parentheses.

-ghci> pure 3 "blah"
+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.

@@ -443,7 +443,7 @@

Functors, Applicative Functors and Monoids

[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" +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).
@@ -543,7 +543,7 @@

Functors, Applicative Functors and Monoids

heyh ho woo -["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

+

The newtype keyword

why_ so serious? @@ -1062,7 +1062,7 @@

type vs. newtype vs. keyword.

-

Monoids

+

Monoids

wow this is pretty much the gayest pirate ship
 ever diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index 584bd36..7871e53 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -34,7 +34,7 @@

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

+

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
@@ -92,7 +92,7 @@ 

Higher Order Functions

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

+

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
@@ -157,7 +157,7 @@ 

Higher Order Functions

ghci> zipWith (flip' div) [2,2..] [10,8,6,4,2] [5,4,3,2,1]
-

Maps and filters

+

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]
@@ -264,7 +264,7 @@ 

Higher Order Functions

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

+

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:

@@ -301,7 +301,7 @@

Higher Order Functions

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

+

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.

@@ -390,7 +390,7 @@

Higher Order Functions

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 $

+

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
@@ -405,7 +405,7 @@ 

Higher Order Functions

ghci> map ($ 3) [(4+), (10*), (^2), sqrt] [7.0,30.0,9.0,1.7320508075688772]
-

Function composition

+

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:

diff --git a/docs/input-and-output.html b/docs/input-and-output.html
index 8d22c55..8878e2f 100644
--- a/docs/input-and-output.html
+++ b/docs/input-and-output.html
@@ -36,7 +36,7 @@ 

Input and Output

We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

While functions being unable to change state is good because it helps us reason about our programs, there’s one problem with that. If a function can’t change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

Do not despair, all is not lost. It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen. With those two parts separated, we can still reason about our pure program and take advantage of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

-

Hello, world!

+

Hello, world!

HELLO!

Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.
@@ -351,7 +351,7 @@

Input and Output

We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

-

Files and streams

+

Files and streams

streams

getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

@@ -705,7 +705,7 @@

Input and Output

$ cat todo.txt Take salad out of the oven
-

Command line arguments

+

Command line arguments

COMMAND LINE ARGUMENTS!!! ARGH

Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line arguments of a program.

In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

@@ -870,7 +870,7 @@

Input and Output

Another cool thing about this is that it’s easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you’re laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

You could make this program fail a bit more gracefully in case of bad input (for example, if someone runs todo UP YOURS HAHAHAHA) by making an I/O action that just reports there has been an error (say, errorExit :: IO ()) and then check for possible erroneous input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

-

Randomness

+

Randomness

this picture is the ultimate source of randomness and wackiness

Many times while programming, you need to get some random data. Maybe you’re making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of uses for random data when programming. Well, actually, pseudo-random, because we all know that the only true source of randomness is a monkey on a unicycle with a cheese in one hand and its butt in the other. In this section, we’ll take a look at how to make Haskell generate seemingly random data.

In most other programming languages, you have functions that give you back some random number. Each time you call that function, you get back a (hopefully) different random number. How about Haskell? Well, remember, Haskell is a pure functional language. What that means is that it has referential transparency. What THAT means is that a function, if given the same parameters twice, must produce the same result twice. That’s really cool because it allows us to reason differently about programs and it enables us to defer evaluation until we really need it. If I call a function, I can be sure that it won’t do any funny stuff before giving me the results. All that matters are its results. However, this makes it a bit tricky for getting random numbers. If I have a function like this:

@@ -1089,7 +1089,7 @@

Input and Output

main

It’s very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

-

Bytestrings

+

Bytestrings

like normal string, only they byte … what a pedestrian pun this is

Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

@@ -1154,7 +1154,7 @@

Input and Output

Notice that a program that doesn’t use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that’s not hard.

Whenever you need better performance in a program that reads a lot of data into strings, give bytestrings a try, chances are you’ll get some good performance boosts with very little effort on your part. I usually write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

-

Exceptions

+

Exceptions

timberr!!!!

All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn’t be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we’ve defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we’re not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell’s type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

diff --git a/docs/introduction.html b/docs/introduction.html index 93c9d62..b570994 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -30,8 +30,8 @@

Introduction

- -

About this tutorial

+ +

About this tutorial

Welcome to Learn You a Haskell for Great Good! If you’re reading this, chances are you want to learn Haskell. Well, you’ve come to the right place, but let’s talk about this tutorial a bit first. @@ -50,7 +50,7 @@

About this tutorial

I failed to learn Haskell approximately 2 times before finally grasping it because it all just seemed too weird to me and I didn’t get it. But then once it just “clicked” and after getting over that initial hurdle, it was pretty much smooth sailing. I guess what I’m trying to say is: Haskell is great and if you’re interested in programming you should really learn it even if it seems weird at first. Learning Haskell is much like learning to program for the first time — it’s fun! It forces you to think differently, which brings us to the next section …

-

So what’s Haskell?

+

So what’s Haskell?

fx Haskell is a purely functional programming language. @@ -70,7 +70,7 @@

About this tutorial

Haskell was made by some really smart folk (with PhDs). Work on Haskell began in 1987 when a committee of researchers got together to design a kick-ass language. In 2003 the Haskell Report was published, which defines a stable version of the language.

-

What you need to dive in

+

What you need to dive in

A text editor and a Haskell compiler. You probably already have your favorite text editor installed so we won’t waste time on that. For the purposes of this tutorial we’ll be using GHC, the most widely used Haskell compiler. The best way to get started is to download GHCup, which is the recommended Haskell installer.

diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index 10eb869..7cd9551 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -33,7 +33,7 @@

Making Our Own Types and Typeclasses

In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we’ll learn how to make our own and how to put them to work!

-

Algebraic data types intro

+

Algebraic data types intro

So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

 data Bool = False | True
@@ -144,7 +144,7 @@ 

Making Our Own Types and Typeclasses

By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It’s the same as writing Shape (Rectangle, Circle).

We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can’t create a map by doing Map.Map [(1,2),(3,4)] because it doesn’t export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

Not exporting the value constructors of a data types makes them more abstract in such a way that we hide their implementation. Also, whoever uses our module can’t pattern match against the value constructors.

-

Record syntax

+

Record syntax

record

OK, we’ve been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I don’t know about you, but that’s all I ever want to know about a person. Let’s give it a go!

@@ -221,7 +221,7 @@ 

Making Our Own Types and Typeclasses

When making a new car, we don’t have to necessarily put the fields in the proper order, as long as we list all of them. But if we don’t use record syntax, we have to specify them in order.

Use record syntax when a constructor has several fields and it’s not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it’s pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn’t so obvious and we greatly benefited from using record syntax.

-

Type parameters

+

Type parameters

A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

 data Maybe a = Nothing | Just a
@@ -323,7 +323,7 @@ 

Making Our Own Types and Typeclasses

ghci> Vector 2 9 3 `vectMult` (Vector 4 9 5 `scalarMult` Vector 9 2 4) Vector 148 666 222
-

Derived instances

+

Derived instances

gob

In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

@@ -470,7 +470,7 @@

Making Our Own Types and Typeclasses

[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]

That’s pretty awesome.

-

Type synonyms

+

Type synonyms

Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char].

@@ -590,7 +590,7 @@

Making Our Own Types and Typeclasses

Right "QOTSA"

We could have used a Maybe a to represent the result but then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

-

Recursive data structures

+

Recursive data structures

the fonz

As we’ve seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have fields that are of the same type! Using that, we can create recursive data types, where one value of some type contains values of that type, which in turn contain more values of the same type and so on.

Think about this list: [5]. That’s just syntactic sugar for 5:[]. On the left side of the :, there’s a value and on the right side, there’s a list. And in this case, it’s an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

@@ -704,7 +704,7 @@

Making Our Own Types and Typeclasses

Checking for membership also works nicely. Cool.

So as you can see, algebraic data structures are a really cool and powerful concept in Haskell. We can use them to make anything from boolean values and weekday enumerations to binary search trees and more!

-

Typeclasses 102

+

Typeclasses 102

tweet

So far, we’ve learned about some of the standard Haskell typeclasses and we’ve seen which types are in them. We’ve also learned how to automatically make our own types instances of the standard typeclasses by asking Haskell to derive the instances for us. In this section, we’re going to learn how to make our own typeclasses and how to make types instances of them by hand.

A quick recap on typeclasses: typeclasses are like interfaces. A typeclass defines some behavior (like comparing for equality, comparing for ordering, enumeration) and then types that can behave in that way are made instances of that typeclass. The behavior of typeclasses is achieved by defining functions or just type declarations that we then implement. So when we say that a type is an instance of a typeclass, we mean that we can use the functions that the typeclass defines with that type.

@@ -798,7 +798,7 @@

Making Our Own Types and Typeclasses

When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

-

A yes-no typeclass

+

A yes-no typeclass

yesno

In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

@@ -883,7 +883,7 @@

Making Our Own Types and Typeclasses

ghci> yesnoIf Nothing "YEAH!" "NO!" "NO!" -

The Functor typeclass

+

The Functor typeclass

So far, we’ve encountered a lot of the typeclasses in the standard library. We’ve played with Ord, which is for stuff that can be ordered. We’ve palled around with Eq, which is for things that can be equated. We’ve seen Show, which presents an interface for types whose values can be displayed as strings. Our good friend Read is there whenever we need to convert a string to a value of some type. And now, we’re going to take a look at the Functor typeclass, which is basically for things that can be mapped over. You’re probably thinking about lists now, since mapping over lists is such a dominant idiom in Haskell. And you’re right, the list type is part of the Functor typeclass.

What better way to get to know the Functor typeclass than to see how it’s implemented? Let’s take a peek.

@@ -954,7 +954,7 @@ 

Making Our Own Types and Typeclasses

Try figuring out how Map k is made an instance of Functor by yourself!

With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.
-

Kinds and some type-foo

+

Kinds and some type-foo

TYPE FOO MASTER

Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

diff --git a/docs/modules.html b/docs/modules.html index e90fc97..8b3c46e 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -32,7 +32,7 @@

Modules

-

Loading modules

+

Loading modules

modules

A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There’s a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we’ve dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we’re going to examine a few useful modules and the functions that they have. But first, we’re going to see how to import modules.

@@ -72,7 +72,7 @@

Modules

Now, to reference Data.Map’s filter function, we just use M.filter.

Use this handy reference to see which modules are in the standard library. A great way to pick up new Haskell knowledge is to just click through the standard library reference and explore the modules and their functions. You can also view the Haskell source code for each module. Reading the source code of some modules is a really good way to learn Haskell and get a solid feel for it.

To search for functions or to find out where they’re located, use Hoogle. It’s a really awesome Haskell search engine, you can search by name, module name or even type signature.

-

Data.List

+

Data.List

The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We’ve already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don’t have to import Data.List via a qualified import because it doesn’t clash with any Prelude names except for those that Prelude already steals from Data.List. Let’s take a look at some of the functions that we haven’t met before.

intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here’s a demonstration:

@@ -408,7 +408,7 @@ 

Modules

[[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]

Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

-

Data.Char

+

Data.Char

lego char

The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here’s what they are:

@@ -528,7 +528,7 @@

Modules

ghci> decode 5 . encode 5 $ "This is a sentence" "This is a sentence"
-

Data.Map

+

Data.Map

Association lists (also called dictionaries) are lists that are used to store key-value pairs where ordering doesn’t matter. For instance, we might use an association list to store phone numbers, where phone numbers would be the values and people’s names would be the keys. We don’t care in which order they’re stored, we just want to get the right phone number for the right person.

The most obvious way to represent association lists in Haskell would be by having a list of pairs. The first component in the pair would be the key, the second component the value. Here’s an example of an association list with phone numbers:

@@ -708,7 +708,7 @@ 

Modules

fromList [(3,104),(5,103),(6,339)]

These were just a few functions from Data.Map. You can see a complete list of functions in the documentation.

-

Data.Set

+

Data.Set

legosets

The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

@@ -793,7 +793,7 @@

Modules

"HEY WATSCRKLIN"

setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

-

Making our own modules

+

Making our own modules

making modules

We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it’s good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

diff --git a/docs/recursion.html b/docs/recursion.html index 379286f..c1f384d 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -32,12 +32,12 @@

Recursion

-

Hello recursion!

+

Hello recursion!

SOVIET RUSSIA

We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

-

Maximum awesome

+

Maximum awesome

The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

Now let’s see how we’d define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it’s the maximum of the tail. That’s it! Now let’s implement that in Haskell.

@@ -61,7 +61,7 @@ 

Recursion

How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

max -

A few more recursive functions

+

A few more recursive functions

Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

 replicate' :: (Num i, Ord i) => i -> a -> [a]
@@ -111,7 +111,7 @@ 

Recursion

| otherwise = a `elem'` xs

Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

-

Quick, sort!

+

Quick, sort!

We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

quickman

So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

@@ -133,7 +133,7 @@

Recursion

Booyah! That’s what I’m talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

quicksort

An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They’re in green here. We chose the head because it’s easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

-

Thinking recursively

+

Thinking recursively

We did quite a bit of recursion so far and as you’ve probably noticed, there’s a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn’t matter if it’s a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera …

brain

Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn’t make sense. When dealing with lists, the edge case is most often the empty list. If you’re dealing with trees, the edge case is usually a node that doesn’t have any children.

diff --git a/docs/starting-out.html b/docs/starting-out.html index ef7d473..2dc129d 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -32,7 +32,7 @@

Starting Out

-

Ready, set, go!

+

Ready, set, go!

egg Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we’re going to load functions. The first thing we’re going to do is run ghc’s interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this. @@ -142,7 +142,7 @@

Starting Out

If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks. For instance, the div function takes two integers and does integral division between them. Doing div 92 10 results in a 9. But when we call it like that, there may be some confusion as to which number is doing the division and which one is being divided. So we can call it as an infix function by doing 92 `div` 10 and suddenly it’s much clearer.

Lots of people who come from imperative languages tend to stick to the notion that parentheses should denote function application. For example, in C, you use parentheses to call functions like foo(), bar(1) or baz(3, "haha"). Like we said, spaces are used for function application in Haskell. So those functions in Haskell would be foo, bar 1 and baz 3 "haha". So if you see something like bar (bar 3), it doesn’t mean that bar is called with bar and 3 as parameters. It means that we first call the function bar with 3 as the parameter to get some number and then we call bar again with that number. In C, that would be something like bar(bar(3)).

-

Baby’s first functions

+

Baby’s first functions

In the previous section we got a basic feel for calling functions. Now let’s try making our own! Open up your favorite text editor and punch in this function that takes a number and multiplies it by two.

@@ -205,7 +205,7 @@

Starting Out

There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably.

-

An intro to lists

+

An intro to lists

BUY A DOG Much like shopping lists in the real world, lists in Haskell are very useful. It’s the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this section we’ll look at the basics of lists, strings (which are lists) and list comprehensions. @@ -362,7 +362,7 @@

Starting Out

False

Those were a few basic functions that operate on lists. We’ll take a look at more list functions later.

-

Texas ranges

+

Texas ranges

draw What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that’s not a solution for gentlemen who demand excellence from their programming languages. Instead, we’ll use ranges. Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers can be enumerated. One, two, three, four, etc. Characters can also be enumerated. The alphabet is an enumeration of characters from A to Z. Names can’t be enumerated. What comes after “John”? I don’t know. @@ -408,7 +408,7 @@

Starting Out

[5,5,5,5,5,5,5,5,5,5]

Although it’s simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

-

I’m a list comprehension

+

I’m a list comprehension

frog If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate. @@ -475,7 +475,7 @@

Starting Out

[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]

You can write list comprehensions across several lines. So if you’re not in GHCI, it’s better to split longer list comprehensions across multiple lines, especially if they’re nested.

-

Tuples

+

Tuples

tuples

In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That’s its type and it doesn’t matter if it has only one number in it or an infinite amount of numbers. Tuples, however, are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.

Another key difference is that they don’t have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index a1203e5..dc5b4f3 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -32,7 +32,7 @@

Syntax in Functions

-

Pattern matching

+

Pattern matching

four!

This chapter will cover some of Haskell’s cool syntactic constructs and we’ll start with pattern matching. Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns.

When defining functions, you can define separate function bodies for different patterns. This leads to really neat code that’s simple and readable. You can pattern match on any data type — numbers, characters, lists, tuples, etc. Let’s make a really trivial function that checks if the number we supplied to it is a seven or not.

@@ -164,7 +164,7 @@

Syntax in Functions

Normally we use as patterns to avoid repeating ourselves when matching against a bigger pattern when we have to use the whole thing again in the function body.

One more thing — you can’t use ++ in pattern matches. If you tried to pattern match against (xs ++ ys), what would be in the first and what would be in the second list? It doesn’t make much sense. It would make sense to match stuff against (xs ++ [x,y,z]) or just (xs ++ [x]), but because of the nature of lists, you can’t do that.

-

Guards, guards!

+

Guards, guards!

guards

Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds a lot like an if statement and it’s very similar. The thing is that guards are a lot more readable when you have several conditions and they play really nicely with patterns.

Instead of explaining their syntax, let’s just dive in and make a function using guards. We’re going to make a simple function that responds differently depending on the density given. Density (or specific mass) is a substance’s mass per unit of volume (here, grams per liter). If a substance has a density of less than 1.2, it will float in air, as 1.2g/L is the density of air. If it has more than 1000g/L (the density of water), it will sink in water. Between are things (like people, usually) that will neither float away nor sink in water. @@ -221,7 +221,7 @@

Syntax in Functions

GT
Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.
-

Where!?

+

Where!?

In the previous section, we defined a density calculator function and responder like this:

 densityTell :: (RealFloat a) => a -> a -> String
@@ -274,7 +274,7 @@ 

Syntax in Functions

And that’s all there is to it! The reason we had to introduce density as a function in this example is because we can’t just calculate one density from the function’s parameters. We have to examine the list passed to the function and there’s a different density for every pair in there.

where bindings can also be nested. It’s a common idiom to make a function and define some helper function in its where clause and then to give those functions helper functions as well, each with its own where clause.

-

Let it be

+

Let it be

Very similar to where bindings are let bindings. Where bindings are a syntactic construct that let you bind to variables at the end of a function and the whole function can see them, including all the guards. Let bindings let you bind to variables anywhere and are expressions themselves, but are very local, so they don’t span across guards. Just like any construct in Haskell that is used to bind values to names, let bindings can be used for pattern matching. Let’s see them in action! This is how we could define a function that gives us a cylinder’s surface area based on its height and radius:

@@ -336,7 +336,7 @@

Syntax in Functions

<interactive>:1:0: Not in scope: `boot'

If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

-

Case expressions

+

Case expressions

case

Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for specific values of that variable and then maybe including a catch-all block of code in case the variable has some value for which we didn’t set up a case.

Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 542964a..242a786 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -32,7 +32,7 @@

Types and Typeclasses

-

Believe the type

+

Believe the type

moo

Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with some number, it won’t even compile. That’s good because it’s better to catch such errors at compile time instead of having your program crash. Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.

Unlike Java or Pascal, Haskell has type inference. If we write a number, we don’t have to tell Haskell it’s a number. It can infer that on its own, so we don’t have to explicitly write out the types of our functions and expressions to get things done. We covered some of the basics of Haskell with only a very superficial glance at types. However, understanding the type system is a very important part of learning Haskell.

@@ -110,7 +110,7 @@

Types and Typeclasses

Char represents a character. It’s denoted by single quotes. A list of characters is a string.

Tuples are types but they are dependent on their length as well as the types of their components, so there is theoretically an infinite number of tuple types, which is too many to cover in this tutorial. Note that the empty tuple () is also a type which can only have a single value: ()

-

Type variables

+

Type variables

What do you think is the type of the head function? Because head takes a list of any type and returns the first element, so what could it be? Let’s check!

@@ -131,7 +131,7 @@

Types and Typeclasses

We see that fst takes a tuple which contains two types and returns an element which is of the same type as the pair’s first component. That’s why we can use fst on a pair that contains any two types. Note that just because a and b are different type variables, they don’t have to be different types. It just states that the first component’s type and the return value’s type are the same.

-

Typeclasses 101

+

Typeclasses 101

class

A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages. Well, they’re not. You can think of them kind of as Java interfaces, only better.

diff --git a/docs/zippers.html b/docs/zippers.html index 636b92c..3bb841f 100644 --- a/docs/zippers.html +++ b/docs/zippers.html @@ -67,8 +67,8 @@

Zippers

efficient. Nice!

- -

Taking a walk

+ +

Taking a walk

Like we’ve learned in biology class, there are many different kinds of trees, so @@ -221,8 +221,8 @@

Taking a walk

allows us to efficiently switch focus to subtrees that are nearby.

- -

A trail of breadcrumbs

+ +

A trail of breadcrumbs

whoop dee doo @@ -584,8 +584,8 @@

I’m going straight to the top, oh yeah, up where the air is fresh and clea root of our tree and see the changes that we’ve done in proper perspective.

- -

Focusing on lists

+ +

Focusing on lists

Zippers can be used with pretty much any data structure, so it’s no surprise @@ -707,8 +707,8 @@

Focusing on lists

existing ones.

- -

A very simple file system

+ +

A very simple file system

Now that we know how zippers work, let’s use trees to represent a very simple @@ -1000,8 +1000,8 @@

Manipulating our file system

Haskell’s data structures really begins to shine.

- -

Watch your step

+ +

Watch your step

So far, while walking through our data structures, whether they were binary From 6b4fe391e2c61877c8d1d22b61d37eddfb4bd312 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 01:00:33 +0900 Subject: [PATCH 14/27] Add id attributes to heading tags that do not have them --- docs/a-fistful-of-monads.html | 10 +++--- docs/chapters.html | 2 +- docs/faq.html | 12 +++---- docs/for-a-few-monads-more.html | 34 +++++++++---------- docs/functionally-solving-problems.html | 2 +- ...tors-applicative-functors-and-monoids.html | 20 +++++------ docs/higher-order-functions.html | 2 +- docs/input-and-output.html | 2 +- docs/introduction.html | 2 +- .../making-our-own-types-and-typeclasses.html | 2 +- docs/modules.html | 2 +- docs/recursion.html | 2 +- docs/starting-out.html | 2 +- docs/syntax-in-functions.html | 2 +- docs/types-and-typeclasses.html | 2 +- docs/zippers.html | 12 +++---- 16 files changed, 55 insertions(+), 55 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index f6b2c40..a583c62 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -31,7 +31,7 @@ -

A Fistful of Monads

+

A Fistful of Monads

When we first talked about functors, we saw that they were a useful concept for @@ -1682,7 +1682,7 @@

The list monad

guard.

-

A knight’s quest

+

A knight’s quest

Here’s a problem that really lends itself to being solved with non-determinism. Say you have a chess board and only one knight piece on it. We want to find out @@ -1864,7 +1864,7 @@

Monad laws

the if the laws hold. But don’t worry, they’re not complicated.

-

Left identity

+

Left identity

The first monad law states that if we take a value, put it in a default @@ -1925,7 +1925,7 @@

Left identity

IO as well.

-

Right identity

+

Right identity

The second law states that if we have a monadic value and we use @@ -1983,7 +1983,7 @@

Right identity

value that it produced did a lot of other stuff.

-

Associativity

+

Associativity

The final monad law says that when we have a chain of monadic function diff --git a/docs/chapters.html b/docs/chapters.html index 7b676cb..1fa4d4a 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -16,7 +16,7 @@

-

Learn You a Haskell for Great Good!

+

Learn You a Haskell for Great Good!

  1. diff --git a/docs/faq.html b/docs/faq.html index 3c28ebf..3961cac 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -16,18 +16,18 @@
    -

    FAQ

    +

    FAQ

    turtle??? -

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

    +

    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?

    +

    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?

    +

    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!

    +

    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!

    +

    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 index 3a259a4..f0be83f 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -31,7 +31,7 @@
    -

    For a Few Monads More

    +

    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 @@ -217,7 +217,7 @@

    Writer? I hardly know her!

    appending the logs.

    -

    Monoids to the rescue

    +

    Monoids to the rescue

    Be sure you know what monoids are at this point! Cheers. @@ -353,7 +353,7 @@

    Monoids to the rescue

    ("beer", Sum 65).

    -

    The Writer type

    +

    The Writer type

    Now that we’ve seen that a value with an attached monoid acts like a monadic @@ -455,7 +455,7 @@

    The Writer type

    do notation, error is called.

    -

    Using do notation with Writer

    +

    Using do notation with Writer

    Now that we have a Monad instance, we’re free to use @@ -539,7 +539,7 @@

    Using do notation with Writer

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

    Adding logging to programs

    +

    Adding logging to programs

    Euclid’s algorithm is an algorithm that takes two numbers and computes their @@ -677,7 +677,7 @@

    Adding logging to programs

    do expressions if it increases readability).

    -

    Inefficient list construction

    +

    Inefficient list construction

    When using the Writer monad, you have to be careful @@ -754,7 +754,7 @@

    Inefficient list construction

    the left instead of to the right.

    -

    Difference lists

    +

    Difference lists

    cactuses @@ -897,7 +897,7 @@

    Difference lists

    finally print its entries to the screen.

    -

    Comparing Performance

    +

    Comparing Performance

    To get a feel for just how much difference lists may improve your performance, @@ -1223,7 +1223,7 @@

    Tasteful stateful computations

    from getting a result we also get a new state.

    -

    Stacks and stones

    +

    Stacks and stones

    Say we want to model operating a stack. You have a stack of things one on top @@ -1323,7 +1323,7 @@

    Stacks and stones

    the state manually.

    -

    The State monad

    +

    The State monad

    The Control.Monad.State module provides a @@ -1574,7 +1574,7 @@

    The State monad

    different monads.

    -

    Randomness and the state monad

    +

    Randomness and the state monad

    At the beginning of this section, we saw how generating numbers can sometimes be @@ -1819,7 +1819,7 @@

    Some useful monadic functions

    Let’s see what they are then!

    -

    liftM and friends

    +

    liftM and friends

    im a cop too @@ -2044,7 +2044,7 @@

    liftM and friends

    and applicative functors use.

    -

    The join function

    +

    The join function

    Here’s some food for thought: if the result of one monadic value is another @@ -2210,7 +2210,7 @@

    The join function

    out how to implement >>=.

    -

    filterM

    +

    filterM

    The filter function is pretty much the bread of @@ -2383,7 +2383,7 @@

    filterM

    to be everything at once, it’s a bit easier.

    -

    foldM

    +

    foldM

    The monadic counterpart to foldl is @@ -2466,7 +2466,7 @@

    foldM

    whatever you want as your fold goes along its way.

    -

    Making a safe RPN calculator

    +

    Making a safe RPN calculator

    i’ve found yellow! @@ -2656,7 +2656,7 @@

    Making a safe RPN calculator

    returns a Nothing.

    -

    Composing monadic functions

    +

    Composing monadic functions

    When we were learning about the monad laws, we said that the diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index a45c607..b67cec7 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -31,7 +31,7 @@

-

Functionally Solving Problems

+

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.

diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index 9577c71..f556a26 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -31,7 +31,7 @@
-

Functors, Applicative Functors and Monoids

+

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

@@ -735,7 +735,7 @@

The newtype keyword

to the other.

-

Using newtype to make type class instances

+

Using newtype to make type class instances

Many times, we want to make our types instances of certain type classes, but the @@ -837,7 +837,7 @@

Using newtype to make type class instances

("gnillac nodnol",3) -

On newtype laziness

+

On newtype laziness

We mentioned that newtype is usually faster than data. The @@ -967,7 +967,7 @@

On newtype laziness

direct conversion from one type to another.

-

type vs. newtype vs. data

+

type vs. newtype vs. data

At this point, you may be a bit confused about what exactly the difference @@ -1261,7 +1261,7 @@

Monoids

as the programmer have to be careful that our instances do indeed obey them.

-

Lists are monoids

+

Lists are monoids

Yes, lists are monoids! Like we’ve seen, the ++ @@ -1348,7 +1348,7 @@

Lists are monoids

multiplication, but it doesn’t hold for all (and indeed, most) monoids.

-

Product and Sum

+

Product and Sum

We already examined one way for numbers to be considered monoids. Just have the @@ -1447,7 +1447,7 @@

Product and Sum

6 -

Any and All

+

Any and All

Another type which can act like a monoid in two distinct but equally valid ways @@ -1554,7 +1554,7 @@

Any and All

if all of them are True, respectively.

-

The Ordering monoid

+

The Ordering monoid

Hey, remember the Ordering type? It’s used as the @@ -1734,7 +1734,7 @@

The Ordering monoid

order themselves, ranging from the most important to the least.

-

Maybe the monoid

+

Maybe the monoid

Let’s take a look at the various ways that Maybe a @@ -1871,7 +1871,7 @@

Maybe the monoid

Just "two" -

Using monoids to fold data structures

+

Using monoids to fold data structures

One of the more interesting ways to put monoids to work is to make them help us diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index 7871e53..41034e2 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -31,7 +31,7 @@ -

Higher Order Functions

+

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

diff --git a/docs/input-and-output.html b/docs/input-and-output.html index 8878e2f..47753cc 100644 --- a/docs/input-and-output.html +++ b/docs/input-and-output.html @@ -31,7 +31,7 @@ -

Input and Output

+

Input and Output

poor dog

We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

While functions being unable to change state is good because it helps us reason about our programs, there’s one problem with that. If a function can’t change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

diff --git a/docs/introduction.html b/docs/introduction.html index b570994..77c51d4 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -29,7 +29,7 @@ -

Introduction

+

Introduction

About this tutorial

diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index 7cd9551..b28247e 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -31,7 +31,7 @@ -

Making Our Own Types and Typeclasses

+

Making Our Own Types and Typeclasses

In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we’ll learn how to make our own and how to put them to work!

Algebraic data types intro

So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

diff --git a/docs/modules.html b/docs/modules.html index 8b3c46e..2452f1b 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -31,7 +31,7 @@ -

Modules

+

Modules

Loading modules

modules

A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

diff --git a/docs/recursion.html b/docs/recursion.html index c1f384d..0a168ba 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -31,7 +31,7 @@ -

Recursion

+

Recursion

Hello recursion!

SOVIET RUSSIA

We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

diff --git a/docs/starting-out.html b/docs/starting-out.html index 2dc129d..8d14c44 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -31,7 +31,7 @@ -

Starting Out

+

Starting Out

Ready, set, go!

egg diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index dc5b4f3..86cd2e1 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -31,7 +31,7 @@ -

Syntax in Functions

+

Syntax in Functions

Pattern matching

four!

This chapter will cover some of Haskell’s cool syntactic constructs and we’ll start with pattern matching. Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns.

diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 242a786..7b4bbfb 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -31,7 +31,7 @@ -

Types and Typeclasses

+

Types and Typeclasses

Believe the type

moo

Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with some number, it won’t even compile. That’s good because it’s better to catch such errors at compile time instead of having your program crash. Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.

diff --git a/docs/zippers.html b/docs/zippers.html index 3bb841f..1ed731a 100644 --- a/docs/zippers.html +++ b/docs/zippers.html @@ -29,7 +29,7 @@ -

Zippers

+

Zippers

hi im chet @@ -312,7 +312,7 @@

A trail of breadcrumbs

(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R]) -

Going back up

+

Going back up

What if we now want to go back up in our tree? From our breadcrumbs we know @@ -468,7 +468,7 @@

Going back up

Zipper.

-

Manipulating trees under focus

+

Manipulating trees under focus

Now that we can move up and down, let’s make a function that @@ -560,7 +560,7 @@

Manipulating trees under focus

an additional 'Z' on its far left.

-

I’m going straight to the top, oh yeah, up where the air is fresh and clean!

+

I’m going straight to the top, oh yeah, up where the air is fresh and clean!

Making a function that walks all the way to the top of the tree, regardless of @@ -771,7 +771,7 @@

A very simple file system

That’s actually what my disk contains right now.

-

A zipper for our file system

+

A zipper for our file system

spongedisk @@ -936,7 +936,7 @@

A zipper for our file system

File "watermelon_smash.gif" "smash!!" -

Manipulating our file system

+

Manipulating our file system

Now that we know how to navigate our file system, manipulating it is easy. From 571b3acda303f7309c19bf53959cb907b1535da2 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 01:24:34 +0900 Subject: [PATCH 15/27] Change order of attributes in img tags --- docs/a-fistful-of-monads.html | 26 +++++++-------- docs/faq.html | 2 +- docs/for-a-few-monads-more.html | 26 +++++++-------- docs/functionally-solving-problems.html | 10 +++--- ...tors-applicative-functors-and-monoids.html | 32 +++++++++---------- docs/higher-order-functions.html | 22 ++++++------- docs/input-and-output.html | 28 ++++++++-------- docs/introduction.html | 8 ++--- .../making-our-own-types-and-typeclasses.html | 24 +++++++------- docs/modules.html | 12 +++---- docs/recursion.html | 12 +++---- docs/starting-out.html | 18 +++++------ docs/syntax-in-functions.html | 8 ++--- docs/types-and-typeclasses.html | 8 ++--- docs/zippers.html | 18 +++++------ 15 files changed, 127 insertions(+), 127 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index a583c62..63dc4a3 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -47,7 +47,7 @@

A Fistful of Monads

applicative functors are only beefed up functors.

-more cool than u +more cool than u

When we started off with functors, we saw that it’s possible to map functions @@ -168,7 +168,7 @@

A Fistful of Monads

Getting our feet wet with Maybe

-monads, grasshoppa +monads, grasshoppa

Now that we have a vague idea of what monads are about, let’s see if we can make @@ -393,7 +393,7 @@

The Monad type class

fail msg = error msg -this is you on monads +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 @@ -429,7 +429,7 @@

The Monad type class

context. -hmmm yaes +hmmm yaes

The next function is >>=, or bind. It’s like @@ -515,7 +515,7 @@

The Monad type class

Walk the line

-pierre +pierre

Now that we know how to feed a Maybe a value to a @@ -809,7 +809,7 @@

Walk the line

Nothing -iama banana +iama banana

Awesome. The final result represents failure, which is what we expected. Let’s @@ -940,7 +940,7 @@

Walk the line

Just pole3 -> landLeft 1 pole3 -john joe glanton +john joe glanton

We land a bird on the left and then we examine the possibility of failure and @@ -1077,7 +1077,7 @@

do notation

Just (show x ++ y) -90s owl +90s owl

It would seem as though we’ve gained the ability to temporarily extract things @@ -1329,7 +1329,7 @@

do notation

The list monad

-dead cat +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 @@ -1465,7 +1465,7 @@

The list monad

[(1,'a'),(1,'b'),(2,'a'),(2,'b')] -concatmap +concatmap

The list [1,2] gets bound to @@ -1692,7 +1692,7 @@

A knight’s quest

the row.

-hee haw im a horse +hee haw im a horse

Let’s make a type synonym for the knight’s current position on the chess board: @@ -1842,8 +1842,8 @@

A knight’s quest

Monad laws

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

Just like applicative functors, and functors before them, monads come with a few diff --git a/docs/faq.html b/docs/faq.html index 3961cac..cc50d7f 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -17,7 +17,7 @@

FAQ

-turtle??? +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?

diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index f0be83f..bc3589d 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -33,7 +33,7 @@

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 +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 @@ -124,7 +124,7 @@

Writer? I hardly know her!

(True,"Compared gang size to 9.") -when you have to poop, poop, don’t talk +when you have to poop, poop, don’t talk

So far so good. isBigGang takes a normal value and @@ -394,7 +394,7 @@

The Writer type

-when you have to poop, poop, don’t talk +when you have to poop, poop, don’t talk

First off, let’s examine >>=. Its implementation is essentially the same as applyLog, @@ -756,7 +756,7 @@

Inefficient list construction

Difference lists

-cactuses +cactuses

Because lists can sometimes be inefficient when repeatedly appended in this @@ -976,7 +976,7 @@

Comparing Performance

Reader? Ugh, not this joke again.

-bang youre dead +bang youre dead

In the chapter about @@ -1136,7 +1136,7 @@

Reader? Ugh, not this joke again.

Tasteful stateful computations

-don’t jest with texas +don’t jest with texas

Haskell is a pure language and because of that, our programs are made of @@ -1366,7 +1366,7 @@

The State monad

a certain value as the result and keeps the state unchanged.

-im a cop +im a cop

What about >>=? Well, the result of feeding a @@ -1821,7 +1821,7 @@

Some useful monadic functions

liftM and friends

-im a cop too +im a cop too

When we started our journey to the top of Monad Mountain, we first looked @@ -2184,7 +2184,7 @@

The join function

m -im a cop too as well also +im a cop too as well also

Perhaps the most interesting thing about join is @@ -2468,7 +2468,7 @@

foldM

Making a safe RPN calculator

-i’ve found yellow! +i’ve found yellow!

When we were solving the problem of implementing a RPN calculator, @@ -2771,7 +2771,7 @@

Composing monadic functions

Making monads

-kewl +kewl

In this section, we’re going to look at an example of how a type gets made, @@ -2926,7 +2926,7 @@

Making monads

models this scenario:

-probs +probs

What are the chances for each of these letters to occur? If we were to draw this @@ -2989,7 +2989,7 @@

Making monads

fail _ = Prob [] -ride em cowboy +ride em cowboy

Because we already did all the hard work, the instance is very simple. We also diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index b67cec7..aa69733 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -36,12 +36,12 @@

Functionally Solving Problems

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 on to 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 +this expression

Let’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to 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 a 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.

Protip: 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 +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.

@@ -129,7 +129,7 @@

Reverse Polish notation calculatorHeathrow 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 +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:

@@ -156,7 +156,7 @@ 

Heathrow to London

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

@@ -218,7 +218,7 @@

Heathrow to London

else (C,c):(A,a):pathA in (newPathToA, newPathToB)
-this is you +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.

diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index f556a26..367a9dd 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -35,7 +35,7 @@

Functors, Applicative Functor

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 +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.
@@ -65,7 +65,7 @@

Functors redux

putStrLn $ "You said " ++ line ++ " backwards!" putStrLn $ "Yes, you really said" ++ line ++ " backwards!" -w00ooOoooOO +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.

@@ -117,7 +117,7 @@

Functors redux

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

@@ -168,7 +168,7 @@ 

Functors redux

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

@@ -286,7 +286,7 @@

Applicative functors

ghci> pure (+) <*> Nothing <*> Just 5 Nothing -whaale +whaale

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

@@ -365,7 +365,7 @@

Applicative functors

x <- b return (f x) -ahahahah! +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.

@@ -420,7 +420,7 @@

Applicative functors

ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5 [8.0,10.0,2.5] -SLAP +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.

@@ -556,7 +556,7 @@

Applicative functors

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? +why_ so serious?

So far, we’ve learned how to make our own algebraic data types by using the @@ -767,7 +767,7 @@

Using newtype to make type c fmap :: (a -> b) -> Maybe a -> Maybe b -wow, very evil +wow, very evil

Isn’t that just peachy? Now what if we wanted to make the tuple an instance of @@ -942,7 +942,7 @@

On newtype laziness

"hello" -top of the mornin to ya!!! +top of the mornin to ya!!!

It worked! Hmmm, why is that? Well, like we’ve said, when we use newtype, @@ -1064,8 +1064,8 @@

type vs. Monoids

-wow this is pretty much the gayest pirate ship
-ever +wow this is pretty much the gayest pirate ship
+ever

Type classes in Haskell are used to present an interface for types that have @@ -1177,7 +1177,7 @@

Monoids

mconcat = foldr mappend mempty -woof dee do!!! +woof dee do!!!

The Monoid type class is defined in @@ -1305,7 +1305,7 @@

Lists are monoids

[] -smug as hell +smug as hell

Notice that in the last line, we had to write an explicit type annotation, @@ -1589,7 +1589,7 @@

The Ordering monoid

GT `mappend` _ = GT -did anyone ORDER pizza?!?! I can’t BEAR these puns! +did anyone ORDER pizza?!?! I can’t BEAR these puns!

The instance is set up like this: when we mappend two @@ -2004,7 +2004,7 @@

Using monoids to fold data struct F.foldMap f r -find the visual pun or whatever +find the visual pun or whatever

We think like this: if we are provided with a function that takes an element of diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index 41034e2..a66da45 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -32,7 +32,7 @@

Higher Order Functions

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

@@ -42,7 +42,7 @@

Curried functions

ghci> (max 4) 5 5 -haskell curry +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:

@@ -98,7 +98,7 @@

Some higher-orderism is in order

applyTwice :: (a -> a) -> a -> a applyTwice f x = f (f x) -rocktopus +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:

@@ -211,7 +211,7 @@

Maps and filters

biggerSorted = quicksort (filter (>x) xs) in smallerSorted ++ [x] ++ biggerSorted -map +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.

@@ -265,7 +265,7 @@ 

Maps and filters

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

@@ -273,7 +273,7 @@ 

Lambdas

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

@@ -302,7 +302,7 @@ 

Lambdas

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

@@ -316,7 +316,7 @@

Only folds and horses

ghci> sum' [3,5,2,1] 11 -foldl +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:

@@ -338,7 +338,7 @@ 

Only folds and horses

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

@@ -396,7 +396,7 @@

Function application with $

($) :: (a -> b) -> a -> b f $ x = f x -dollar +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].

@@ -412,7 +412,7 @@

Function composition

(.) :: (b -> c) -> (a -> b) -> a -> c f . g = \x -> f (g x) -notes +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:

diff --git a/docs/input-and-output.html b/docs/input-and-output.html
index 47753cc..734c175 100644
--- a/docs/input-and-output.html
+++ b/docs/input-and-output.html
@@ -32,12 +32,12 @@
                 
             
         

Input and Output

-poor dog +poor dog

We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

While functions being unable to change state is good because it helps us reason about our programs, there’s one problem with that. If a function can’t change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

Do not despair, all is not lost. It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen. With those two parts separated, we can still reason about our pure program and take advantage of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

Hello, world!

-HELLO! +HELLO!

Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

So, for starters, punch in the following in your favorite text editor:

@@ -81,7 +81,7 @@

Hello, world!

ghci> :t getLine getLine :: IO String
-luggage +luggage

Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

@@ -352,7 +352,7 @@ 

Hello, world!

In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

Files and streams

-streams +streams

getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

@@ -541,7 +541,7 @@ 

Files and streams

 data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
 
-A FILE IN A CAKE!!! +A FILE IN A CAKE!!!

Just like our type that represents the seven possible values for the days of the week, this type is an enumeration that represents what we want to do with our opened file. Very simple. Just note that this type is IOMode and not IO Mode. IO Mode would be the type of an I/O action that has a value of some type Mode as its result, but IOMode is just a simple enumeration.

Finally, it returns an I/O action that will open the specified file in the specified mode. If we bind that action to something we get a Handle. A value of type Handle represents where our file is. We’ll use that handle so we know which file to read from. It would be stupid to read a file but not bind that read to a handle because we wouldn’t be able to do anything with the file. So in our case, we bound the handle to handle.

In the next line, we see a function called hGetContents. It takes a Handle, so it knows which file to get the contents from and returns an IO String — an I/O action that holds as its result the contents of the file. This function is pretty much like getContents. The only difference is that getContents will automatically read from the standard input (that is from the terminal), whereas hGetContents takes a file handle which tells it which file to read from. In all other respects, they work the same. And just like getContents, hGetContents won’t attempt to read the file at once and store it in memory, but it will read it as needed. That’s really cool because we can treat contents as the whole contents of the file, but it’s not really loaded in memory. So if this were a really huge file, doing hGetContents wouldn’t choke up our memory, but it would read only what it needed to from the file, when it needed to.

@@ -565,7 +565,7 @@

Files and streams

hClose handle return result
-butter toast +butter toast

We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

Loading files and then treating their contents as strings is so common that we have these three nice little functions to make our work even easier:

@@ -706,7 +706,7 @@

Files and streams

Take salad out of the oven

Command line arguments

-COMMAND LINE ARGUMENTS!!! ARGH +COMMAND LINE ARGUMENTS!!! ARGH

Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line arguments of a program.

In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

One way to solve that is to always ask the user which file they want to use as their to-do list. We used that approach when we wanted to know which item the user wants to delete. It works, but it’s not so good, because it requires the user to run the program, wait for the program to ask something and then tell that to the program. That’s called an interactive program and the difficult bit with interactive command line programs is this — what if you want to automate the execution of that program, like with a batch script? It’s harder to make a batch script that interacts with a program than a batch script that just calls one program or several of them.

@@ -843,7 +843,7 @@

Command line arguments

removeFile fileName renameFile tempName fileName -fresh baked salad +fresh baked salad

To summarize our solution: we made a dispatch association that maps from commands to functions that take some command line arguments and return an I/O action. We see what the command is and based on that we get the appropriate function from the dispatch list. We call that function with the rest of the command line arguments to get back an I/O action that will do the appropriate thing and then just perform that action!

In other languages, we might have implemented this with a big switch case statement or whatever, but using higher order functions allows us to just tell the dispatch list to give us the appropriate function and then tell that function to give us an I/O action for some command line arguments.

Let’s try our app out!

@@ -871,7 +871,7 @@

Command line arguments

Another cool thing about this is that it’s easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you’re laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

You could make this program fail a bit more gracefully in case of bad input (for example, if someone runs todo UP YOURS HAHAHAHA) by making an I/O action that just reports there has been an error (say, errorExit :: IO ()) and then check for possible erroneous input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

Randomness

-this picture is the ultimate source of randomness and wackiness +this picture is the ultimate source of randomness and wackiness

Many times while programming, you need to get some random data. Maybe you’re making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of uses for random data when programming. Well, actually, pseudo-random, because we all know that the only true source of randomness is a monkey on a unicycle with a cheese in one hand and its butt in the other. In this section, we’ll take a look at how to make Haskell generate seemingly random data.

In most other programming languages, you have functions that give you back some random number. Each time you call that function, you get back a (hopefully) different random number. How about Haskell? Well, remember, Haskell is a pure functional language. What that means is that it has referential transparency. What THAT means is that a function, if given the same parameters twice, must produce the same result twice. That’s really cool because it allows us to reason differently about programs and it enables us to defer evaluation until we really need it. If I call a function, I can be sure that it won’t do any funny stuff before giving me the results. All that matters are its results. However, this makes it a bit tricky for getting random numbers. If I have a function like this:

@@ -1051,7 +1051,7 @@ 

Randomness

else putStrLn $ "Sorry, it was " ++ show randNumber askForNumber newGen
-jack of diamonds +jack of diamonds

We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

@@ -1090,7 +1090,7 @@

Randomness

It’s very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

Bytestrings

-like normal string, only they byte … what a pedestrian pun this is +like normal string, only they byte … what a pedestrian pun this is

Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

@@ -1155,7 +1155,7 @@

Bytestrings

Notice that a program that doesn’t use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that’s not hard.

Whenever you need better performance in a program that reads a lot of data into strings, give bytestrings a try, chances are you’ll get some good performance boosts with very little effort on your part. I usually write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

Exceptions

-timberr!!!! +timberr!!!!

All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn’t be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we’ve defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we’re not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell’s type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

Despite having expressive types that support failed computations, Haskell still has support for exceptions, because they make more sense in I/O contexts. A lot of things can go wrong when dealing with the outside world because it is so unreliable. For instance, when opening a file, a bunch of things can go wrong. The file might be locked, it might not be there at all or the hard disk drive or something might not be there at all. So it’s good to be able to jump to some error handling part of our code when such an error occurs.

@@ -1166,7 +1166,7 @@

Exceptions

ghci> head [] *** Exception: Prelude.head: empty list -Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail. +Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail.

Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can’t do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don’t know when they will be evaluated and that it really shouldn’t matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That’s why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that’s bad, because we want to keep the I/O part as small as possible. However, if we don’t catch them in the I/O part of our code, our program crashes. The solution? Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful type system and use types like Either and Maybe to represent results that may have failed.

That’s why we’ll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that’s part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

@@ -1199,7 +1199,7 @@

Exceptions

We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

Another solution here would be to use exceptions. It’s perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

To deal with this by using exceptions, we’re going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

-non sequitor +non sequitor

If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

So let’s put our new friend catch to use!

diff --git a/docs/introduction.html b/docs/introduction.html index 77c51d4..dd6ecc6 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -39,7 +39,7 @@

About this tutorial

I decided to write this because I wanted to solidify my own knowledge of Haskell and because I thought I could help people new to Haskell learn it from my perspective. There are quite a few tutorials on Haskell floating around on the internet. When I was starting out in Haskell, I didn’t learn from just one resource. The way I learned it was by reading several different tutorials and articles because each explained something in a different way than the other did. By going through several resources, I was able to put together the pieces and it all just came falling into place. So this is an attempt at adding another useful resource for learning Haskell so you have a bigger chance of finding one you like.

-bird +bird

This tutorial is aimed at people who have experience in imperative programming languages (C, C++, Java, Python …) but haven’t programmed in a functional language before (Haskell, ML, OCaml …). Although I bet that even if you don’t have any significant programming experience, a smart person such as yourself will be able to follow along and learn Haskell.

@@ -52,16 +52,16 @@

About this tutorial

So what’s Haskell?

-fx +fx Haskell is a purely functional programming language. In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together.

-lazy +lazy Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you “Yeah yeah, I’ll do it later!”. But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end.

-boat +boat Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers.

diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index b28247e..5e3d5af 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -43,7 +43,7 @@

Algebraic data types intro

 data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
 
-caveman +caveman

The first and last value constructors are the minimum and maximum possible values of Int. It’s not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so this is just for illustrative purposes.

Now, let’s think about how we would represent a shape in Haskell. One way would be to use tuples. A circle could be denoted as (43.1, 55.0, 10.4) where the first and second fields are the coordinates of the circle’s center and the third field is the radius. Sounds OK, but those could also represent a 3D vector or anything else. A better solution would be to make our own type to represent a shape. Let’s say that a shape can be a circle or a rectangle. Here it is:

@@ -145,7 +145,7 @@ 

Algebraic data types intro

We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can’t create a map by doing Map.Map [(1,2),(3,4)] because it doesn’t export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

Not exporting the value constructors of a data types makes them more abstract in such a way that we hide their implementation. Also, whoever uses our module can’t pattern match against the value constructors.

Record syntax

-record +record

OK, we’ve been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I don’t know about you, but that’s all I ever want to know about a person. Let’s give it a go!

 data Person = Person String String Int Float String String deriving (Show)
@@ -226,7 +226,7 @@ 

Type parameters

 data Maybe a = Nothing | Just a
 
-yeti +yeti

The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that’s not a type per se, it’s a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

So if we pass Char as the type parameter to Maybe, we get a type of Maybe Char. The value Just 'a' has a type of Maybe Char, for example.

You might not know it, but we used a type that has a type parameter before we used Maybe. That type is the list type. Although there’s some syntactic sugar in play, the list type takes a parameter to produce a concrete type. Values can have an [Int] type, a [Char] type, a [[String]] type, but you can’t have a value that just has a type of [].

@@ -287,7 +287,7 @@

Type parameters

ghci> :t Car "Ford" "Mustang" "nineteen sixty seven" Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char]
-meekrat +meekrat

In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We usually use type parameters when the type that’s contained inside the data type’s various value constructors isn’t really that important for the type to work. A list of stuff is a list of stuff and it doesn’t matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn’t matter what the type of that something is.

Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

@@ -324,7 +324,7 @@

Type parameters

Vector 148 666 222

Derived instances

-gob +gob

In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

@@ -477,7 +477,7 @@

Type synonyms

  type String = [Char]
  
- chicken + chicken

We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a synonym for an already existing type.

@@ -591,7 +591,7 @@

Type synonyms

We could have used a Maybe a to represent the result but then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

Recursive data structures

-the fonz +the fonz

As we’ve seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have fields that are of the same type! Using that, we can create recursive data types, where one value of some type contains values of that type, which in turn contain more values of the same type and so on.

Think about this list: [5]. That’s just syntactic sugar for 5:[]. On the left side of the :, there’s a value and on the right side, there’s a list. And in this case, it’s an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

We could say that a list can be an empty list or it can be an element joined together with a : with another list (that can be either the empty list or not).

@@ -653,7 +653,7 @@

Recursive data structures

Nice. Is nice. If we wanted, we could implement all of the functions that operate on lists on our own list type.

Notice how we pattern matched on (x :-: xs). That works because pattern matching is actually about matching constructors. We can match on :-: because it is a constructor for our own list type and we can also match on : because it is a constructor for the built-in list type. Same goes for []. Because pattern matching works (only) on constructors, we can match for stuff like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

-binary search tree +binary search tree

Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we’d start at 5 and then because 8 is greater than 5, we’d go right. We’re now at 7 and because 8 is greater than 7, we go right again. And we’ve found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we’ll just be implementing normal binary search trees.

Here’s what we’re going to say: a tree is either an empty tree or it’s an element that contains some value and two trees. Sounds like a perfect fit for an algebraic data type!

@@ -705,7 +705,7 @@

Recursive data structures

Checking for membership also works nicely. Cool.

So as you can see, algebraic data structures are a really cool and powerful concept in Haskell. We can use them to make anything from boolean values and weekday enumerations to binary search trees and more!

Typeclasses 102

-tweet +tweet

So far, we’ve learned about some of the standard Haskell typeclasses and we’ve seen which types are in them. We’ve also learned how to automatically make our own types instances of the standard typeclasses by asking Haskell to derive the instances for us. In this section, we’re going to learn how to make our own typeclasses and how to make types instances of them by hand.

A quick recap on typeclasses: typeclasses are like interfaces. A typeclass defines some behavior (like comparing for equality, comparing for ordering, enumeration) and then types that can behave in that way are made instances of that typeclass. The behavior of typeclasses is achieved by defining functions or just type declarations that we then implement. So when we say that a type is an instance of a typeclass, we mean that we can use the functions that the typeclass defines with that type.

Typeclasses have pretty much nothing to do with classes in languages like Java or Python. This confuses many people, so I want you to forget everything you know about classes in imperative languages right now.

@@ -799,7 +799,7 @@

Typeclasses 102

Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

A yes-no typeclass

-yesno +yesno

In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

@@ -890,7 +890,7 @@ 

The Functor typeclass

class Functor f where fmap :: (a -> b) -> f a -> f b
-I AM FUNCTOOOOR!!! +I AM FUNCTOOOOR!!!

Alright. We see that it defines one function, fmap, and doesn’t provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so far, the type variable that played the role of the type in the typeclass was a concrete type, like the a in (==) :: (Eq a) => a -> a -> Bool. But now, the f is not a concrete type (a type that a value can hold, like Int, Bool or Maybe String), but a type constructor that takes one type parameter. A quick refresher example: Maybe Int is a concrete type, but Maybe is a type constructor that takes one type as the parameter. Anyway, we see that fmap takes a function from one type to another and a functor applied with one type and returns a functor applied with another type.

If this sounds a bit confusing, don’t worry. All will be revealed soon when we check out a few examples. Hmm, this type declaration for fmap reminds me of something. If you don’t know what the type signature of map is, it’s: map :: (a -> b) -> [a] -> [b].

Ah, interesting! It takes a function from one type to another and a list of one type and returns a list of another type. My friends, I think we have ourselves a functor! In fact, map is just a fmap that works only on lists. Here’s how the list is an instance of the Functor typeclass.

@@ -955,7 +955,7 @@

The Functor typeclass

With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

Kinds and some type-foo

-TYPE FOO MASTER +TYPE FOO MASTER

Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

What are kinds and what are they good for? Well, let’s examine the kind of a type by using the :k command in GHCI.

diff --git a/docs/modules.html b/docs/modules.html index 2452f1b..580173a 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -33,7 +33,7 @@

Modules

Loading modules

-modules +modules

A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There’s a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we’ve dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we’re going to examine a few useful modules and the functions that they have. But first, we’re going to see how to import modules.

The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file. One script can, of course, import several modules. Just put each import statement into a separate line. Let’s import the Data.List module, which has a bunch of useful functions for working with lists and use a function that it exports to create a function that tells us how many unique elements a list has.

@@ -101,7 +101,7 @@

Data.List

[18,8,6,17]

When we transpose these three lists, the third powers are then in the first row, the second powers in the second one and so on. Mapping sum to that produces our desired result.

-shopping lists +shopping lists

foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, you might often get a stack overflow error. The culprit for that is that due to the lazy nature of the folds, the accumulator value isn’t actually updated as the folding happens. What actually happens is that the accumulator kind of makes a promise that it will compute its value when asked to actually produce the result (also called a thunk). That happens for every intermediate accumulator and all those thunks overflow your stack. The strict folds aren’t lazy buggers and actually compute the intermediate values as they go along instead of filling up your stack with thunks. So if you ever get stack overflow errors when doing lazy folds, try switching to their strict versions.

concat flattens a list of lists into just a list of elements.

@@ -409,7 +409,7 @@ 

Data.List

Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

Data.Char

-lego char +lego char

The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here’s what they are:

isControl checks whether a character is a control character.

@@ -569,7 +569,7 @@

Data.Map

ghci> findKey "christopher" phoneBook Nothing -legomap +legomap

Works like a charm! If we have the friend’s phone number, we Just get the number, otherwise we get Nothing.

We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they’re internally implemented with trees) and also it provides a lot of utility functions. From now on, we’ll say we’re working with maps instead of association lists.

Because Data.Map exports functions that clash with the Prelude and Data.List ones, we’ll do a qualified import.

@@ -709,7 +709,7 @@

Data.Map

These were just a few functions from Data.Map. You can see a complete list of functions in the documentation.

Data.Set

-legosets +legosets

The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

Put this import statement in a script:

@@ -794,7 +794,7 @@

Data.Set

setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

Making our own modules

-making modules +making modules

We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it’s good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

We say that a module exports functions. What that means is that when I import a module, I can use the functions that it exports. It can define functions that its functions call internally, but we can only see and use the ones that it exports.

diff --git a/docs/recursion.html b/docs/recursion.html index 0a168ba..259aa26 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -33,7 +33,7 @@

Recursion

Hello recursion!

-SOVIET RUSSIA +SOVIET RUSSIA

We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

@@ -60,7 +60,7 @@

Maximum awesome

maximum' (x:xs) = max x (maximum' xs)

How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

-max +max

A few more recursive functions

Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

@@ -79,7 +79,7 @@ 

A few more recursive functions

take' _ [] = [] take' n (x:xs) = x : take' (n-1) xs
-painter +painter

The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

reverse simply reverses a list. Think about the edge condition. What is it? Come on … it’s the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

@@ -113,7 +113,7 @@ 

A few more recursive functions

Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

Quick, sort!

We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

-quickman +quickman

So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

 quicksort :: (Ord a) => [a] -> [a]
@@ -131,11 +131,11 @@ 

Quick, sort!

" abcdeeefghhijklmnoooopqrrsttuuvwxyz"

Booyah! That’s what I’m talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

-quicksort +quicksort

An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They’re in green here. We chose the head because it’s easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

Thinking recursively

We did quite a bit of recursion so far and as you’ve probably noticed, there’s a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn’t matter if it’s a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera …

-brain +brain

Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn’t make sense. When dealing with lists, the edge case is most often the empty list. If you’re dealing with trees, the edge case is usually a node that doesn’t have any children.

It’s similar when you’re dealing with numbers recursively. Usually it has to do with some number and the function applied to that number modified. We did the factorial function earlier and it’s the product of a number and the factorial of that number minus one. Such a recursive application doesn’t make sense with zero, because factorials are defined only for positive integers. Often the edge case value turns out to be an identity. The identity for multiplication is 1 because if you multiply something by 1, you get that something back. Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the identity for addition. In quicksort, the edge case is the empty list and the identity is also the empty list, because if you add an empty list to a list, you just get the original list back.

So when trying to think of a recursive way to solve a problem, try to think of when a recursive solution doesn’t apply and see if you can use that as an edge case, think about identities and think about whether you’ll break apart the parameters of the function (for instance, lists are usually broken into a head and a tail via pattern matching) and on which part you’ll use the recursive call.

diff --git a/docs/starting-out.html b/docs/starting-out.html index 8d14c44..67377c3 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -34,7 +34,7 @@

Starting Out

Ready, set, go!

-egg +egg Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we’re going to load functions. The first thing we’re going to do is run ghc’s interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this.

@@ -113,7 +113,7 @@ 

Ready, set, go!

You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them.

-phoen +phoen Functions are usually prefix, so from now on we won’t explicitly state that a function is of the prefix form, we’ll just assume it. In most imperative languages, functions are called by writing the function name and then writing its parameters in parentheses, usually separated by commas. In Haskell, functions are called by writing the function name, a space and then the parameters, separated by spaces. For a start, we’ll try calling one of the most boring functions in Haskell.

@@ -190,7 +190,7 @@ 

Baby’s first functions

doubleSmallNumber x = if x > 100 then x else x*2
-this is you +this is you

Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this.

@@ -207,7 +207,7 @@

Baby’s first functions

An intro to lists

-BUY A DOG +BUY A DOG Much like shopping lists in the real world, lists in Haskell are very useful. It’s the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this section we’ll look at the basics of lists, strings (which are lists) and list comprehensions.

@@ -301,7 +301,7 @@

An intro to lists

ghci> init [5,4,3,2,1] [5,4,3,2]

If we think of a list as a monster, here’s what’s what.

-list monster +list monster

But what happens if we try to get the head of an empty list?

 ghci> head []
@@ -364,7 +364,7 @@ 

An intro to lists

Those were a few basic functions that operate on lists. We’ll take a look at more list functions later.

Texas ranges

-draw +draw What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that’s not a solution for gentlemen who demand excellence from their programming languages. Instead, we’ll use ranges. Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers can be enumerated. One, two, three, four, etc. Characters can also be enumerated. The alphabet is an enumeration of characters from A to Z. Names can’t be enumerated. What comes after “John”? I don’t know.

To make a list containing all the natural numbers from 1 to 20, you just write [1..20]. That is the equivalent of writing [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and there’s no difference between writing one or the other except that writing out long enumeration sequences manually is stupid.

@@ -410,7 +410,7 @@

Texas ranges

Although it’s simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

I’m a list comprehension

-frog +frog If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate.

If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn’t want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We’ll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here’s that comprehension in action.

@@ -476,7 +476,7 @@

I’m a list comprehension

You can write list comprehensions across several lines. So if you’re not in GHCI, it’s better to split longer list comprehensions across multiple lines, especially if they’re nested.

Tuples

-tuples +tuples

In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That’s its type and it doesn’t matter if it has only one number in it or an infinite amount of numbers. Tuples, however, are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.

Another key difference is that they don’t have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

Think about how we’d represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it’s still a list of lists with numbers, but it kind of doesn’t make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can’t have a couple of pairs in it and then a triple (a tuple of size three), so let’s use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we’d get this error:

@@ -523,7 +523,7 @@

Tuples

ghci> zip [1..] ["apple", "orange", "cherry", "mango"] [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
-look at meee +look at meee

Here’s a problem that combines tuples and list comprehensions: which right triangle that has integers for all sides and all sides equal to or smaller than 10 has a perimeter of 24? First, let’s try generating all triangles with sides equal to or smaller than 10:

ghci> triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ] 

We’re just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out triangles in GHCI, you’ll get a list of all possible triangles with sides under or equal to 10. Next, we’ll add a condition that they all have to be right triangles. We’ll also modify this function by taking into consideration that side b isn’t larger than the hypothenuse and that side a isn’t larger than side b.

diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index 86cd2e1..6e2be46 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -33,7 +33,7 @@

Syntax in Functions

Pattern matching

-four! +four!

This chapter will cover some of Haskell’s cool syntactic constructs and we’ll start with pattern matching. Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns.

When defining functions, you can define separate function bodies for different patterns. This leads to really neat code that’s simple and readable. You can pattern match on any data type — numbers, characters, lists, tuples, etc. Let’s make a really trivial function that checks if the number we supplied to it is a seven or not.

@@ -165,7 +165,7 @@ 

Pattern matching

Normally we use as patterns to avoid repeating ourselves when matching against a bigger pattern when we have to use the whole thing again in the function body.

One more thing — you can’t use ++ in pattern matches. If you tried to pattern match against (xs ++ ys), what would be in the first and what would be in the second list? It doesn’t make much sense. It would make sense to match stuff against (xs ++ [x,y,z]) or just (xs ++ [x]), but because of the nature of lists, you can’t do that.

Guards, guards!

-guards +guards

Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds a lot like an if statement and it’s very similar. The thing is that guards are a lot more readable when you have several conditions and they play really nicely with patterns.

Instead of explaining their syntax, let’s just dive in and make a function using guards. We’re going to make a simple function that responds differently depending on the density given. Density (or specific mass) is a substance’s mass per unit of volume (here, grams per liter). If a substance has a density of less than 1.2, it will float in air, as 1.2g/L is the density of air. If it has more than 1000g/L (the density of water), it will sink in water. Between are things (like people, usually) that will neither float away nor sink in water. @@ -285,7 +285,7 @@

Let it be

topArea = pi * r ^2 in sideArea + 2 * topArea
-let it be +let it be

The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs. Remember when we did the if statement and it was explained that an if else statement is an expression and you can cram it in almost anywhere?

@@ -337,7 +337,7 @@ 

Let it be

If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

Case expressions

-case +case

Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for specific values of that variable and then maybe including a catch-all block of code in case the variable has some value for which we didn’t set up a case.

Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html
index 7b4bbfb..56e39f8 100644
--- a/docs/types-and-typeclasses.html
+++ b/docs/types-and-typeclasses.html
@@ -33,7 +33,7 @@
             
         

Types and Typeclasses

Believe the type

-moo +moo

Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with some number, it won’t even compile. That’s good because it’s better to catch such errors at compile time instead of having your program crash. Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.

Unlike Java or Pascal, Haskell has type inference. If we write a number, we don’t have to tell Haskell it’s a number. It can infer that on its own, so we don’t have to explicitly write out the types of our functions and expressions to get things done. We covered some of the basics of Haskell with only a very superficial glance at types. However, understanding the type system is a very important part of learning Haskell.

A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

@@ -51,7 +51,7 @@

Believe the type

4 == 5 :: Bool

-bomb +bomb Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. 'a', as it would seem, has a type of Char. It’s not hard to conclude that it stands for character. True is of a Bool type. That makes sense. But what’s this? Examining the type of "HELLO!" yields a [Char]. The square brackets denote a list. So we read that as it being a list of characters. Unlike lists, each tuple length has its own type. So the expression of (True, 'a') has a type of (Bool, Char), whereas an expression such as ('a','b','c') would have the type of (Char, Char, Char). 4 == 5 will always return False, so its type is Bool.

@@ -119,7 +119,7 @@

Type variables

head :: [a] -> a

-box +box Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means that a can be of any type. This is much like generics in other languages, only in Haskell it’s much more powerful because it allows us to easily write very general functions if they don’t use any specific behavior of the types in them. Functions that have type variables are called polymorphic functions. The type declaration of head states that it takes a list of any type and returns one element of that type.

Although type variables can have names longer than one character, we usually give them names of a, b, c, d …

@@ -132,7 +132,7 @@

Type variables

We see that fst takes a tuple which contains two types and returns an element which is of the same type as the pair’s first component. That’s why we can use fst on a pair that contains any two types. Note that just because a and b are different type variables, they don’t have to be different types. It just states that the first component’s type and the return value’s type are the same.

Typeclasses 101

-class +class

A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages. Well, they’re not. You can think of them kind of as Java interfaces, only better.

What’s the type signature of the == function?

diff --git a/docs/zippers.html b/docs/zippers.html index 1ed731a..cac3702 100644 --- a/docs/zippers.html +++ b/docs/zippers.html @@ -31,7 +31,7 @@

Zippers

-hi im chet +hi im chet

While Haskell’s purity comes with a whole bunch of benefits, it makes us tackle @@ -115,7 +115,7 @@

Taking a walk

And here’s this tree represented graphically:

-polly says her back hurts +polly says her back hurts

Notice that W in the tree there? Say we want to @@ -224,7 +224,7 @@

Taking a walk

A trail of breadcrumbs

-whoop dee doo +whoop dee doo

Okay, so for focusing on a subtree, we want something better than just a list @@ -280,7 +280,7 @@

A trail of breadcrumbs

(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R]) -almostthere +almostthere

Okay, so now we have a tree that has 'W' @@ -429,7 +429,7 @@

Going back up

goUp (t, RightCrumb x l:bs) = (Node x l t, bs) -asstronaut +asstronaut

We’re focusing on the tree t and we check what the @@ -600,7 +600,7 @@

Focusing on lists

data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord) -the best damn thing +the best damn thing

Contrast this with our definition of our binary tree and it’s easy to see how @@ -773,7 +773,7 @@

A very simple file system

A zipper for our file system

-spongedisk +spongedisk

Now that we have a file system, all we need is a zipper so we can zip and zoom @@ -874,7 +874,7 @@

A zipper for our file system

function doesn’t search all over the place, it just looks at the current folder.

-wow cool great +wow cool great

First we use break to break the list of items in a @@ -1015,7 +1015,7 @@

Watch your step

goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs) -falling for you +falling for you

But what if the tree we’re stepping off from is an empty tree? That is, what if From af2552cfa27b2e52613f1de9693d4b918d3b0a1d Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 01:45:32 +0900 Subject: [PATCH 16/27] Delete px from img tag width and height attributes --- docs/functors-applicative-functors-and-monoids.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index 367a9dd..f8c98e3 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -420,7 +420,7 @@

Applicative functors

ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5 [8.0,10.0,2.5] -SLAP +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.

@@ -767,7 +767,7 @@

Using newtype to make type c fmap :: (a -> b) -> Maybe a -> Maybe b -wow, very evil +wow, very evil

Isn’t that just peachy? Now what if we wanted to make the tuple an instance of From c84190a4b915bab96700b169c7503b07c8dc6a98 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 01:49:45 +0900 Subject: [PATCH 17/27] Replace newline with space in img alt attributes --- docs/a-fistful-of-monads.html | 3 +-- docs/functors-applicative-functors-and-monoids.html | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 63dc4a3..cd45a63 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -1842,8 +1842,7 @@

A knight’s quest

Monad laws

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

Just like applicative functors, and functors before them, monads come with a few diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html index f8c98e3..21901ec 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -1064,8 +1064,7 @@

type vs. Monoids

-wow this is pretty much the gayest pirate ship
-ever +wow this is pretty much the gayest pirate ship ever

Type classes in Haskell are used to present an interface for types that have From 1c5b1a073908e82fe728adbbd63d9bcda3cc7ff6 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 02:19:41 +0900 Subject: [PATCH 18/27] Change license link rel attribute from nofollow to license and adjust its position --- docs/chapters.html | 2 +- docs/faq.html | 2 +- markdown/generated_html/faq.html | 2 +- markdown/source_md/chapters_foot.md | 2 +- markdown/source_md/faq.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/chapters.html b/docs/chapters.html index 1fa4d4a..68f346c 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -166,7 +166,7 @@

Learn You a Haskell for Great Good!<

- 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. + 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 index cc50d7f..f56e382 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -19,7 +19,7 @@

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.

+

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.

diff --git a/markdown/generated_html/faq.html b/markdown/generated_html/faq.html index 5c298e7..9a1b98c 100644 --- a/markdown/generated_html/faq.html +++ b/markdown/generated_html/faq.html @@ -23,7 +23,7 @@

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 +rel="license">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 diff --git a/markdown/source_md/chapters_foot.md b/markdown/source_md/chapters_foot.md index 939def4..1818275 100644 --- a/markdown/source_md/chapters_foot.md +++ b/markdown/source_md/chapters_foot.md @@ -1,3 +1,3 @@ -This work is licensed under a [Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/){rel=license rel=nofollow} because I couldn't find a license with an even longer name. +This work is licensed under a [Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/){rel=license} because I couldn't find a license with an even longer name. diff --git a/markdown/source_md/faq.md b/markdown/source_md/faq.md index d7cd10d..f69c3c3 100644 --- a/markdown/source_md/faq.md +++ b/markdown/source_md/faq.md @@ -4,7 +4,7 @@ ## Can I put this tutorial on my site or change it or whatever? -Sure, it's licensed under a [creative commons](https://creativecommons.org/licenses/by-nc-sa/3.0/){rel=nofollow} 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. +Sure, it's licensed under a [creative commons](https://creativecommons.org/licenses/by-nc-sa/3.0/){rel=license} 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? From 02209b5386f3e5a32163c6573b5728e3948ea555 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 03:26:41 +0900 Subject: [PATCH 19/27] Post-process pandoc output to add chapters class to chapter list --- markdown/generate.sh | 10 ++- .../generated_html/a-fistful-of-monads.html | 48 +++++++------- markdown/generated_html/chapters.html | 2 +- markdown/generated_html/faq.html | 4 +- .../generated_html/for-a-few-monads-more.html | 52 +++++++-------- .../functionally-solving-problems.html | 20 +++--- ...tors-applicative-functors-and-monoids.html | 64 +++++++++---------- .../higher-order-functions.html | 46 ++++++------- markdown/generated_html/input-and-output.html | 56 ++++++++-------- markdown/generated_html/introduction.html | 10 +-- .../making-our-own-types-and-typeclasses.html | 54 ++++++++-------- markdown/generated_html/modules.html | 28 ++++---- markdown/generated_html/recursion.html | 24 +++---- markdown/generated_html/starting-out.html | 28 ++++---- .../generated_html/syntax-in-functions.html | 16 ++--- .../generated_html/types-and-typeclasses.html | 12 ++-- markdown/generated_html/zippers.html | 36 +++++------ markdown/generated_md/chapters.md | 2 +- 18 files changed, 260 insertions(+), 252 deletions(-) diff --git a/markdown/generate.sh b/markdown/generate.sh index 8205d7d..f67d6d1 100644 --- a/markdown/generate.sh +++ b/markdown/generate.sh @@ -54,7 +54,10 @@ do --metadata title="${title[$i]}$titlesuffix" \ -V prev_title="$prev_title" -V prev_filename=$prev_filename \ -V next_title="$next_title" -V next_filename=$next_filename \ - -o generated_html/${filename[$i]}.html source_md/${filename[$i]}.md + -o generated_html/${filename[$i]}.html source_md/${filename[$i]}.md + + sed '/

\(]*\) />

#\1># } + s# />#>#' -i generated_html/${filename[$i]}.html done cat source_md/chapters_foot.md >>$chapterfile @@ -63,6 +66,11 @@ pandoc -d config/pandoc-defaults.yml --template=config/template.html \ -V title="Chapters" --metadata title="${title[$i]}$titlesuffix" \ -o generated_html/chapters.html $chapterfile +sed 's/
    \(]*\) />

    #\1># } + s# />#>#' -i generated_html/faq.html diff --git a/markdown/generated_html/a-fistful-of-monads.html b/markdown/generated_html/a-fistful-of-monads.html index 226f740..b336165 100644 --- a/markdown/generated_html/a-fistful-of-monads.html +++ b/markdown/generated_html/a-fistful-of-monads.html @@ -41,8 +41,8 @@

    A Fistful of Monads

    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

    +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 @@ -119,8 +119,8 @@

    A Fistful of Monads

    that it’s easy as one two three.

    Getting our feet wet with Maybe

    -

    monads, grasshoppa

    +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 @@ -264,8 +264,8 @@

    The Monad type class

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

    this is you on monads

    +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 @@ -294,8 +294,8 @@

    The Monad type class

    function execution or anything, it just takes a normal value and puts it in a context.

    -

    hmmm yaes

    +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 @@ -348,8 +348,8 @@

    The Monad type class

    result of using >>= will be Nothing as well.

    Walk the line

    -

    pierre

    +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 @@ -518,8 +518,8 @@

    Walk the line

    (>>=) instead of normal application:

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

    iama banana

    +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, @@ -600,8 +600,8 @@

    Walk the line

    Just pole2 -> case landLeft 2 pole2 of Nothing -> Nothing Just pole3 -> landLeft 1 pole3 -

    john joe glanton

    +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 @@ -684,8 +684,8 @@

    do notation

    x <- Just 3 y <- Just "!" Just (show x ++ y) -

    90s owl

    +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 @@ -841,8 +841,8 @@

    do notation

    of our monad instead of causing a program-wide failure, which is pretty neat.

    The list monad

    -

    dead cat

    +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 @@ -925,8 +925,8 @@

    The list monad

    with >>=, propagating the non-determinism:

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

    concatmap

    +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 @@ -1061,8 +1061,8 @@

    A knight’s quest

    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

    +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)
    @@ -1144,9 +1144,9 @@

    A knight’s quest

    the number of moves to take instead of that number being hardcoded like it is now.

    Monad laws

    -

    +alt="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 diff --git a/markdown/generated_html/chapters.html b/markdown/generated_html/chapters.html index b5ea74a..faac4bd 100644 --- a/markdown/generated_html/chapters.html +++ b/markdown/generated_html/chapters.html @@ -18,7 +18,7 @@

    Learn You a Haskell for Great Good!

    -
      +
      1. Introduction
        • About this diff --git a/markdown/generated_html/faq.html b/markdown/generated_html/faq.html index 9a1b98c..fd9f17f 100644 --- a/markdown/generated_html/faq.html +++ b/markdown/generated_html/faq.html @@ -17,8 +17,8 @@

          For a Few Monads More

          -

          +alt="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 @@ -86,9 +86,9 @@

          Writer? I hardly know her!

          (False,"Compared gang size to 9.") ghci> isBigGang 30 (True,"Compared gang size to 9.") -

          +alt="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 @@ -255,9 +255,9 @@

          The Writer type

          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')
          -

          +alt="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 @@ -492,8 +492,8 @@

          Inefficient list

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

          Difference lists

          -

          cactuses

          +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 @@ -617,8 +617,8 @@

          Comparing Performance

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

          Reader? Ugh, not this joke again.

          -

          bang youre dead

          +bang youre dead

          In the chapter about applicatives, we saw that the function type, (->) r @@ -725,8 +725,8 @@

          Reader? Ugh, not this joke again.

          results and the >>= implementation will make sure that it all works out.

          Tasteful stateful computations

          -

          don’t jest with texas

          +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 @@ -876,8 +876,8 @@

          The State monad

          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

          +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 @@ -1146,8 +1146,8 @@

          Some useful monadic functions

          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

          +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 @@ -1363,8 +1363,8 @@

          The join function

          joinedMaybes = do m <- Just (Just 8) m -

          im a cop too as well also

          +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 @@ -1532,8 +1532,8 @@

          foldM

          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!

          +i’ve found yellow!

          When we were solving the problem of implementing a RPN calculator, we noted that it worked fine as long as the input @@ -1717,8 +1717,8 @@

          Composing monadic functions

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

          Making monads

          -

          kewl

          +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 @@ -1817,8 +1817,8 @@

          Making monads

          happen. 'c' and 'd' are also equally likely to happen. Here’s a picture of a probability list that models this scenario:

          -

          probs

          +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 @@ -1856,8 +1856,8 @@

          Making monads

          return x = Prob [(x,1%1)] m >>= f = flatten (fmap f m) fail _ = Prob [] -

          ride em cowboy

          +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 diff --git a/markdown/generated_html/functionally-solving-problems.html b/markdown/generated_html/functionally-solving-problems.html index fc2e681..04d6c6d 100644 --- a/markdown/generated_html/functionally-solving-problems.html +++ b/markdown/generated_html/functionally-solving-problems.html @@ -67,8 +67,8 @@

          Reverse Polish notation 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

          +this expression

          Let’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to the stack and the stack is now 10. The next item is 4, so we push it to the @@ -102,8 +102,8 @@

          Reverse Polish notation declaration tells us a whole lot about the function, due to the very strong type system.

          -

          HA HA HA

          +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 @@ -285,8 +285,8 @@

          Heathrow to London

          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

          +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 @@ -337,9 +337,9 @@

          Heathrow to London

          of say that we’re pretty sure.

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

          -

          roads

          +class="center" width="685" height="245" alt="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 @@ -515,8 +515,8 @@

          Heathrow to London

          then (B,b):pathB else (C,c):(A,a):pathA in (newPathToA, newPathToB) -

          this is you

          +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 diff --git a/markdown/generated_html/functors-applicative-functors-and-monoids.html b/markdown/generated_html/functors-applicative-functors-and-monoids.html index a9919da..46d5b36 100644 --- a/markdown/generated_html/functors-applicative-functors-and-monoids.html +++ b/markdown/generated_html/functors-applicative-functors-and-monoids.html @@ -59,10 +59,10 @@

          Functors, Applicative versions of functors called applicative functors. We’ll also take a look at monoids, which are sort of like socks.

          Functors redux

          -

          +alt="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 @@ -154,9 +154,9 @@

          Functors redux

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

          w00ooOoooOO

          +class="left" width="262" height="212" alt="w00ooOoooOO">

          Just like when we fmap reverse over Just "blah" to get Just "halb", we can fmap reverse over getLine. @@ -298,10 +298,10 @@

          Functors redux

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

          -

          +alt="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 @@ -422,10 +422,10 @@

          Functors redux

          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.

          -

          +alt="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 @@ -576,10 +576,10 @@

          Functors redux

          and then it will go through the attached transformation where it will be added to three. This is what happens with composition.

          Applicative functors

          -

          +alt="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 @@ -756,9 +756,9 @@

          Applicative functors

          Nothing ghci> pure (+) <*> Nothing <*> Just 5 Nothing -

          whaale

          +class="right" width="214" height="177" alt="whaale">

          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 @@ -940,9 +940,9 @@

          Applicative functors

          f <- a x <- b return (f x) -

          ahahahah!

          +class="left" width="195" height="458" alt="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 @@ -1055,9 +1055,9 @@

          Applicative functors

          508.

          ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
           [8.0,10.0,2.5]
          -

          SLAP

          +class="right" width="400" height="230" alt="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 @@ -1390,9 +1390,9 @@

          Applicative functors

          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?

          +class="left" width="107" height="202" alt="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 @@ -1510,9 +1510,9 @@

          Using newtype to fmap like it only worked on Maybe, it ends up behaving like:

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

          wow, very evil

          +class="right" width="322" height="280" alt="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 @@ -1609,10 +1609,10 @@

          On newtype laziness

          apply helloMe to an undefined value:

          ghci> helloMe undefined
           "hello"
          -

          +alt="top of the mornin 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 @@ -1692,10 +1692,10 @@

          type if you want to make something completely new, odds are good that you’re looking for the data keyword.

          Monoids

          -

          +alt="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 @@ -1770,9 +1770,9 @@

          Monoids

          mappend :: m -> m -> m mconcat :: [m] -> m mconcat = foldr mappend mempty -

          woof dee do!!!

          +class="right" width="260" height="326" alt="woof dee do!!!">

          The Monoid type class is defined in import Data.Monoid. Let’s take some time and get properly acquainted with it.

          @@ -1860,9 +1860,9 @@

          Lists are monoids

          [1,2,3,6,9] ghci> mempty :: [a] [] -

          smug as hell

          +class="left" width="157" height="144" alt="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. @@ -2038,10 +2038,10 @@

          The Ordering monoid

          LT `mappend` _ = LT EQ `mappend` y = y GT `mappend` _ = GT -

          +alt="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 @@ -2298,10 +2298,10 @@

          Using monoids to fold foldMap f (Node x l r) = F.foldMap f l `mappend` f x `mappend` F.foldMap f r -

          +alt="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 diff --git a/markdown/generated_html/higher-order-functions.html b/markdown/generated_html/higher-order-functions.html index 14cb5cf..60dfb46 100644 --- a/markdown/generated_html/higher-order-functions.html +++ b/markdown/generated_html/higher-order-functions.html @@ -33,8 +33,8 @@

    Higher Order Functions

    -

    sun

    +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 @@ -62,8 +62,8 @@

    Curried functions

    5 ghci> (max 4) 5 5 -

    haskell curry

    +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 @@ -176,8 +176,8 @@

    Some higher-orderism is in order

    and then applies it twice to something!

    applyTwice :: (a -> a) -> a -> a
     applyTwice f x = f (f x)
    -

    rocktopus

    +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 @@ -373,8 +373,8 @@

    Maps and filters

    let smallerSorted = quicksort (filter (<=x) xs) biggerSorted = quicksort (filter (>x) xs) in smallerSorted ++ [x] ++ biggerSorted -

    map

    +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 @@ -504,8 +504,8 @@

    Maps and filters

    just apply 5 to that function. So that’s like writing (4*) 5 or just 4 * 5.

    Lambdas

    -

    lambda

    +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 @@ -525,8 +525,8 @@

    Lambdas

    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

    +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 @@ -573,8 +573,8 @@

    Lambdas

    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

    +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 @@ -605,8 +605,8 @@

    Only folds and horses

    Testing, one two three:

    ghci> sum' [3,5,2,1]
     11
    -

    foldl

    +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. @@ -685,8 +685,8 @@

    Only folds and horses

    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!

    +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 @@ -810,8 +810,8 @@

    Function application with $

    how it’s defined:

    ($) :: (a -> b) -> a -> b
     f $ x = f x
    -

    dollar

    +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 @@ -849,7 +849,7 @@

    Function application with $

    Function composition

    In mathematics, function composition is defined like this: , meaning that composing two functions +alt="(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.

    @@ -858,8 +858,8 @@

    Function composition

    like so:

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

    notes

    +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 diff --git a/markdown/generated_html/input-and-output.html b/markdown/generated_html/input-and-output.html index 400542b..95098d2 100644 --- a/markdown/generated_html/input-and-output.html +++ b/markdown/generated_html/input-and-output.html @@ -32,8 +32,8 @@

    Input and Output

    -

    poor dog

    +poor dog

    We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of @@ -68,8 +68,8 @@

    Input and Output

    of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

    Hello, world!

    -

    HELLO!

    +HELLO!

    Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally @@ -152,8 +152,8 @@

    Hello, world!

    getLine.

    ghci> :t getLine
     getLine :: IO String
    -

    luggage

    +luggage

    Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that @@ -672,8 +672,8 @@

    Hello, world!

    function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

    Files and streams

    -

    streams

    +streams

    getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most @@ -968,8 +968,8 @@

    Files and streams

    type FilePath = String

    IOMode is a type that’s defined like this:

    data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
    -

    A FILE IN A CAKE!!!

    +A FILE IN A CAKE!!!

    Just like our type that represents the seven possible values for the days of the week, this type is an enumeration that represents what we want to do with our opened file. Very simple. Just note that this type @@ -1048,8 +1048,8 @@

    Files and streams

    result <- f handle hClose handle return result -

    butter toast

    +butter toast

    We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O @@ -1319,8 +1319,8 @@

    Files and streams

    $ cat todo.txt Take salad out of the oven

    Command line arguments

    -

    COMMAND LINE ARGUMENTS!!! ARGH

    +COMMAND LINE ARGUMENTS!!! ARGH

    Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line @@ -1540,8 +1540,8 @@

    Command line arguments

    hClose tempHandle removeFile fileName renameFile tempName fileName -

    fresh baked salad

    +fresh baked salad

    To summarize our solution: we made a dispatch association that maps from commands to functions that take some command line arguments and return an I/O action. We see what the command is and based on that we @@ -1587,9 +1587,9 @@

    Command line arguments

    input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

    Randomness

    -

    +alt="this picture is the ultimate source of randomness and wackiness">

    Many times while programming, you need to get some random data. Maybe you’re making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of @@ -1860,8 +1860,8 @@

    Randomness

    then putStrLn "You are correct!" else putStrLn $ "Sorry, it was " ++ show randNumber askForNumber newGen -

    jack of diamonds

    +jack of diamonds

    We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first @@ -1933,9 +1933,9 @@

    Randomness

    less stuff in main and also provides us with a function that we can reuse easily.

    Bytestrings

    -

    +alt="like normal string, only they byte … what a pedestrian pun this is">

    Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while @@ -2121,8 +2121,8 @@

    Bytestrings

    write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

    Exceptions

    -

    timberr!!!!

    +timberr!!!!

    All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use @@ -2166,9 +2166,9 @@

    Exceptions

    *** Exception: divide by zero ghci> head [] *** Exception: Prelude.head: empty list -

    +alt="Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail.">

    Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) @@ -2252,8 +2252,8 @@

    Exceptions

    which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

    -

    non sequitor

    +non sequitor

    If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the diff --git a/markdown/generated_html/introduction.html b/markdown/generated_html/introduction.html index 147f497..21544c1 100644 --- a/markdown/generated_html/introduction.html +++ b/markdown/generated_html/introduction.html @@ -46,8 +46,8 @@

    About this tutorial

    came falling into place. So this is an attempt at adding another useful resource for learning Haskell so you have a bigger chance of finding one you like.

    -

    bird

    +bird

    This tutorial is aimed at people who have experience in imperative programming languages (C, C++, Java, Python …) but haven’t programmed in a functional language before (Haskell, ML, OCaml …). Although I bet that @@ -68,7 +68,7 @@

    About this tutorial

    So what’s Haskell?

    fx Haskell is a purely +width="150" height="146" alt="fx"> Haskell is a purely functional programming language. In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, @@ -93,7 +93,7 @@

    So what’s Haskell?

    is correct and then build more complex functions by gluing simple functions together.

    lazy Haskell is lazy. +width="240" height="209" alt="lazy"> Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows @@ -118,7 +118,7 @@

    So what’s Haskell?

    language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end.

    boat Haskell is statically +width="160" height="153" alt="boat"> Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add diff --git a/markdown/generated_html/making-our-own-types-and-typeclasses.html b/markdown/generated_html/making-our-own-types-and-typeclasses.html index 8d1497e..846bfb0 100644 --- a/markdown/generated_html/making-our-own-types-and-typeclasses.html +++ b/markdown/generated_html/making-our-own-types-and-typeclasses.html @@ -54,9 +54,9 @@

    Algebraic data types intro

    In a similar fashion, we can think of the Int type as being defined like this:

    data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
    -

    caveman

    +class="left" width="220" height="215" alt="caveman">

    The first and last value constructors are the minimum and maximum possible values of Int. It’s not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so @@ -216,9 +216,9 @@

    Algebraic data types intro

    abstract in such a way that we hide their implementation. Also, whoever uses our module can’t pattern match against the value constructors.

    Record syntax

    -

    record

    +class="right" width="208" height="97" alt="record">

    OK, we’ve been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I @@ -319,9 +319,9 @@

    Type parameters

    get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

    data Maybe a = Nothing | Just a
    -

    yeti

    +class="left" width="209" height="260" alt="yeti">

    The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not @@ -412,9 +412,9 @@

    Type parameters

    Car "Ford" "Mustang" 1967 :: (Num t) => Car [Char] [Char] t ghci> :t Car "Ford" "Mustang" "nineteen sixty seven" Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char] -

    meekrat

    +class="right" width="150" height="267" alt="meekrat">

    In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We @@ -502,8 +502,8 @@

    Type parameters

    ghci> Vector 2 9 3 `vectMult` (Vector 4 9 5 `scalarMult` Vector 9 2 4) Vector 148 666 222

    Derived instances

    -

    gob

    +gob

    In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a @@ -710,9 +710,9 @@

    Type synonyms

    reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char].

    type String = [Char]
    -

    chicken

    +class="left" width="169" height="225" alt="chicken">

    We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a @@ -782,9 +782,9 @@

    Type synonyms

    Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and -my buddies say that Maybe is a type, but we don’t mean that, -cause every idiot knows Maybe is a type constructor. When I -apply an extra type to Maybe, like +my buddies say that Maybe is a type, but we don’t mean +that, cause every idiot knows Maybe is a type constructor. +When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

    @@ -920,9 +920,9 @@

    Type synonyms

    then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

    Recursive data structures

    -

    the fonz

    +class="right" width="168" height="301" alt="the fonz">

    As we’ve seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have @@ -1023,9 +1023,9 @@

    Recursive data structures

    like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

    -

    binary search tree

    +class="left" width="323" height="225" alt="binary search tree">

    Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left @@ -1139,9 +1139,9 @@

    Recursive data structures

    boolean values and weekday enumerations to binary search trees and more!

    Typeclasses 102

    -

    tweet

    +class="right" width="175" height="480" alt="tweet">

    So far, we’ve learned about some of the standard Haskell typeclasses and we’ve seen which types are in them. We’ve also learned how to automatically make our own types instances of the standard typeclasses @@ -1375,9 +1375,9 @@

    Typeclasses 102

    show you the type declaration of a function. I think that’s pretty cool.

    A yes-no typeclass

    -

    yesno

    +class="left" width="201" height="111" alt="yesno">

    In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), @@ -1499,9 +1499,9 @@

    The Functor typeclass

    than to see how it’s implemented? Let’s take a peek.

    class Functor f where
         fmap :: (a -> b) -> f a -> f b
    -

    I AM FUNCTOOOOR!!!

    +class="right" width="220" height="441" alt="I AM FUNCTOOOOR!!!">

    Alright. We see that it defines one function, fmap, and doesn’t provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so @@ -1673,9 +1673,9 @@

    The Functor typeclass

    functor laws in more detail in one of the next chapters.

    Kinds and some type-foo

    -

    TYPE FOO MASTER

    +class="right" width="287" height="400" alt="TYPE FOO MASTER">

    Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type diff --git a/markdown/generated_html/modules.html b/markdown/generated_html/modules.html index 0265bd5..163da8c 100644 --- a/markdown/generated_html/modules.html +++ b/markdown/generated_html/modules.html @@ -33,8 +33,8 @@

    Modules

    Loading modules

    -

    modules

    +modules

    A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in @@ -169,8 +169,8 @@

    Data.List

    When we transpose these three lists, the third powers are then in the first row, the second powers in the second one and so on. Mapping sum to that produces our desired result.

    -

    shopping lists

    +shopping lists

    foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, @@ -638,8 +638,8 @@

    Data.List

    By functions that take an ordering function, you usually do compare `on` something.

    Data.Char

    -

    lego char

    +lego char

    The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of @@ -873,8 +873,8 @@

    Data.Map

    Just "555-2938" ghci> findKey "christopher" phoneBook Nothing -

    legomap

    +legomap

    Works like a charm! If we have the friend’s phone number, we Just get the number, otherwise we get Nothing.

    @@ -984,8 +984,8 @@

    Data.Map

    fromListWith is a cool little function. It acts like fromList, only it doesn’t discard duplicate keys but it uses a function supplied to it to decide what to -do with them. Let’s say that a friend can have several numbers and we have -an association list set up like this.

    +do with them. Let’s say that a friend can have several numbers and we +have an association list set up like this.

    phoneBook =
         [("amelia","555-2938")
         ,("amelia","342-2492")
    @@ -1035,8 +1035,8 @@ 

    Data.Map

    see a complete list of functions in the documentation.

    Data.Set

    -

    legosets

    +legosets

    The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally @@ -1138,8 +1138,8 @@

    Data.Set

    lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

    Making our own modules

    -

    making modules

    +making modules

    We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, diff --git a/markdown/generated_html/recursion.html b/markdown/generated_html/recursion.html index 85ad224..426ee35 100644 --- a/markdown/generated_html/recursion.html +++ b/markdown/generated_html/recursion.html @@ -33,8 +33,8 @@

    Recursion

    Hello recursion!

    -

    SOVIET RUSSIA

    +SOVIET RUSSIA

    We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to @@ -129,8 +129,8 @@

    Maximum awesome

    maximum' (x:xs) = max x (maximum' xs)

    How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

    -

    max

    +max

    A few more recursive functions

    Now that we know how to generally think recursively, let’s implement @@ -170,8 +170,8 @@

    A few more recursive | n <= 0 = [] take' _ [] = [] take' n (x:xs) = x : take' (n-1) xs -

    painter

    +painter

    The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is @@ -254,8 +254,8 @@

    Quick, sort!

    child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

    -

    quickman

    +quickman

    So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty @@ -296,8 +296,8 @@

    Quick, sort!

    the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

    -

    quicksort

    +quicksort

    An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose @@ -321,8 +321,8 @@

    Thinking recursively

    element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera …

    -

    brain

    +brain

    Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn’t make sense. When dealing with lists, the edge case is most often the empty list. If you’re diff --git a/markdown/generated_html/starting-out.html b/markdown/generated_html/starting-out.html index add4522..675a08c 100644 --- a/markdown/generated_html/starting-out.html +++ b/markdown/generated_html/starting-out.html @@ -34,7 +34,7 @@

    Starting Out

    Ready, set, go!

    egg Alright, let’s get started! If +width="214" height="187" alt="egg"> Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this @@ -120,7 +120,7 @@

    Ready, set, go!

    functions that aren’t used with numbers are prefix functions. Let’s take a look at them.

    phoen Functions are usually prefix, so +width="160" height="161" alt="phoen"> Functions are usually prefix, so from now on we won’t explicitly state that a function is of the prefix form, we’ll just assume it. In most imperative languages, functions are called by writing the function name and then writing its parameters in @@ -230,8 +230,8 @@

    Baby’s first functions

    doubleSmallNumber x = if x > 100
                             then x
                             else x*2
    -

    this is you

    +this is you

    Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that @@ -270,7 +270,7 @@

    Baby’s first functions

    interchangeably.

    An intro to lists

    BUY A DOG Much like shopping lists in +width="150" height="103" alt="BUY A DOG"> Much like shopping lists in the real world, lists in Haskell are very useful. It’s the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this @@ -384,8 +384,8 @@

    An intro to lists

    ghci> init [5,4,3,2,1]
     [5,4,3,2]

    If we think of a list as a monster, here’s what’s what.

    -

    list monster

    +list monster

    But what happens if we try to get the head of an empty list?

    ghci> head []
     *** Exception: Prelude.head: empty list
    @@ -463,7 +463,7 @@

    An intro to lists

    href="modules.html#data-list">later.

    Texas ranges

    draw What if we want a list of all +width="200" height="258" alt="draw"> What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that’s not a solution for gentlemen who demand excellence from their programming languages. Instead, we’ll use ranges. Ranges are a way @@ -533,12 +533,12 @@

    Texas ranges

    [10,10,10].

    I’m a list comprehension

    frog If you’ve ever taken a course in +width="180" height="156" alt="frog"> If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is . The part before the pipe is called the output +alt="set notation">. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the @@ -638,8 +638,8 @@

    I’m a list comprehension

    not in GHCI, it’s better to split longer list comprehensions across multiple lines, especially if they’re nested.

    Tuples

    -

    tuples

    +tuples

    In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That’s its type and @@ -733,8 +733,8 @@

    Tuples

    infinite lists:

    ghci> zip [1..] ["apple", "orange", "cherry", "mango"]
     [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
    -

    look at meee

    +look at meee

    Here’s a problem that combines tuples and list comprehensions: which right triangle that has integers for all sides and all sides equal to or smaller than 10 has a perimeter of 24? First, let’s try generating all diff --git a/markdown/generated_html/syntax-in-functions.html b/markdown/generated_html/syntax-in-functions.html index 4b84390..d538e02 100644 --- a/markdown/generated_html/syntax-in-functions.html +++ b/markdown/generated_html/syntax-in-functions.html @@ -34,8 +34,8 @@

    Syntax in Functions

    Pattern matching

    -

    four!

    +four!

    This chapter will cover some of Haskell’s cool syntactic constructs and we’ll start with pattern matching. Pattern matching consists of specifying patterns to which some data should conform and then checking @@ -255,8 +255,8 @@

    Pattern matching

    (xs ++ [x,y,z]) or just (xs ++ [x]), but because of the nature of lists, you can’t do that.

    Guards, guards!

    -

    guards

    +guards

    Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds @@ -440,8 +440,8 @@

    Let it be

    let sideArea = 2 * pi * r * h topArea = pi * r ^2 in sideArea + 2 * topArea -

    let it be

    +let it be

    The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have @@ -516,8 +516,8 @@

    Let it be

    way, the function body is closer to its name and type declaration and to some that’s more readable.

    Case expressions

    -

    case

    +case

    Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for diff --git a/markdown/generated_html/types-and-typeclasses.html b/markdown/generated_html/types-and-typeclasses.html index db74b96..9c965fc 100644 --- a/markdown/generated_html/types-and-typeclasses.html +++ b/markdown/generated_html/types-and-typeclasses.html @@ -33,8 +33,8 @@

    Types and Typeclasses

    Believe the type

    -

    moo

    +moo

    Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with @@ -67,7 +67,7 @@

    Believe the type

    ghci> :t 4 == 5 4 == 5 :: Bool

    bomb Here we see that doing +width="171" height="144" alt="bomb"> Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. @@ -158,7 +158,7 @@

    Type variables

    ghci> :t head
     head :: [a] -> a

    box Hmmm! What is this a? +width="130" height="93" alt="box"> Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means @@ -183,8 +183,8 @@

    Type variables

    different types. It just states that the first component’s type and the return value’s type are the same.

    Typeclasses 101

    -

    class

    +class

    A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming diff --git a/markdown/generated_html/zippers.html b/markdown/generated_html/zippers.html index ee10f5c..9b8f25c 100644 --- a/markdown/generated_html/zippers.html +++ b/markdown/generated_html/zippers.html @@ -30,8 +30,8 @@

    Zippers

    -

    hi im chet

    +hi im chet

    While Haskell’s purity comes with a whole bunch of benefits, it makes us tackle some problems differently than we would in impure languages. Because of referential transparency, one value is as good as another in @@ -87,8 +87,8 @@

    Taking a walk

    ) )

    And here’s this tree represented graphically:

    -

    polly says her back hurts

    +polly says her back hurts

    Notice that W in the tree there? Say we want to change it into a P. How would we go about doing that? Well, one way would be to pattern match on our tree until we find the element @@ -157,8 +157,8 @@

    Taking a walk

    subtree, one that allows us to efficiently switch focus to subtrees that are nearby.

    A trail of breadcrumbs

    -

    whoop dee doo

    +whoop dee doo

    Okay, so for focusing on a subtree, we want something better than just a list of directions that we always follow from the root of our tree. Would it help if we start at the root of the tree and move either @@ -185,8 +185,8 @@

    A trail of breadcrumbs

    freeTree and go right and then left:

    ghci> goLeft (goRight (freeTree, []))
     (Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
    -

    almostthere

    +almostthere

    Okay, so now we have a tree that has 'W' in its root and 'C' in the root of its left subtree and 'R' in the root of its right subtree. The breadcrumbs are [L,R], @@ -268,8 +268,8 @@

    Going back up

    goUp :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
     goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)
     goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
    -

    asstronaut

    +asstronaut

    We’re focusing on the tree t and we check what the latest Crumb is. If it’s a LeftCrumb, then we construct a new tree where our tree t is the left subtree @@ -368,8 +368,8 @@

    Focusing on lists

    href="making-our-own-types-and-typeclasses.html#recursive-data-structures">implemented our own lists, we defined our data type like so:

    data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
    -

    the best damn thing

    +the best damn thing

    Contrast this with our definition of our binary tree and it’s easy to see how lists can be viewed as trees where each node has only one subtree.

    @@ -479,8 +479,8 @@

    A very simple file system

    ]

    That’s actually what my disk contains right now.

    A zipper for our file system

    -

    spongedisk

    +spongedisk

    Now that we have a file system, all we need is a zipper so we can zip and zoom around it and add, modify and remove files as well as folders. Like with binary trees and lists, we’re going to be leaving breadcrumbs @@ -540,8 +540,8 @@

    A zipper for our file system

    focuses on the file with the given name. That file has to be in the current focused folder. This function doesn’t search all over the place, it just looks at the current folder.

    -

    wow cool great

    +wow cool great

    First we use break to break the list of items in a folder into those that precede the file that we’re searching for and those that come after it. If you remember, break takes a @@ -617,8 +617,8 @@

    Watch your step

    subtree:

    goLeft :: Zipper a -> Zipper a
     goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
    -

    falling for you

    +falling for you

    But what if the tree we’re stepping off from is an empty tree? That is, what if it’s not a Node, but an Empty? In this case, we’d get a runtime error because the pattern match would fail diff --git a/markdown/generated_md/chapters.md b/markdown/generated_md/chapters.md index 1c9b6bf..cd9051d 100644 --- a/markdown/generated_md/chapters.md +++ b/markdown/generated_md/chapters.md @@ -89,5 +89,5 @@ * [A very simple file system](zippers.html#a-very-simple-file-system) * [Watch your step](zippers.html#watch-your-step) -This work is licensed under a [Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/){rel=license rel=nofollow} because I couldn't find a license with an even longer name. +This work is licensed under a [Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License](https://creativecommons.org/licenses/by-nc-sa/3.0/){rel=license} because I couldn't find a license with an even longer name. From e1a6952adbe072304ffb56808a40c405f63fa66f Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 03:33:59 +0900 Subject: [PATCH 20/27] Change HTML chapter list to match markdown: remove enclosing paragraph and add type attribute --- docs/chapters.html | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/chapters.html b/docs/chapters.html index 68f346c..e8a969c 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -17,8 +17,7 @@

    Learn You a Haskell for Great Good!

    -

    -

      +
      1. Introduction
          @@ -164,7 +163,6 @@

          Learn You a Haskell for Great Good!<

      -

      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.

      From e4771d8d915ee8fac31702abd9ad514b6739371b Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Sun, 27 Nov 2022 00:22:32 +0900 Subject: [PATCH 21/27] Add upper margin to chapter list to restore the spacing from the removed

      tag --- docs/assets/css/style.css | 1 + docs/style.css | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index cba43e6..5cbbee8 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -127,6 +127,7 @@ pre.code { padding-left:25px; margin:0px; color:#408156 ; + margin-top:25px; margin-bottom:25px; } .chapters > li { diff --git a/docs/style.css b/docs/style.css index cba43e6..5cbbee8 100644 --- a/docs/style.css +++ b/docs/style.css @@ -127,6 +127,7 @@ pre.code { padding-left:25px; margin:0px; color:#408156 ; + margin-top:25px; margin-bottom:25px; } .chapters > li { From 8565206fb734cf2acc23b6947a23f317914d078c Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 22:09:09 +0900 Subject: [PATCH 22/27] Change and tags to in HTML --- docs/a-fistful-of-monads.html | 4 +-- docs/assets/css/style.css | 2 +- docs/functionally-solving-problems.html | 8 +++--- ...tors-applicative-functors-and-monoids.html | 22 ++++++++-------- docs/higher-order-functions.html | 22 ++++++++-------- docs/input-and-output.html | 16 ++++++------ docs/introduction.html | 12 ++++----- .../making-our-own-types-and-typeclasses.html | 26 +++++++++---------- docs/modules.html | 2 +- docs/recursion.html | 6 ++--- docs/starting-out.html | 8 +++--- docs/style.css | 2 +- docs/syntax-in-functions.html | 8 +++--- docs/types-and-typeclasses.html | 8 +++--- 14 files changed, 73 insertions(+), 73 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index cd45a63..517e3f3 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -146,8 +146,8 @@

      A Fistful of Monads

      -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 +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 diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index 5cbbee8..73773bb 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -35,7 +35,7 @@ p { a:hover { text-decoration:none; } -em { +strong { font-style:normal; font-weight:bold; } diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index aa69733..5e83ff1 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -40,7 +40,7 @@

      Reverse Polish notation calculatorLet’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to 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 a 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.

      -
      Protip: 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.
      +
      Protip: 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.

      @@ -160,7 +160,7 @@

      Heathrow to London

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

      @@ -200,7 +200,7 @@

      Heathrow to London

      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
      +
      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) =
      @@ -228,7 +228,7 @@ 

      Heathrow to London

      ([(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.
      +
      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
      diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html
      index 21901ec..811ff34 100644
      --- a/docs/functors-applicative-functors-and-monoids.html
      +++ b/docs/functors-applicative-functors-and-monoids.html
      @@ -38,7 +38,7 @@ 

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

      @@ -143,8 +143,8 @@

      Functors redux

      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.

      +

      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)
      @@ -169,7 +169,7 @@ 

      Functors redux

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

      +

      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.

      @@ -294,7 +294,7 @@

      Applicative functors

      (<$>) :: (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.
      +
      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:

      @@ -560,10 +560,10 @@ 

      The newtype keyword

      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 +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. +newtype keyword and why we’d want to do that in the first place.

      @@ -976,7 +976,7 @@

      type vs. 

      -The type keyword is for making type synonyms. What that means is that +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:

      @@ -1010,7 +1010,7 @@

      type vs. 

      -The newtype keyword is for taking existing types and wrapping them in +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: @@ -1047,7 +1047,7 @@

      type vs. 

      -The data keyword is for making your own data types and with them, you +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. diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index a66da45..a8a9e59 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -35,7 +35,7 @@

      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:

      +

      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
      @@ -43,8 +43,8 @@ 

      Curried functions

      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.

      +

      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
      @@ -70,7 +70,7 @@ 

      Curried functions

      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:

      +
      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)
      @@ -100,7 +100,7 @@ 

      Some higher-orderism is in order

      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.
      +
      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
      @@ -213,14 +213,14 @@ 

      Maps and filters

      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.

      +

      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:

      +

      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
      @@ -232,7 +232,7 @@ 

      Maps and filters

      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:

      +

      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]
      @@ -256,7 +256,7 @@ 

      Maps and filters

      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.
      +
      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 listOfFuns = map (*) [0..]
      @@ -340,7 +340,7 @@ 

      Only folds and horses

      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.

      +

      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:

      @@ -376,7 +376,7 @@ 

      Only folds and horses

      [[],[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 roots of all natural numbers to exceed 1000? To get the squares 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.

      +

      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 roots of all natural numbers to exceed 1000? To get the squares 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
      diff --git a/docs/input-and-output.html b/docs/input-and-output.html
      index 734c175..3b7b28e 100644
      --- a/docs/input-and-output.html
      +++ b/docs/input-and-output.html
      @@ -39,7 +39,7 @@ 

      Input and Output

      Hello, world!

      HELLO!

      Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

      -
      Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.
      +
      Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

      So, for starters, punch in the following in your favorite text editor:

       main = putStrLn "hello, world"
      @@ -64,7 +64,7 @@ 

      Hello, world!

      ghci> :t putStrLn "hello, world" putStrLn "hello, world" :: IO ()
      -

      We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

      +

      We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

      The empty tuple is a value of () and it also has a type of ().

      So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

      Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

      @@ -82,7 +82,7 @@

      Hello, world!

      getLine :: IO String
      luggage -

      Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

      +

      Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

      When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

       main = do
      @@ -103,7 +103,7 @@ 

      Hello, world!

      name <- getLine putStrLn ("Hey " ++ name ++ ", you rock!")
      -

      However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

      +

      However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

      Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

      Beginners sometimes think that doing

      @@ -146,7 +146,7 @@ 

      Hello, world!

      reverseWords = unwords . map reverse . words

      To get a feel of what it does, you can run it before we go over the code.

      -
      Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.
      +
      Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

      First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

      What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

      Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

      @@ -156,7 +156,7 @@

      Hello, world!

      main)

      This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

      -

      Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

      +

      Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

      Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

       main = do
      @@ -1053,7 +1053,7 @@ 

      Randomness

      jack of diamonds

      We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

      -
      Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.
      +
      Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

      We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

      main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

      Here’s our program in action!

      @@ -1093,7 +1093,7 @@

      Bytestrings

      like normal string, only they byte … what a pedestrian pun this is

      Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

      However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

      -

      That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

      +

      That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

      Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

      The other variety of bytestrings resides in Data.ByteString.Lazy. They’re lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That’s what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it’s just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won’t cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU’s L2 cache.

      If you look through the documentation for Data.ByteString.Lazy, you’ll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we’re going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

      diff --git a/docs/introduction.html b/docs/introduction.html index dd6ecc6..d45dcb2 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -33,7 +33,7 @@

      Introduction

      About this tutorial

      -Welcome to Learn You a Haskell for Great Good! +Welcome to Learn You a Haskell for Great Good! If you’re reading this, chances are you want to learn Haskell. Well, you’ve come to the right place, but let’s talk about this tutorial a bit first.

      @@ -53,22 +53,22 @@

      About this tutorial

      So what’s Haskell?

      fx -Haskell is a purely functional programming language. +Haskell is a purely functional programming language. In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together.

      lazy -Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you “Yeah yeah, I’ll do it later!”. But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end. +Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you “Yeah yeah, I’ll do it later!”. But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end.

      boat -Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers. +Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers.

      -Haskell is elegant and concise. Because it uses a lot of high level concepts, Haskell programs are usually shorter than their imperative equivalents. And shorter programs are easier to maintain than longer ones and have less bugs. +Haskell is elegant and concise. Because it uses a lot of high level concepts, Haskell programs are usually shorter than their imperative equivalents. And shorter programs are easier to maintain than longer ones and have less bugs.

      - Haskell was made by some really smart folk (with PhDs). Work on Haskell began in 1987 when a committee of researchers got together to design a kick-ass language. In 2003 the Haskell Report was published, which defines a stable version of the language. + Haskell was made by some really smart folk (with PhDs). Work on Haskell began in 1987 when a committee of researchers got together to design a kick-ass language. In 2003 the Haskell Report was published, which defines a stable version of the language.

      What you need to dive in

      diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index 5e3d5af..fcd17c3 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -34,11 +34,11 @@

      Making Our Own Types and Typeclasses

      In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we’ll learn how to make our own and how to put them to work!

      Algebraic data types intro

      -

      So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

      +

      So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

       data Bool = False | True
       
      -

      data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

      +

      data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

      In a similar fashion, we can think of the Int type as being defined like this:

       data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
      @@ -222,7 +222,7 @@ 

      Record syntax

      When making a new car, we don’t have to necessarily put the fields in the proper order, as long as we list all of them. But if we don’t use record syntax, we have to specify them in order.

      Use record syntax when a constructor has several fields and it’s not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it’s pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn’t so obvious and we greatly benefited from using record syntax.

      Type parameters

      -

      A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

      +

      A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

       data Maybe a = Nothing | Just a
       
      @@ -294,7 +294,7 @@

      Type parameters

       data (Ord k) => Map k v = ...
       
      -

      However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

      +

      However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

      So don’t put type constraints into data declarations even if it seems to make sense, because you’ll have to put them into the function type declarations either way.

      Let’s implement a 3D vector type and add some operations for it. We’ll be using a parameterized type because even though it will usually contain numeric types, it will still support several of them.

      @@ -310,7 +310,7 @@ 

      Type parameters

      (Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n

      vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

      -

      Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

      +

      Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

       ghci> Vector 3 5 8 `vplus` Vector 9 2 8
       Vector 12 7 16
      @@ -325,7 +325,7 @@ 

      Type parameters

      Derived instances

      gob -

      In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

      +

      In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

      We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

      In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

      Consider this data type:

      @@ -472,7 +472,7 @@

      Derived instances

      That’s pretty awesome.

      Type synonyms

      - Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char]. + Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char].

        type String = [Char]
      @@ -517,7 +517,7 @@ 

      Type synonyms

      type AssocList k v = [(k,v)]

      Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

      -
      Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!
      +
      Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

      Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

       type IntMap v = Map Int v
      @@ -527,7 +527,7 @@ 

      Type synonyms

      type IntMap = Map Int

      Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

      -
      Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.
      +
      Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.

      Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

      Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

      @@ -654,7 +654,7 @@ 

      Recursive data structures

      Nice. Is nice. If we wanted, we could implement all of the functions that operate on lists on our own list type.

      Notice how we pattern matched on (x :-: xs). That works because pattern matching is actually about matching constructors. We can match on :-: because it is a constructor for our own list type and we can also match on : because it is a constructor for the built-in list type. Same goes for []. Because pattern matching works (only) on constructors, we can match for stuff like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

      binary search tree -

      Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we’d start at 5 and then because 8 is greater than 5, we’d go right. We’re now at 7 and because 8 is greater than 7, we go right again. And we’ve found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

      +

      Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we’d start at 5 and then because 8 is greater than 5, we’d go right. We’re now at 7 and because 8 is greater than 7, we go right again. And we’ve found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

      Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we’ll just be implementing normal binary search trees.

      Here’s what we’re going to say: a tree is either an empty tree or it’s an element that contains some value and two trees. Sounds like a perfect fit for an algebraic data type!

      @@ -953,11 +953,11 @@ 

      The Functor typeclass

      Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

      Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

      Try figuring out how Map k is made an instance of Functor by yourself!

      With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

      -
      Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.
      +
      Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

      Kinds and some type-foo

      TYPE FOO MASTER -

      Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

      -

      So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

      +

      Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

      +

      So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

      What are kinds and what are they good for? Well, let’s examine the kind of a type by using the :k command in GHCI.

       ghci> :k Int
      diff --git a/docs/modules.html b/docs/modules.html
      index 580173a..c1cbb26 100644
      --- a/docs/modules.html
      +++ b/docs/modules.html
      @@ -560,7 +560,7 @@ 

      Data.Map

      findKey :: (Eq k) => k -> [(k,v)] -> Maybe v findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
      -
      Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.
      +
      Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.
       ghci> findKey "tenzing" phoneBook
       Just "853-2492"
      diff --git a/docs/recursion.html b/docs/recursion.html
      index 259aa26..b14c5a3 100644
      --- a/docs/recursion.html
      +++ b/docs/recursion.html
      @@ -35,7 +35,7 @@ 

      Recursion

      Hello recursion!

      SOVIET RUSSIA

      We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

      -

      If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

      +

      If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

      Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

      Maximum awesome

      The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

      @@ -70,7 +70,7 @@

      A few more recursive functions

      | otherwise = x:replicate' (n-1) x

      We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

      -
      Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.
      +
      Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.

      Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

       take' :: (Num i, Ord i) => i -> [a] -> [a]
      @@ -114,7 +114,7 @@ 

      A few more recursive functions

      Quick, sort!

      We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

      quickman -

      So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

      +

      So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

       quicksort :: (Ord a) => [a] -> [a]
       quicksort [] = []
      diff --git a/docs/starting-out.html b/docs/starting-out.html
      index 67377c3..640c947 100644
      --- a/docs/starting-out.html
      +++ b/docs/starting-out.html
      @@ -211,7 +211,7 @@ 

      An intro to lists

      Much like shopping lists in the real world, lists in Haskell are very useful. It’s the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this section we’ll look at the basics of lists, strings (which are lists) and list comprehensions.

      -In Haskell, lists are a homogenous data structure. They store several elements of the same type. That means that we can have a list of integers or a list of characters but we can’t have a list that has a few integers and then a few characters. And now, a list! +In Haskell, lists are a homogenous data structure. They store several elements of the same type. That means that we can have a list of integers or a list of characters but we can’t have a list that has a few integers and then a few characters. And now, a list!

       ghci> lostNumbers = [4,8,15,16,23,42]
      @@ -244,7 +244,7 @@ 

      An intro to lists

      [1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on.

      -

      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      +

      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      If you want to get an element out of a list by index, use !!. The indices start at 0.

       ghci> "Steve Buscemi" !! 6
      @@ -428,7 +428,7 @@ 

      I’m a list comprehension

       ghci> [ x | x <- [50..100], x `mod` 7 == 3]
       [52,59,66,73,80,87,94] 
      -

      Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let’s say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that’s less than 10 with "BOOM!". If a number isn’t odd, we throw it out of our list. For convenience, we’ll put that comprehension inside a function so we can easily reuse it.

      +

      Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let’s say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that’s less than 10 with "BOOM!". If a number isn’t odd, we throw it out of our list. For convenience, we’ll put that comprehension inside a function so we can easily reuse it.

       boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] 

      The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True.

      @@ -505,7 +505,7 @@

      Tuples

      11 ghci> snd ("Wow", False) False
      -
      Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.
      +
      Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.

      A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.

       ghci> zip [1,2,3,4,5] [5,5,5,5,5]
      diff --git a/docs/style.css b/docs/style.css
      index 5cbbee8..73773bb 100644
      --- a/docs/style.css
      +++ b/docs/style.css
      @@ -35,7 +35,7 @@ p {
       a:hover {
           text-decoration:none;
       }
      -em {
      +strong {
           font-style:normal;
           font-weight:bold;
       }
      diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html
      index 6e2be46..88fa5c4 100644
      --- a/docs/syntax-in-functions.html
      +++ b/docs/syntax-in-functions.html
      @@ -112,7 +112,7 @@ 

      Pattern matching

      Should a pattern match fail, it will just move on to the next element.

      Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list. But since [1,2,3] is just syntactic sugar for 1:2:3:[], you can also use the former pattern. A pattern like x:xs will bind the head of the list to x and the rest of it to xs, even if there’s only one element so xs ends up being an empty list.

      -
      Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.
      +
      Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.

      If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.

      Now that we know how to pattern match against list, let’s make our own implementation of the head function.

      @@ -179,7 +179,7 @@ 

      Guards, guards!

      Guards are indicated by pipes that follow a function’s name and its parameters. Usually, they’re indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that’s smaller than or equal to 1.2. Because it isn’t, it falls through to the next guard. The check is carried out with the second guard and because 24.3 is less than 1000.0, the second string is returned.

      This is very reminiscent of a big if else tree in imperative languages, only this is far better and more readable. While big if else trees are usually frowned upon, sometimes a problem is defined in such a discrete way that you can’t get around them. Guards are a very nice alternative for this.

      -

      Many times, the last guard is otherwise. otherwise is defined simply as otherwise = True and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to False (and we haven’t provided an otherwise catch-all guard), evaluation falls through to the next pattern. That’s how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.

      +

      Many times, the last guard is otherwise. otherwise is defined simply as otherwise = True and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to False (and we haven’t provided an otherwise catch-all guard), evaluation falls through to the next pattern. That’s how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.

      Of course we can use guards with functions that take as many parameters as we want. Instead of having the user calculate the density of the substance on their own before calling the function, let’s modify this function so that it takes a mass (in grams) and volume (in liters).

       densityTell :: (RealFloat a) => a -> a -> String
      @@ -220,7 +220,7 @@ 

      Guards, guards!

      ghci> 3 `myCompare` 2 GT
      -
      Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.
      +
      Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.

      Where!?

      In the previous section, we defined a density calculator function and responder like this:

      @@ -252,7 +252,7 @@ 

      Where!?

      The names we define in the where section of a function are only visible to that function, so we don’t have to worry about them polluting the namespace of other functions. Notice that all the names are aligned at a single column. If we don’t align them nice and proper, Haskell gets confused because then it doesn’t know they’re all part of the same block.

      where bindings aren’t shared across function bodies of different patterns. If you want several patterns of one function to access some shared name, you have to define it globally.

      -

      You can also use where bindings to pattern match! We could have rewritten the where section of our previous function as:

      +

      You can also use where bindings to pattern match! We could have rewritten the where section of our previous function as:

           ...
           where density = mass / volume
      diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html
      index 56e39f8..7948d3f 100644
      --- a/docs/types-and-typeclasses.html
      +++ b/docs/types-and-typeclasses.html
      @@ -120,7 +120,7 @@ 

      Type variables

      box -Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means that a can be of any type. This is much like generics in other languages, only in Haskell it’s much more powerful because it allows us to easily write very general functions if they don’t use any specific behavior of the types in them. Functions that have type variables are called polymorphic functions. The type declaration of head states that it takes a list of any type and returns one element of that type. +Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means that a can be of any type. This is much like generics in other languages, only in Haskell it’s much more powerful because it allows us to easily write very general functions if they don’t use any specific behavior of the types in them. Functions that have type variables are called polymorphic functions. The type declaration of head states that it takes a list of any type and returns one element of that type.

      Although type variables can have names longer than one character, we usually give them names of a, b, c, d …

      Remember fst? It returns the first component of a pair. Let’s examine its type.

      @@ -140,8 +140,8 @@

      Typeclasses 101

      ghci> :t (==) (==) :: (Eq a) => a -> a -> Bool
      -
      Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.
      -

      Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

      +
      Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.
      +

      Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

      The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.

      The elem function has a type of (Eq a) => a -> [a] -> Bool because it uses == over a list to check whether some value we’re looking for is in it.

      Some basic typeclasses:

      @@ -211,7 +211,7 @@

      Typeclasses 101

      read :: (Read a) => String -> a

      -See? It returns a type that’s part of Read but if we don’t try to use it in some way later, it has no way of knowing which type. That’s why we can use explicit type annotations. Type annotations are a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type. Observe: +See? It returns a type that’s part of Read but if we don’t try to use it in some way later, it has no way of knowing which type. That’s why we can use explicit type annotations. Type annotations are a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type. Observe:

       ghci> read "5" :: Int
      
      From acf0b8a2fbb1b15dbc3a9adae318614fafb5d9d6 Mon Sep 17 00:00:00 2001
      From: Gregory Cox 
      Date: Fri, 25 Nov 2022 23:26:01 +0900
      Subject: [PATCH 23/27] Change  tags to  in HTML
      
      ---
       docs/a-fistful-of-monads.html                 |   4 +-
       docs/assets/css/style.css                     |   3 +
       docs/functionally-solving-problems.html       |  18 +--
       ...tors-applicative-functors-and-monoids.html | 150 +++++++++---------
       docs/higher-order-functions.html              |  20 +--
       docs/input-and-output.html                    | 124 +++++++--------
       docs/introduction.html                        |   2 +-
       .../making-our-own-types-and-typeclasses.html |  46 +++---
       docs/modules.html                             |  14 +-
       docs/recursion.html                           |  10 +-
       docs/starting-out.html                        |  10 +-
       docs/style.css                                |   3 +
       docs/syntax-in-functions.html                 |  26 +--
       docs/types-and-typeclasses.html               |   6 +-
       docs/zippers.html                             |   6 +-
       15 files changed, 224 insertions(+), 218 deletions(-)
      
      diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html
      index 517e3f3..876379c 100644
      --- a/docs/a-fistful-of-monads.html
      +++ b/docs/a-fistful-of-monads.html
      @@ -101,7 +101,7 @@ 

      A Fistful of Monads

      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 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, @@ -152,7 +152,7 @@

      A Fistful of Monads

      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. +>>= function is pronounced as bind.

      diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index 73773bb..466bfc8 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -35,6 +35,9 @@ p { a:hover { text-decoration:none; } +em { + font-style:italic; +} strong { font-style:normal; font-weight:bold; diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index 5e83ff1..69339c4 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -35,7 +35,7 @@

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

      +

      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 on to 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 on to 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 a RPN expression, like "10 4 3 + 2 * -" and gives us back its result.

      @@ -157,11 +157,11 @@

      Heathrow to London

      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!

      +

      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.

      @@ -170,13 +170,13 @@

      Heathrow to London

      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.

      +

      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.

      +

      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)
      @@ -199,7 +199,7 @@ 

      Heathrow to London

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

      +

      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)
      diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html
      index 811ff34..8d9f158 100644
      --- a/docs/functors-applicative-functors-and-monoids.html
      +++ b/docs/functors-applicative-functors-and-monoids.html
      @@ -38,10 +38,10 @@ 

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

      +

      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.

      @@ -51,7 +51,7 @@ 

      Functors redux

      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. +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
      @@ -119,7 +119,7 @@ 

      Functors redux

      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:

      +

      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
      @@ -127,7 +127,7 @@ 

      Functors redux

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

      @@ -169,15 +169,15 @@

      Functors redux

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

      +

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

      +

      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
      @@ -221,7 +221,7 @@ 

      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!

      +

      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])
      @@ -249,9 +249,9 @@ 

      Applicative functors

      (<*>) :: 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.

      +

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

      +

      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 a 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
      @@ -367,8 +367,8 @@ 

      Applicative functors

      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.

      +

      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
      @@ -603,7 +603,7 @@ 

      The newtype keyword

      -So, what does this have to do with this newtype keyword? Well, think +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:

      @@ -624,13 +624,13 @@

      The newtype keyword

      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 +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 +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: @@ -641,10 +641,10 @@

      The newtype keyword

      -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 +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 +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 @@ -652,10 +652,10 @@

      The newtype keyword

      -So why not just use newtype all the time instead of data then? +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 +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:

      @@ -669,13 +669,13 @@

      The newtype keyword

      -When using newtype, you’re restricted to just one constructor with one +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 +We can also use the deriving keyword with newtype just like we +would with data. We can derive instances for Eq, Ord, Enum, @@ -684,7 +684,7 @@

      The newtype keyword

      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 +newtype just wraps an existing type. So now if we do the following, we can print and equate values of our new type:

      @@ -706,7 +706,7 @@

      The newtype keyword

      -In this particular newtype, the value constructor has the following type: +In this particular newtype, the value constructor has the following type:

      @@ -720,7 +720,7 @@ 

      The newtype keyword

      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 +was generated for us because we used record syntax in our newtype, has this type:

      @@ -782,7 +782,7 @@

      Using newtype to make type c 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 +can newtype our tuple in such a way that the second type parameter represents the type of the first component in the tuple:

      @@ -801,7 +801,7 @@

      Using newtype to make type c

      -As you can see, we can pattern match on types defined with newtype. We +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. @@ -840,12 +840,12 @@

      Using newtype to make type c

      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 +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 +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. +newtype faster, it’s also lazier. Let’s take a look at what this means.

      @@ -886,7 +886,7 @@

      On newtype laziness

      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 +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 @@ -911,7 +911,7 @@

      On newtype laziness

      Yikes! An exception! Now why did this exception happen? Types defined with -the data keyword can have multiple value constructors (even though +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 @@ -921,8 +921,8 @@

      On newtype laziness

      -Instead of using the data keyword for CoolBool, -let’s try using newtype: +Instead of using the data keyword for CoolBool, +let’s try using newtype:

      @@ -931,8 +931,8 @@ 

      On newtype laziness

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

      @@ -945,25 +945,25 @@

      On newtype laziness

      top of the mornin to ya!!!

      -It worked! Hmmm, why is that? Well, like we’ve said, when we use newtype, +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 +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 +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 +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 +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.

      @@ -971,7 +971,7 @@

      type vs.  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 +between type, data and newtype is, so let’s refresh our memory a bit.

      @@ -1012,8 +1012,8 @@

      type vs.  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: +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:

      @@ -1032,18 +1032,18 @@ 

      type vs. 

      -When we use record syntax in our newtype declarations, we get functions +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 +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 +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. +such a data declaration, consider using newtype.

      @@ -1057,8 +1057,8 @@

      type vs. newtype. And if you want to make -something completely new, odds are good that you’re looking for the data +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.

      @@ -1145,7 +1145,7 @@

      Monoids

      -We call this property associativity. * is +We call this property associativity. * is associative, and so is ++, but -, for example, is not. The expressions (5 - 3) - 4 and 5 - (3 - 4) @@ -1154,7 +1154,7 @@

      Monoids

      By noticing and writing down these properties, we have chanced upon -monoids! A monoid +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 @@ -1374,7 +1374,7 @@

      Product and Product and Monoid goes a little something like this:

      @@ -1410,7 +1410,7 @@

      Product and 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: +newtype wrapping and unwrapping:

      @@ -1450,16 +1450,16 @@ 

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

      @@ -1481,7 +1481,7 @@

      Any and All<

      The reason it’s called Any is because x `mappend` y will be True -if any one of those two is True. Even +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: @@ -1502,8 +1502,8 @@

      Any and All< 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 +the identity value. Logical and will return True only +if both of its parameters are True. This is the newtype declaration, nothing fancy:

      @@ -1525,7 +1525,7 @@

      Any and All<

      When we mappend values of the All type, the result will be -True only if all the values +True only if all the values used in the mappend operations are True:

      @@ -1543,7 +1543,7 @@

      Any and All<

      Just like with multiplication and addition, we usually explicitly state the -binary functions instead of wrapping them in newtypes and then using +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 @@ -1559,7 +1559,7 @@

      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: +less than, equal and greater than respectively:

      @@ -1811,7 +1811,7 @@ 

      Maybe the monoid

      We take a Maybe a and we wrap it with a -newtype. The Monoid instance is as follows: +newtype. The Monoid instance is as follows:

      @@ -1824,7 +1824,7 @@ 

      Maybe the monoid

      Just like we said. mempty is just a Nothing wrapped with the First -newtype constructor. If mappend’s 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 diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index a8a9e59..fa6e753 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -33,9 +33,9 @@

    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.

    +

    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:

    +

    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
    @@ -81,7 +81,7 @@ 

    Curried functions

    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?

    +

    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:
    @@ -231,7 +231,7 @@ 

    Maps and filters

    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.

    +

    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]
    @@ -267,7 +267,7 @@ 

    Maps and filters

    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:

    +

    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]))
    @@ -391,14 +391,14 @@ 

    Only folds and horses

    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:

    +

    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.

    +

    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.

    @@ -406,7 +406,7 @@ 

    Function application with $

    [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 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
    @@ -435,7 +435,7 @@ 

    Function composition

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

    +

    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
    @@ -448,7 +448,7 @@ 

    Function composition

     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.

    +

    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
    diff --git a/docs/input-and-output.html b/docs/input-and-output.html
    index 3b7b28e..37eb50d 100644
    --- a/docs/input-and-output.html
    +++ b/docs/input-and-output.html
    @@ -33,12 +33,12 @@
                 

    Input and Output

    poor dog -

    We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

    +

    We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

    While functions being unable to change state is good because it helps us reason about our programs, there’s one problem with that. If a function can’t change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

    Do not despair, all is not lost. It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen. With those two parts separated, we can still reason about our pure program and take advantage of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

    Hello, world!

    HELLO! -

    Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

    +

    Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

    Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

    So, for starters, punch in the following in your favorite text editor:

    @@ -67,23 +67,23 @@ 

    Hello, world!

    We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

    The empty tuple is a value of () and it also has a type of ().

    So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

    -

    Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

    +

    Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

     main = do
         putStrLn "Hello, what's your name?"
         name <- getLine
         putStrLn ("Hey " ++ name ++ ", you rock!")
     
    -

    Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that’s the type of the last I/O action inside.

    -

    Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don’t usually specify a type declaration for main.

    +

    Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that’s the type of the last I/O action inside.

    +

    Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don’t usually specify a type declaration for main.

    An interesting thing that we haven’t met before is the third line, which states name <- getLine. It looks like it reads a line from the input and stores it into a variable called name. Does it really? Well, let’s examine the type of getLine.

     ghci> :t getLine
     getLine :: IO String
     
    luggage -

    Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

    -

    When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

    +

    Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

    +

    When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

     main = do
         putStrLn "Hello, what's your name?"
    @@ -103,21 +103,21 @@ 

    Hello, world!

    name <- getLine putStrLn ("Hey " ++ name ++ ", you rock!")
    -

    However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

    -

    Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

    +

    However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

    +

    Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

    Beginners sometimes think that doing

     name = getLine
     

    will read from the input and then bind the value of that to name. Well, it won’t, all this does is give the getLine I/O action a different name called, well, name. Remember, to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-.

    -

    I/O actions will only be performed when they are given a name of main or when they’re inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they’ll be performed only if they eventually fall into main.

    +

    I/O actions will only be performed when they are given a name of main or when they’re inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they’ll be performed only if they eventually fall into main.

    Oh, right, there’s also one more case when I/O actions will be performed. When we type out an I/O action in GHCI and press return, it will be performed.

     ghci> putStrLn "HEEY"
     HEEY
     

    Even when we just punch out a number or call a function in GHCI and press return, it will evaluate it (as much as it needs) and then call show on it and then it will print that string to the terminal using putStrLn implicitly.

    -

    Remember let bindings? If you don’t, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn’t needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

    +

    Remember let bindings? If you don’t, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn’t needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

     import Data.Char
     
    @@ -130,8 +130,8 @@ 

    Hello, world!

    bigLastName = map toUpper lastName putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?"
    -

    See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

    -

    You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action. It’s a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we’d still have to run it through a <- to perform it.

    +

    See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

    +

    You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action. It’s a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we’d still have to run it through a <- to perform it.

    Now we’re going to make a program that continuously reads a line and prints out the same line with the words reversed. The program’s execution will stop when we input a blank line. This is the program:

     main = do
    @@ -148,16 +148,16 @@ 

    Hello, world!

    To get a feel of what it does, you can run it before we go over the code.

    Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

    First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

    -

    What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

    -

    Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

    +

    What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

    +

    Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

             else (do
                 putStrLn $ reverseWords line
                 main)
     
    -

    This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

    -

    Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

    -

    Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

    +

    This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

    +

    Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

    +

    Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

     main = do
         return ()
    @@ -174,15 +174,15 @@ 

    Hello, world!

    b <- return "yeah!" putStrLn $ a ++ " " ++ b
    -

    So you see, return is sort of the opposite to <-. While return takes a value and wraps it up in a box, <- takes a box (and performs it) and takes the value out of it, binding it to a name. But doing this is kind of redundant, especially since you can use let bindings in do blocks to bind to names, like so:

    +

    So you see, return is sort of the opposite to <-. While return takes a value and wraps it up in a box, <- takes a box (and performs it) and takes the value out of it, binding it to a name. But doing this is kind of redundant, especially since you can use let bindings in do blocks to bind to names, like so:

     main = do
         let a = "hell"
             b = "yeah"
         putStrLn $ a ++ " " ++ b
     
    -

    When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

    -
    A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.
    +

    When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

    +
    A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.

    Before we move on to files, let’s take a look at some functions that are useful when dealing with I/O.

    putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

    @@ -259,7 +259,7 @@ 

    Hello, world!

    hello

    The second line is the input. We input hello sir and then press return. Due to buffering, the execution of the program will begin only when after we’ve hit return and not after every inputted character. But once we press return, it acts on what we’ve been putting in so far. Try playing with this program to get a feel for it!

    -

    The when function is found in Control.Monad (to get access to it, do import Control.Monad). It’s interesting because in a do block it looks like a control flow statement, but it’s actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return (), action, so an I/O action that doesn’t do anything. Here’s how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

    +

    The when function is found in Control.Monad (to get access to it, do import Control.Monad). It’s interesting because in a do block it looks like a control flow statement, but it’s actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return (), action, so an I/O action that doesn’t do anything. Here’s how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

     import Control.Monad
     
    @@ -269,7 +269,7 @@ 

    Hello, world!

    putChar c main
    -

    So as you can see, it’s useful for encapsulating the if something then do some I/O action else return () pattern.

    +

    So as you can see, it’s useful for encapsulating the if something then do some I/O action else return () pattern.

    sequence takes a list of I/O actions and returns an I/O actions that will perform those actions one after the other. The result contained in that I/O action will be a list of the results of all the I/O actions that were performed. Its type signature is sequence :: [IO a] -> IO [a]. Doing this:

     main = do
    @@ -318,7 +318,7 @@ 

    Hello, world!

    l <- getLine putStrLn $ map toUpper l
    -

    forM (located in Control.Monad) is like mapM, only that it has its parameters switched around. The first parameter is the list and the second one is the function to map over that list, which is then sequenced. Why is that useful? Well, with some creative use of lambdas and do notation, we can do stuff like this:

    +

    forM (located in Control.Monad) is like mapM, only that it has its parameters switched around. The first parameter is the list and the second one is the function to map over that list, which is then sequenced. Why is that useful? Well, with some creative use of lambdas and do notation, we can do stuff like this:

     import Control.Monad
     
    @@ -330,7 +330,7 @@ 

    Hello, world!

    putStrLn "The colors that you associate with 1, 2, 3 and 4 are: " mapM putStrLn colors
    -

    The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn’t have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it’s the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

    +

    The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn’t have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it’s the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

    You can think of forM as meaning: make an I/O action for every element in this list. What each I/O action will do can depend on the element that was used to make the action. Finally, perform those actions and bind their results to something. We don’t have to bind it, we can also just throw it away.

     $ runhaskell form_test.hs
    @@ -348,12 +348,12 @@ 

    Hello, world!

    red orange
    -

    We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

    +

    We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

    In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

    Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

    Files and streams

    streams -

    getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

    +

    getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

    getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

     I'm a lil' teapot
    @@ -371,7 +371,7 @@ 

    Files and streams

    l <- getLine putStrLn $ map toUpper l
    -

    We’ll save that program as capslocker.hs or something and compile it. And then, we’re going to use a unix pipe to feed our text file directly to our little program. We’re going to use the help of the GNU cat program, which prints out a file that’s given to it as an argument. Check it out, booyaka!

    +

    We’ll save that program as capslocker.hs or something and compile it. And then, we’re going to use a unix pipe to feed our text file directly to our little program. We’re going to use the help of the GNU cat program, which prints out a file that’s given to it as an argument. Check it out, booyaka!

     $ ghc --make capslocker
     [1 of 1] Compiling Main             ( capslocker.hs, capslocker.o )
    @@ -386,7 +386,7 @@ 

    Files and streams

    IT'S SO SMALL, TASTELESS capslocker <stdin>: hGetLine: end of file
    -

    As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

    +

    As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

    So what we’re essentially doing with that use of forever is taking the input and transforming it into some output. That’s why we can use getContents to make our program even shorter and better:

     import Data.Char
    @@ -402,7 +402,7 @@ 

    Files and streams

    WHAT'S WITH THAT AIRPLANE FOOD, HUH? IT'S SO SMALL, TASTELESS
    -

    Cool, it works. What if we just run capslocker and try to type in the lines ourselves?

    +

    Cool, it works. What if we just run capslocker and try to type in the lines ourselves?

     $ ./capslocker
     hey ho
    @@ -410,7 +410,7 @@ 

    Files and streams

    lets go LETS GO
    -

    We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it’s not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that’s also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: “Hey, I need a capslocked line!”. It doesn’t have any lines yet, so it says to contents: “Hey, how about actually getting a line from the terminal?”. So that’s when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: “Hey, I need the next line, come on!” and this repeats until there’s no more input, which is signified by an end-of-file character.

    +

    We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it’s not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that’s also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: “Hey, I need a capslocked line!”. It doesn’t have any lines yet, so it says to contents: “Hey, how about actually getting a line from the terminal?”. So that’s when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: “Hey, I need the next line, come on!” and this repeats until there’s no more input, which is signified by an end-of-file character.

    Let’s make program that takes some input and prints out only those lines that are shorter than 10 characters. Observe:

     main = do
    @@ -444,7 +444,7 @@ 

    Files and streams

    so am i short
    -

    We pipe the contents of shortlines.txt into the output of shortlinesonly and as the output, we only get the short lines.

    +

    We pipe the contents of shortlines.txt into the output of shortlinesonly and as the output, we only get the short lines.

    This pattern of getting some string from the input, transforming it with a function and then outputting that is so common that there exists a function which makes that even easier, called interact. interact takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it and then print out the function’s result. Let’s modify our program to use that.

     main = interact shortLinesOnly
    @@ -505,8 +505,8 @@ 

    Files and streams

    Again, we get the same output as if we had run our program and put in the words ourselves at the standard input. We just don’t see the input that palindromes.hs because the input came from the file and not from us typing the words in.

    So now you probably see how lazy I/O works and how we can use it to our advantage. You can just think in terms of what the output is supposed to be for some given input and write a function to do that transformation. In lazy I/O, nothing is eaten from the input until it absolutely has to be because what we want to print right now depends on that input.

    -

    So far, we’ve worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we’ve already been doing that. One way to think about reading from the terminal is to imagine that it’s like reading from a (somewhat special) file. Same goes for writing to the terminal, it’s kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we’ll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

    -

    We’ll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne’s #1 hit Girlfriend, and just prints it out to the terminal. Here’s girlfriend.txt:

    +

    So far, we’ve worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we’ve already been doing that. One way to think about reading from the terminal is to imagine that it’s like reading from a (somewhat special) file. Same goes for writing to the terminal, it’s kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we’ll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

    +

    We’ll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne’s #1 hit Girlfriend, and just prints it out to the terminal. Here’s girlfriend.txt:

     Hey! Hey! You! You!
     I don't like your girlfriend!
    @@ -532,7 +532,7 @@ 

    Files and streams

    I think you need a new one!

    Let’s go over this line by line. The first line is just four exclamations, to get our attention. In the second line, Avril tells us that she doesn’t like our current romantic partner. The third line serves to emphasize that disapproval, whereas the fourth line suggests we should seek out a new girlfriend.

    -

    Let’s also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file’s associated handle encapsulated as its result.

    +

    Let’s also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file’s associated handle encapsulated as its result.

    FilePath is just a type synonym for String, simply defined as:

     type FilePath = String
    @@ -566,8 +566,8 @@ 

    Files and streams

    return result
    butter toast -

    We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

    -

    Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

    +

    We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

    +

    Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

    Loading files and then treating their contents as strings is so common that we have these three nice little functions to make our work even easier:

    readFile has a type signature of readFile :: FilePath -> IO String. @@ -588,7 +588,7 @@

    Files and streams

    writeFile has a type of writeFile :: FilePath -> String -> IO (). -It takes a path to a file and a string to write to that file and returns an I/O action that will do the writing. If such a file already exists, it will be stomped down to zero length before being written on. Here’s how to turn girlfriend.txt into a CAPSLOCKED version and write it to girlfriendcaps.txt: +It takes a path to a file and a string to write to that file and returns an I/O action that will do the writing. If such a file already exists, it will be stomped down to zero length before being written on. Here’s how to turn girlfriend.txt into a CAPSLOCKED version and write it to girlfriendcaps.txt:

     import System.IO
    @@ -607,7 +607,7 @@ 

    Files and streams

    I THINK YOU NEED A NEW ONE!

    appendFile has a type signature that’s just like writeFile, only appendFile doesn’t truncate the file to zero length if it already exists but it appends stuff to it.

    -

    Let’s say we have a file todo.txt that has one task per line that we have to do. Now let’s make a program that takes a line from the standard input and adds that to our to-do list.

    +

    Let’s say we have a file todo.txt that has one task per line that we have to do. Now let’s make a program that takes a line from the standard input and adds that to our to-do list.

     import System.IO
     
    @@ -648,8 +648,8 @@ 

    Files and streams

    Reading files in bigger chunks can help if we want to minimize disk access or when our file is actually a slow network resource.

    We can also use hFlush, which is a function that takes a handle and returns an I/O action that will flush the buffer of the file associated with the handle. When we’re doing line-buffering, the buffer is flushed after every line. When we’re doing block-buffering, it’s after we’ve read a chunk. It’s also flushed after closing a handle. That means that when we’ve reached a newline character, the reading (or writing) mechanism reports all the data so far. But we can use hFlush to force that reporting of data that has been read so far. After flushing, the data is available to other programs that are running at the same time.

    Think of reading a block-buffered file like this: your toilet bowl is set to flush itself after it has one gallon of water inside it. So you start pouring in water and once the gallon mark is reached, that water is automatically flushed and the data in the water that you’ve poured in so far is read. But you can flush the toilet manually too by pressing the button on the toilet. This makes the toilet flush and all the water (data) inside the toilet is read. In case you haven’t noticed, flushing the toilet manually is a metaphor for hFlush. This is not a very great analogy by programming analogy standards, but I wanted a real world object that can be flushed for the punchline.

    -

    We already made a program to add a new item to our to-do list in todo.txt, now let’s make a program to remove an item. I’ll just paste the code and then we’ll go over the program together so you see that it’s really easy. We’ll be using a few new functions from System.Directory and one new function from System.IO, but they’ll all be explained.

    -

    Anyway, here’s the program for removing an item from todo.txt:

    +

    We already made a program to add a new item to our to-do list in todo.txt, now let’s make a program to remove an item. I’ll just paste the code and then we’ll go over the program together so you see that it’s really easy. We’ll be using a few new functions from System.Directory and one new function from System.IO, but they’ll all be explained.

    +

    Anyway, here’s the program for removing an item from todo.txt:

     import System.IO
     import System.Directory
    @@ -673,14 +673,14 @@ 

    Files and streams

    removeFile "todo.txt" renameFile tempName "todo.txt"
    -

    At first, we just open todo.txt in read mode and bind its handle to handle.

    -

    Next up, we use a function that we haven’t met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it’s better practice to use openTempFile so you know you’re probably not overwriting anything. +

    At first, we just open todo.txt in read mode and bind its handle to handle.

    +

    Next up, we use a function that we haven’t met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it’s better practice to use openTempFile so you know you’re probably not overwriting anything.

    The reason we didn’t use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

    -

    Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

    +

    Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

    We ask the user which one they want to delete and wait for them to enter a number. Let’s say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

    Remember the delete and !! functions from Data.List. !! returns an element from a list with some index and delete deletes the first occurrence of an element in a list and returns a new list without that occurrence. (todoTasks !! number) (number is now 1) returns "Dust the dog". We bind todoTasks without the first occurrence of "Dust the dog" to newTodoItems and then join that into a single string with unlines before writing it to the temporary file that we opened. The old file is now unchanged and the temporary file contains all the lines that the old one does, except the one we deleted.

    -

    After that we close both the original and the temporary files and then we remove the original one with removeFile, which, as you can see, takes a path to a file and deletes it. After deleting the old todo.txt, we use renameFile to rename the temporary file to todo.txt. Be careful, removeFile and renameFile (which are both in System.Directory by the way) take file paths as their parameters, not handles.

    +

    After that we close both the original and the temporary files and then we remove the original one with removeFile, which, as you can see, takes a path to a file and deletes it. After deleting the old todo.txt, we use renameFile to rename the temporary file to todo.txt. Be careful, removeFile and renameFile (which are both in System.Directory by the way) take file paths as their parameters, not handles.

    And that’s that! We could have done this in even fewer lines, but we were very careful not to overwrite any existing files and politely asked the operating system to tell us where we can put our temporary file. Let’s give this a go!

     $ runhaskell deletetodo.hs
    @@ -708,7 +708,7 @@ 

    Files and streams

    Command line arguments

    COMMAND LINE ARGUMENTS!!! ARGH

    Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line arguments of a program.

    -

    In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

    +

    In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

    One way to solve that is to always ask the user which file they want to use as their to-do list. We used that approach when we wanted to know which item the user wants to delete. It works, but it’s not so good, because it requires the user to run the program, wait for the program to ask something and then tell that to the program. That’s called an interactive program and the difficult bit with interactive command line programs is this — what if you want to automate the execution of that program, like with a batch script? It’s harder to make a batch script that interacts with a program than a batch script that just calls one program or several of them.

    That’s why it’s sometimes better to have the user tell the program what they want when they run the program, instead of having the program ask the user once it’s run. And what better way to have the user tell the program what they want it to do when they run it than via command line arguments!

    The System.Environment module has two cool I/O actions. One is getArgs, which has a type of getArgs :: IO [String] and is an I/O action that will get the arguments that the program was run with and have as its contained result a list with the arguments. getProgName has a type of getProgName :: IO String and is an I/O action that contains the program name.

    @@ -736,15 +736,15 @@

    Command line arguments

    The program name is: arg-test
    -

    Nice. Armed with this knowledge you could create some cool command line apps. In fact, let’s go ahead and make one. In the previous section, we made a separate program for adding tasks and a separate program for deleting them. Now, we’re going to join that into one program, what it does will depend on the command line arguments. We’re also going to make it so it can operate on different files, not just todo.txt.

    -

    We’ll call it simply todo and it’ll be able to do (haha!) three different things:

    +

    Nice. Armed with this knowledge you could create some cool command line apps. In fact, let’s go ahead and make one. In the previous section, we made a separate program for adding tasks and a separate program for deleting them. Now, we’re going to join that into one program, what it does will depend on the command line arguments. We’re also going to make it so it can operate on different files, not just todo.txt.

    +

    We’ll call it simply todo and it’ll be able to do (haha!) three different things:

    • View tasks
    • Add tasks
    • Delete tasks

    We’re not going to concern ourselves with possible bad input too much right now.

    -

    Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we’ll just do todo view todo.txt and to remove the task with the index of 2, we’ll do todo remove todo.txt 2.

    +

    Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we’ll just do todo view todo.txt and to remove the task with the index of 2, we’ll do todo remove todo.txt 2.

    We’ll start by making a dispatch association list. It’s going to be a simple association list that has command line arguments as keys and functions as their corresponding values. All these functions will be of type [String] -> IO (). They’re going to take the argument list as a parameter and return an I/O action that does the viewing, adding, deleting, etc.

     import System.Environment
    @@ -767,7 +767,7 @@ 

    Command line arguments

    First, we get the arguments and bind them to (command:args). If you remember your pattern matching, this means that the first argument will get bound to command and the rest of them will get bound to args. If we call our program like todo add todo.txt "Spank the monkey", command will be "add" and args will be ["todo.txt", "Spank the monkey"].

    In the next line, we look up our command in the dispatch list. Because "add" points to add, we get Just add as a result. We use pattern matching again to extract our function out of the Maybe. What happens if our command isn’t in the dispatch list? Well then the lookup will return Nothing, but we said we won’t concern ourselves with failing gracefully too much, so the pattern matching will fail and our program will throw a fit.

    -

    Finally, we call our action function with the rest of the argument list. That will return an I/O action that either adds an item, displays a list of items or deletes an item and because that action is part of the main do block, it will get performed. If we follow our concrete example so far and our action function is add, it will get called with args (so ["todo.txt", "Spank the monkey"]) and return an I/O action that adds Spank the monkey to todo.txt.

    +

    Finally, we call our action function with the rest of the argument list. That will return an I/O action that either adds an item, displays a list of items or deletes an item and because that action is part of the main do block, it will get performed. If we follow our concrete example so far and our action function is add, it will get called with args (so ["todo.txt", "Spank the monkey"]) and return an I/O action that adds Spank the monkey to todo.txt.

    Great! All that’s left now is to implement add, view and remove. Let’s start with add:

     add :: [String] -> IO ()
    @@ -784,7 +784,7 @@ 

    Command line arguments

    putStr $ unlines numberedTasks

    We already did pretty much the same thing in the program that only deleted tasks when we were displaying the tasks so that the user can choose one for deletion, only here we just display the tasks.

    -

    And finally, we’re going to implement remove. It’s going to be very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, check out the explanation under that program. The main difference is that we’re not hardcoding todo.txt but getting it as an argument. We’re also not prompting the user for the task number to delete, we’re getting it as an argument.

    +

    And finally, we’re going to implement remove. It’s going to be very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, check out the explanation under that program. The main difference is that we’re not hardcoding todo.txt but getting it as an argument. We’re also not prompting the user for the task number to delete, we’re getting it as an argument.

     remove :: [String] -> IO ()
     remove [fileName, numberString] = do
    @@ -963,7 +963,7 @@ 

    Randomness

    (restOfList, finalGen) = finiteRandoms (n-1) newGen in (value:restOfList, finalGen)
    -

    Again, a recursive definition. We say that if we want 0 numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

    +

    Again, a recursive definition. We say that if we want 0 numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

    What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it’s kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

     ghci> randomR (1,6) (mkStdGen 359353)
    @@ -1052,7 +1052,7 @@ 

    Randomness

    askForNumber newGen
    jack of diamonds -

    We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

    +

    We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

    Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

    We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

    main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

    @@ -1094,7 +1094,7 @@

    Bytestrings

    Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

    However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

    That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

    -

    Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

    +

    Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

    The other variety of bytestrings resides in Data.ByteString.Lazy. They’re lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That’s what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it’s just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won’t cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU’s L2 cache.

    If you look through the documentation for Data.ByteString.Lazy, you’ll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we’re going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

    @@ -1167,7 +1167,7 @@ 

    Exceptions

    *** Exception: Prelude.head: empty list
    Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail. -

    Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

    +

    Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

    Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can’t do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don’t know when they will be evaluated and that it really shouldn’t matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That’s why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that’s bad, because we want to keep the I/O part as small as possible. However, if we don’t catch them in the I/O part of our code, our program crashes. The solution? Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful type system and use types like Either and Maybe to represent results that may have failed.

    That’s why we’ll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that’s part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

    @@ -1196,12 +1196,12 @@ 

    Exceptions

    putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!" else do putStrLn "The file doesn't exist!"
    -

    We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

    +

    We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

    Another solution here would be to use exceptions. It’s perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

    To deal with this by using exceptions, we’re going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

    non sequitor -

    If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

    -

    The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

    +

    If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

    +

    The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

    So let’s put our new friend catch to use!

     import System.Environment
    @@ -1245,8 +1245,8 @@ 

    Exceptions

    | isDoesNotExistError e = putStrLn "The file doesn't exist!" | otherwise = ioError e
    -

    Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

    -

    So if the exception thrown in the toTry I/O action that we glued together with a do block isn’t caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

    +

    Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

    +

    So if the exception thrown in the toTry I/O action that we glued together with a do block isn’t caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

    There are several predicates that act on IOError and if a guard doesn’t evaluate to True, evaluation falls through to the next guard. The predicates that act on IOError are:

    • isAlreadyExistsError
    • @@ -1289,14 +1289,14 @@

      Exceptions

      Nothing -> putStrLn "Whoops! File does not exist at unknown location!" | otherwise = ioError e
    -

    In the guard where isDoesNotExistError is True, we used a case expression to call ioeGetFileName with e and then pattern match against the Maybe value that it returned. Using case expressions is commonly used when you want to pattern match against something without bringing in a new function.

    +

    In the guard where isDoesNotExistError is True, we used a case expression to call ioeGetFileName with e and then pattern match against the Maybe value that it returned. Using case expressions is commonly used when you want to pattern match against something without bringing in a new function.

    You don’t have to use one handler to catch exceptions in your whole I/O part. You can just cover certain parts of your I/O code with catch or you can cover several of them with catch and use different handlers for them, like so:

     main = do toTry `catch` handler1
               thenTryThis `catch` handler2
               launchRockets
     
    -

    Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn’t a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

    +

    Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn’t a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

    Now you know how to deal with I/O exceptions! Throwing exceptions from pure code and dealing with them hasn’t been covered here, mainly because, like we said, Haskell offers much better ways to indicate errors than reverting to I/O to catch them. Even when glueing together I/O actions that might fail, I prefer to have their type be something like IO (Either a b), meaning that they’re normal I/O actions but the result that they yield when performed is of type Either a b, meaning it’s either Left a or Right b.

      diff --git a/docs/introduction.html b/docs/introduction.html index d45dcb2..50cab3d 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -54,7 +54,7 @@

      So what’s Haskell?

      fx Haskell is a purely functional programming language. -In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together. +In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together.

      lazy diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index fcd17c3..cf3a86a 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -38,7 +38,7 @@

      Algebraic data types intro

       data Bool = False | True
       
      -

      data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

      +

      data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

      In a similar fashion, we can think of the Int type as being defined like this:

       data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
      @@ -76,7 +76,7 @@ 

      Algebraic data types intro

       data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)
       
      -

      We won’t concern ourselves with deriving too much for now. Let’s just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

      +

      We won’t concern ourselves with deriving too much for now. Let’s just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

       ghci> Circle 10 20 5
       Circle 10.0 20.0 5.0
      @@ -290,12 +290,12 @@ 

      Type parameters

      meekrat

      In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We usually use type parameters when the type that’s contained inside the data type’s various value constructors isn’t really that important for the type to work. A list of stuff is a list of stuff and it doesn’t matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn’t matter what the type of that something is.

      -

      Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

      +

      Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

       data (Ord k) => Map k v = ...
       
      -

      However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

      -

      So don’t put type constraints into data declarations even if it seems to make sense, because you’ll have to put them into the function type declarations either way.

      +

      However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

      +

      So don’t put type constraints into data declarations even if it seems to make sense, because you’ll have to put them into the function type declarations either way.

      Let’s implement a 3D vector type and add some operations for it. We’ll be using a parameterized type because even though it will usually contain numeric types, it will still support several of them.

       data Vector a = Vector a a a deriving (Show)
      @@ -309,7 +309,7 @@ 

      Type parameters

      scalarMult :: (Num t) => Vector t -> Vector t -> t (Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n
      -

      vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

      +

      vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

      Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

       ghci> Vector 3 5 8 `vplus` Vector 9 2 8
      @@ -327,7 +327,7 @@ 

      Derived instances

      gob

      In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

      We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

      -

      In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

      +

      In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

      Consider this data type:

       data Person = Person { firstName :: String
      @@ -479,7 +479,7 @@ 

      Type synonyms

      chicken

      - We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a synonym for an already existing type. + We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a synonym for an already existing type.

      If we make a function that converts a string to uppercase and call it toUpperString or something, we can give it a type declaration of toUpperString :: [Char] -> [Char] or toUpperString :: String -> String. Both of these are essentially the same, only the latter is nicer to read.

      @@ -517,7 +517,7 @@

      Type synonyms

      type AssocList k v = [(k,v)]

      Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

      -
      Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!
      +
      Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

      Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

       type IntMap v = Map Int v
      @@ -528,7 +528,7 @@ 

      Type synonyms

      Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

      Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.
      -

      Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

      +

      Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

      Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

       data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
      @@ -603,7 +603,7 @@ 

      Recursive data structures

       data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)
       
      -

      You might also be confused about the Cons constructor here. cons is another word for :. You see, in lists, : is actually a constructor that takes a value and another list and returns a list. We can already use our new list type! In other words, it has two fields. One field is of the type of a and the other is of the type [a].

      +

      You might also be confused about the Cons constructor here. cons is another word for :. You see, in lists, : is actually a constructor that takes a value and another list and returns a list. We can already use our new list type! In other words, it has two fields. One field is of the type of a and the other is of the type [a].

       ghci> Empty
       Empty
      @@ -720,7 +720,7 @@ 

      Typeclasses 102

      Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

      Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.
      -

      Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

      +

      Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

      If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.

      So once we have a class, what can we do with it? Well, not much, really. But once we start making types instances of that class, we start getting some nice functionality. So check out this type:

      @@ -734,8 +734,8 @@ 

      Typeclasses 102

      Yellow == Yellow = True _ == _ = False
      -

      We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we’re making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

      -

      Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That’s called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

      +

      We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we’re making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

      +

      Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That’s called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

       class Eq a where
           (==) :: a -> a -> Bool
      @@ -762,11 +762,11 @@ 

      Typeclasses 102

      [Red light,Yellow light,Green light]

      Nice. We could have just derived Eq and it would have had the same effect (but we didn’t for educational purposes). However, deriving Show would have just directly translated the value constructors to strings. But if we want lights to appear like "Red light", then we have to make the instance declaration by hand.

      -

      You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here’s the first part:

      +

      You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here’s the first part:

       class (Eq a) => Num a where
          ...  
      -

      As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We’re essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That’s all there is to subclassing really, it’s just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

      +

      As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We’re essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That’s all there is to subclassing really, it’s just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

      But how are the Maybe or list types made as instances of typeclasses? What makes Maybe different from, say, TrafficLight is that Maybe in itself isn’t a concrete type, it’s a type constructor that takes one type parameter (like Char or something) to produce a concrete type (like Maybe Char). Let’s take a look at the Eq typeclass again:

       class Eq a where
      @@ -786,22 +786,22 @@ 

      Typeclasses 102

      _ == _ = False

      This is like saying that we want to make all types of the form Maybe something an instance of Eq. We actually could have written (Maybe something), but we usually opt for single letters to be true to the Haskell style. The (Maybe m) here plays the role of the a from class Eq a where. While Maybe isn’t a concrete type, Maybe m is. By specifying a type parameter (m, which is in lowercase), we said that we want all types that are in the form of Maybe m, where m is any type, to be an instance of Eq.

      -

      There’s one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That’s why we have to modify our instance declaration like this:

      +

      There’s one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That’s why we have to modify our instance declaration like this:

       instance (Eq m) => Eq (Maybe m) where
           Just x == Just y = x == y
           Nothing == Nothing = True
           _ == _ = False
             
      -

      We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

      -

      Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

      +

      We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

      +

      Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

      When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

      -
      Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.
      +
      Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

      Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

      A yes-no typeclass

      yesno

      In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

      -

      Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

      +

      Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

       class YesNo a where
           yesno :: a -> Bool
      @@ -963,7 +963,7 @@ 

      Kinds and some type-foo

      ghci> :k Int Int :: *
      -

      A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn’t take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven’t had to do that so far), I’d say star or just type.

      +

      A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn’t take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven’t had to do that so far), I’d say star or just type.

      Okay, now let’s see what the kind of Maybe is.

       ghci> :k Maybe
      @@ -1029,7 +1029,7 @@ 

      Kinds and some type-foo

       data Barry t k p = Barry { yabba :: p, dabba :: t k }
       
      -

      And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn’t look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it’s going to be something -> something -> something -> *. It’s safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let’s just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let’s check that with GHCI.

      +

      And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn’t look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it’s going to be something -> something -> something -> *. It’s safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let’s just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let’s check that with GHCI.

       ghci> :k Barry
       Barry :: (* -> *) -> * -> * -> *
      diff --git a/docs/modules.html b/docs/modules.html
      index c1cbb26..c1318cd 100644
      --- a/docs/modules.html
      +++ b/docs/modules.html
      @@ -95,7 +95,7 @@ 

      Data.List

      ghci> transpose ["hey","there","folks"] ["htg","ehu","yey","rs","e"]
      -

      Say we have the polynomials 3x2 + 5x + 9, 10x3 + 9 and 8x3 + 5x2 + x - 1 and we want to add them together. We can use the lists [0,3,5,9], [10,0,0,9] and [8,5,1,-1] to represent them in Haskell. Now, to add them, all we have to do is this:

      +

      Say we have the polynomials 3x2 + 5x + 9, 10x3 + 9 and 8x3 + 5x2 + x - 1 and we want to add them together. We can use the lists [0,3,5,9], [10,0,0,9] and [8,5,1,-1] to represent them in Haskell. Now, to add them, all we have to do is this:

       ghci> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]
       [18,8,6,17]
      @@ -382,19 +382,19 @@ 

      Data.List

      [1,2,3,4,3,2,1]

      What length, take, drop, splitAt, !! and replicate have in common is that they take an Int as one of their parameters (or return an Int), even though they could be more generic and usable if they just took any type that’s part of the Integral or Num typeclasses (depending on the functions). They do that for historical reasons. However, fixing that would probably break a lot of existing code. That’s why Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, length has a type signature of length :: [a] -> Int. If we try to get the average of a list of numbers by doing let xs = [1..6] in sum xs / length xs, we get a type error, because you can’t use / with an Int. genericLength, on the other hand, has a type signature of genericLength :: (Num a) => [b] -> a. Because a Num can act like a floating point number, getting the average by doing let xs = [1..6] in sum xs / genericLength xs works out just fine.

      -

      The nub, delete, union, intersect and group functions all have their more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is that the first set of functions use == to test for equality, whereas the By ones also take an equality function and then compare them by using that equality function. group is the same as groupBy (==).

      -

      For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That’s where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

      +

      The nub, delete, union, intersect and group functions all have their more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is that the first set of functions use == to test for equality, whereas the By ones also take an equality function and then compare them by using that equality function. group is the same as groupBy (==).

      +

      For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That’s where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

       ghci> let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
       ghci> groupBy (\x y -> (x > 0) == (y > 0)) values
       [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
       
      -

      From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they’re both negative or if they’re both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

      +

      From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they’re both negative or if they’re both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

       on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
       f `on` g = \x y -> f (g x) (g y)
       
      -

      So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do:

      +

      So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do:

       ghci> groupBy ((==) `on` (> 0)) values
       [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
      @@ -407,7 +407,7 @@ 

      Data.List

      ghci> sortBy (compare `on` length) xs [[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]
      -

      Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

      +

      Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

      Data.Char

      lego char

      The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

      @@ -797,7 +797,7 @@

      Making our own modules

      making modules

      We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it’s good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

      Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

      -

      We say that a module exports functions. What that means is that when I import a module, I can use the functions that it exports. It can define functions that its functions call internally, but we can only see and use the ones that it exports.

      +

      We say that a module exports functions. What that means is that when I import a module, I can use the functions that it exports. It can define functions that its functions call internally, but we can only see and use the ones that it exports.

      At the beginning of a module, we specify the module name. If we have a file called Geometry.hs, then we should name our module Geometry. Then, we specify the functions that it exports and after that, we can start writing the functions. So we’ll start with this.

       module Geometry
      diff --git a/docs/recursion.html b/docs/recursion.html
      index b14c5a3..b683b79 100644
      --- a/docs/recursion.html
      +++ b/docs/recursion.html
      @@ -35,8 +35,8 @@ 

      Recursion

      Hello recursion!

      SOVIET RUSSIA

      We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

      -

      If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

      -

      Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

      +

      If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

      +

      Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

      Maximum awesome

      The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

      Now let’s see how we’d define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it’s the maximum of the tail. That’s it! Now let’s implement that in Haskell.

      @@ -50,8 +50,8 @@

      Maximum awesome

      where maxTail = maximum' xs

      As you can see, pattern matching goes great with recursion! Most imperative languages don’t have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what’s the maximum of an empty list? I don’t know. The second pattern also lays out an edge condition. It says that if it’s the singleton list, just give back the only element.

      -

      Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

      -

      Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

      +

      Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

      +

      Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

      An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here’s how we could rewrite maximum' by using max:

       maximum' :: (Ord a) => [a] -> a
      @@ -114,7 +114,7 @@ 

      A few more recursive functions

      Quick, sort!

      We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

      quickman -

      So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

      +

      So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

       quicksort :: (Ord a) => [a] -> [a]
       quicksort [] = []
      diff --git a/docs/starting-out.html b/docs/starting-out.html
      index 640c947..c64a33e 100644
      --- a/docs/starting-out.html
      +++ b/docs/starting-out.html
      @@ -71,7 +71,7 @@ 

      Ready, set, go!

      Pretty cool, huh? Yeah, I know it’s not but bear with me. A little pitfall to watch out for here is negating numbers. If we want to have a negative number, it’s always best to surround it with parentheses. Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine.

      -Boolean algebra is also pretty straightforward. As you probably know, && means a boolean and, || means a boolean or. not negates a True or a False. +Boolean algebra is also pretty straightforward. As you probably know, && means a boolean and, || means a boolean or. not negates a True or a False.

       ghci> True && False
      @@ -110,7 +110,7 @@ 

      Ready, set, go!

      Yikes! What GHCI is telling us here is that "llama" is not a number and so it doesn’t know how to add it to 5. Even if it wasn’t "llama" but "four" or "4", Haskell still wouldn’t consider it to be a number. + expects its left and right side to be numbers. If we tried to do True == 5, GHCI would tell us that the types don’t match. Whereas + works only on things that are considered numbers, == works on any two things that can be compared. But the catch is that they both have to be the same type of thing. You can’t compare apples and oranges. We’ll take a closer look at types a bit later. Note: you can do 5 + 4.0 because 5 is sneaky and can act like an integer or a floating-point number. 4.0 can’t act like an integer, so 5 is the one that has to adapt.

      -You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them. +You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them.

      phoen @@ -192,7 +192,7 @@

      Baby’s first functions

      else x*2
      this is you

      -Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this. +Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this.

       doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
      @@ -203,7 +203,7 @@ 

      Baby’s first functions

       conanO'Brien = "It's a-me, Conan O'Brien!" 

      -There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably. +There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably.

      An intro to lists

      @@ -411,7 +411,7 @@

      Texas ranges

      I’m a list comprehension

      frog -If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate. +If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate.

      If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn’t want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We’ll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here’s that comprehension in action.

      diff --git a/docs/style.css b/docs/style.css
      index 73773bb..466bfc8 100644
      --- a/docs/style.css
      +++ b/docs/style.css
      @@ -35,6 +35,9 @@ p {
       a:hover {
           text-decoration:none;
       }
      +em {
      +    font-style:italic;
      +}
       strong {
           font-style:normal;
           font-weight:bold;
      diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html
      index 88fa5c4..58d5c49 100644
      --- a/docs/syntax-in-functions.html
      +++ b/docs/syntax-in-functions.html
      @@ -51,7 +51,7 @@ 

      Pattern matching

      sayMe x = "Not between 1 and 5"

      Note that if we moved the last pattern (the catch-all one) to the top, it would always say "Not between 1 and 5", because it would catch all the numbers and they wouldn’t have a chance to fall through and be checked for any other patterns.

      -

      Remember the factorial function we implemented previously? We defined the factorial of a number n as product [1..n]. We can also define a factorial function recursively, the way it is usually defined in mathematics. We start by saying that the factorial of 0 is 1. Then we state that the factorial of any positive integer is that integer multiplied by the factorial of its predecessor. Here’s how that looks like translated in Haskell terms.

      +

      Remember the factorial function we implemented previously? We defined the factorial of a number n as product [1..n]. We can also define a factorial function recursively, the way it is usually defined in mathematics. We start by saying that the factorial of 0 is 1. Then we state that the factorial of any positive integer is that integer multiplied by the factorial of its predecessor. Here’s how that looks like translated in Haskell terms.

       factorial :: (Integral a) => a -> a
       factorial 0 = 1
      @@ -152,7 +152,7 @@ 

      Pattern matching

      sum' [] = 0 sum' (x:xs) = x + sum' xs
      -

      There’s also a thing called as patterns. Those are a handy way of breaking something up according to a pattern and binding it to names whilst still keeping a reference to the whole thing. You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again. Here’s a quick and dirty example:

      +

      There’s also a thing called as patterns. Those are a handy way of breaking something up according to a pattern and binding it to names whilst still keeping a reference to the whole thing. You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again. Here’s a quick and dirty example:

       capital :: String -> String
       capital "" = "Empty string, whoops!"
      @@ -251,7 +251,7 @@ 

      Where!?

      water = 1000.0

      The names we define in the where section of a function are only visible to that function, so we don’t have to worry about them polluting the namespace of other functions. Notice that all the names are aligned at a single column. If we don’t align them nice and proper, Haskell gets confused because then it doesn’t know they’re all part of the same block.

      -

      where bindings aren’t shared across function bodies of different patterns. If you want several patterns of one function to access some shared name, you have to define it globally.

      +

      where bindings aren’t shared across function bodies of different patterns. If you want several patterns of one function to access some shared name, you have to define it globally.

      You can also use where bindings to pattern match! We could have rewritten the where section of our previous function as:

           ...
      @@ -273,7 +273,7 @@ 

      Where!?

      where density mass volume = mass / volume

      And that’s all there is to it! The reason we had to introduce density as a function in this example is because we can’t just calculate one density from the function’s parameters. We have to examine the list passed to the function and there’s a different density for every pair in there.

      -

      where bindings can also be nested. It’s a common idiom to make a function and define some helper function in its where clause and then to give those functions helper functions as well, each with its own where clause.

      +

      where bindings can also be nested. It’s a common idiom to make a function and define some helper function in its where clause and then to give those functions helper functions as well, each with its own where clause.

      Let it be

      Very similar to where bindings are let bindings. Where bindings are a syntactic construct that let you bind to variables at the end of a function and the whole function can see them, including all the guards. Let bindings let you bind to variables anywhere and are expressions themselves, but are very local, so they don’t span across guards. Just like any construct in Haskell that is used to bind values to names, let bindings can be used for pattern matching. Let’s see them in action! This is how we could define a function that gives us a cylinder’s surface area based on its height and radius: @@ -286,8 +286,8 @@

      Let it be

      in sideArea + 2 * topArea
      let it be -

      The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

      -

      The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs. Remember when we did the if statement and it was explained that an if else statement is an expression and you can cram it in almost anywhere?

      +

      The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

      +

      The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs. Remember when we did the if statement and it was explained that an if else statement is an expression and you can cram it in almost anywhere?

       ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
       ["Woo", "Bar"]
      @@ -309,23 +309,23 @@ 

      Let it be

      ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar) (6000000,"Hey there!")
      -

      You don’t have to put a semicolon after the last binding but you can if you want. Like we said before, you can pattern match with let bindings. They’re very useful for quickly dismantling a tuple into components and binding them to names and such.

      +

      You don’t have to put a semicolon after the last binding but you can if you want. Like we said before, you can pattern match with let bindings. They’re very useful for quickly dismantling a tuple into components and binding them to names and such.

       ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100
       600
       
      -

      You can also put let bindings inside list comprehensions. Let’s rewrite our previous example of calculating lists of mass-volume pairs to use a let inside a list comprehension instead of defining an auxiliary function with a where.

      +

      You can also put let bindings inside list comprehensions. Let’s rewrite our previous example of calculating lists of mass-volume pairs to use a let inside a list comprehension instead of defining an auxiliary function with a where.

       calcDensities :: (RealFloat a) => [(a, a)] -> [a]
       calcDensities xs = [density | (m, v) <- xs, let density = m / v]
       
      -

      We include a let inside a list comprehension much like we would a predicate, only it doesn’t filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the densities that will float in air:

      +

      We include a let inside a list comprehension much like we would a predicate, only it doesn’t filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the densities that will float in air:

       calcDensities :: (RealFloat a) => [(a, a)] -> [a]
       calcDensities xs = [density | (m, v) <- xs, let density = m / v, density < 1.2]
       
      -

      We can’t use the density name in the (m, v) <- xs part because it’s defined prior to the let binding.

      -

      We omitted the in part of the let binding when we used them in list comprehensions because the visibility of the names is already predefined there. However, we could use a let in binding in a predicate and the names defined would only be visible to that predicate. The in part can also be omitted when defining functions and constants directly in GHCi. If we do that, then the names will be visible throughout the entire interactive session.

      +

      We can’t use the density name in the (m, v) <- xs part because it’s defined prior to the let binding.

      +

      We omitted the in part of the let binding when we used them in list comprehensions because the visibility of the names is already predefined there. However, we could use a let in binding in a predicate and the names defined would only be visible to that predicate. The in part can also be omitted when defining functions and constants directly in GHCi. If we do that, then the names will be visible throughout the entire interactive session.

       ghci> let zoot x y z = x * y + z
       ghci> zoot 3 9 2
      @@ -335,11 +335,11 @@ 

      Let it be

      ghci> boot <interactive>:1:0: Not in scope: `boot'
      -

      If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

      +

      If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

      Case expressions

      case

      Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for specific values of that variable and then maybe including a catch-all block of code in case the variable has some value for which we didn’t set up a case.

      -

      Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

      +

      Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

       head' :: [a] -> a
       head' [] = error "No head for empty lists!"
      diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html
      index 7948d3f..45c7cfe 100644
      --- a/docs/types-and-typeclasses.html
      +++ b/docs/types-and-typeclasses.html
      @@ -35,7 +35,7 @@ 

      Types and Typeclasses

      Believe the type

      moo

      Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with some number, it won’t even compile. That’s good because it’s better to catch such errors at compile time instead of having your program crash. Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.

      -

      Unlike Java or Pascal, Haskell has type inference. If we write a number, we don’t have to tell Haskell it’s a number. It can infer that on its own, so we don’t have to explicitly write out the types of our functions and expressions to get things done. We covered some of the basics of Haskell with only a very superficial glance at types. However, understanding the type system is a very important part of learning Haskell.

      +

      Unlike Java or Pascal, Haskell has type inference. If we write a number, we don’t have to tell Haskell it’s a number. It can infer that on its own, so we don’t have to explicitly write out the types of our functions and expressions to get things done. We covered some of the basics of Haskell with only a very superficial glance at types. However, understanding the type system is a very important part of learning Haskell.

      A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

      Now we’ll use GHCI to examine the types of some expressions. We’ll do that by using the :t command which, followed by any valid expression, tells us its type. Let’s give it a whirl.

      @@ -52,7 +52,7 @@ 

      Believe the type

      bomb -Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. 'a', as it would seem, has a type of Char. It’s not hard to conclude that it stands for character. True is of a Bool type. That makes sense. But what’s this? Examining the type of "HELLO!" yields a [Char]. The square brackets denote a list. So we read that as it being a list of characters. Unlike lists, each tuple length has its own type. So the expression of (True, 'a') has a type of (Bool, Char), whereas an expression such as ('a','b','c') would have the type of (Char, Char, Char). 4 == 5 will always return False, so its type is Bool. +Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. 'a', as it would seem, has a type of Char. It’s not hard to conclude that it stands for character. True is of a Bool type. That makes sense. But what’s this? Examining the type of "HELLO!" yields a [Char]. The square brackets denote a list. So we read that as it being a list of characters. Unlike lists, each tuple length has its own type. So the expression of (True, 'a') has a type of (Bool, Char), whereas an expression such as ('a','b','c') would have the type of (Char, Char, Char). 4 == 5 will always return False, so its type is Bool.

      Functions also have types. When writing our own functions, we can choose to give them an explicit type declaration. This is generally considered to be good practice except when writing very short functions. From here on, we’ll give all the functions that we make type declarations. Remember the list comprehension we made previously that filters a string so that only caps remain? Here’s how it looks like with a type declaration. @@ -165,7 +165,7 @@

      Typeclasses 101

      (>) :: (Ord a) => a -> a -> Bool

      -All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively.

      +All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively.

      To be a member of Ord, a type must first have membership in the prestigious and exclusive Eq club.

       ghci> "Abrakadabra" < "Zebra"
      diff --git a/docs/zippers.html b/docs/zippers.html
      index cac3702..93fd66d 100644
      --- a/docs/zippers.html
      +++ b/docs/zippers.html
      @@ -47,7 +47,7 @@ 

      Zippers

      impure languages, we could just note where in our memory the five is located and change that. But in Haskell, one five is as good as another, so we can’t discriminate based on where in our memory they are. We also can’t really -change anything; when we say that we change a tree, we actually +change anything; when we say that we change a tree, we actually mean that we take a tree and return a new one that’s similar to the original tree, but slightly different.

      @@ -200,7 +200,7 @@

      Taking a walk

      Nice, this seems to work. In these functions, the list of directions acts as a -sort of focus, because it pinpoints one exact subtree from our tree. +sort of focus, because it pinpoints one exact subtree from our tree. A direction list of [R] focuses on the subtree that’s right of the root, for example. An empty direction list focuses on the main tree itself. @@ -363,7 +363,7 @@

      Going back up

      In essence, every breadcrumb is now like a tree node with a hole in it. When we move deeper into a tree, the breadcrumb carries all the information that the -node that we moved away from carried except the subtree that we chose to +node that we moved away from carried except the subtree that we chose to focus on. It also has to note where the hole is. In the case of a LeftCrumb, we know that we moved left, so the subtree that’s missing is the left one. From 469256b75e373128a73f2ca2983284aefbaab02a Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Fri, 25 Nov 2022 23:48:31 +0900 Subject: [PATCH 24/27] Change hintbox tag from

      to

      in one location --- docs/starting-out.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/starting-out.html b/docs/starting-out.html index c64a33e..dea6b80 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -244,7 +244,7 @@

      An intro to lists

      [1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on.

      -

      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      +
      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      If you want to get an element out of a list by index, use !!. The indices start at 0.

       ghci> "Steve Buscemi" !! 6
      
      From 11f0e05951ead4d9230c1a2fe98de0e2f42f693b Mon Sep 17 00:00:00 2001
      From: Gregory Cox 
      Date: Sat, 26 Nov 2022 00:05:54 +0900
      Subject: [PATCH 25/27] Put 

      tag around contents of hintboxes --- docs/a-fistful-of-monads.html | 12 ++++++------ docs/assets/css/style.css | 3 +++ docs/for-a-few-monads-more.html | 8 ++++---- docs/functionally-solving-problems.html | 10 +++++----- .../functors-applicative-functors-and-monoids.html | 12 ++++++------ docs/higher-order-functions.html | 6 +++--- docs/input-and-output.html | 10 +++++----- docs/making-our-own-types-and-typeclasses.html | 14 +++++++------- docs/modules.html | 2 +- docs/recursion.html | 2 +- docs/starting-out.html | 4 ++-- docs/style.css | 3 +++ docs/syntax-in-functions.html | 4 ++-- docs/types-and-typeclasses.html | 2 +- 14 files changed, 49 insertions(+), 43 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 876379c..0b82559 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -181,12 +181,12 @@

      Getting our feet wet with Maybe

      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 @@ -422,12 +422,12 @@

      The Monad type class

      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 @@ -1487,11 +1487,11 @@

      The list monad

      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: diff --git a/docs/assets/css/style.css b/docs/assets/css/style.css index 466bfc8..fe2e5e6 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -80,6 +80,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; diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index bc3589d..d9f18a9 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -219,9 +219,9 @@

      Writer? I hardly know her!

      Monoids to the rescue

      -
      +

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

      +

      Right now, applyLog takes values of type @@ -1203,7 +1203,7 @@

      Tasteful stateful computations

      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 @@ -1213,7 +1213,7 @@

      Tasteful stateful computations

      (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 diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index 69339c4..8261603 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -40,7 +40,7 @@

      Reverse Polish notation calculatorLet’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to 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 a 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.

      -
      Protip: 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.
      +

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

      @@ -160,7 +160,7 @@

      Heathrow to London

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

      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!

      @@ -183,7 +183,7 @@

      Heathrow to London

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

      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
      @@ -200,7 +200,7 @@ 

      Heathrow to London

      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
      +

      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) =
      @@ -228,7 +228,7 @@ 

      Heathrow to London

      ([(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.
      +

      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
      diff --git a/docs/functors-applicative-functors-and-monoids.html b/docs/functors-applicative-functors-and-monoids.html
      index 8d9f158..d360188 100644
      --- a/docs/functors-applicative-functors-and-monoids.html
      +++ b/docs/functors-applicative-functors-and-monoids.html
      @@ -38,7 +38,7 @@ 

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

      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.

      @@ -84,7 +84,7 @@

      Functors redux

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

      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))
      @@ -127,7 +127,7 @@ 

      Functors redux

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

      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.

      @@ -294,7 +294,7 @@

      Applicative functors

      (<$>) :: (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.
      +

      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:

      @@ -392,7 +392,7 @@ 

      Applicative functors

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

      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)
      @@ -446,7 +446,7 @@ 

      Applicative functors

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

      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:

      diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html
      index fa6e753..e459e94 100644
      --- a/docs/higher-order-functions.html
      +++ b/docs/higher-order-functions.html
      @@ -70,7 +70,7 @@ 

      Curried functions

      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:

      +

      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)
      @@ -100,7 +100,7 @@ 

      Some higher-orderism is in order

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

      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
      @@ -256,7 +256,7 @@ 

      Maps and filters

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

      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 listOfFuns = map (*) [0..]
      diff --git a/docs/input-and-output.html b/docs/input-and-output.html
      index 37eb50d..194c513 100644
      --- a/docs/input-and-output.html
      +++ b/docs/input-and-output.html
      @@ -39,7 +39,7 @@ 

      Input and Output

      Hello, world!

      HELLO!

      Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

      -
      Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.
      +

      Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

      So, for starters, punch in the following in your favorite text editor:

       main = putStrLn "hello, world"
      @@ -65,7 +65,7 @@ 

      Hello, world!

      putStrLn "hello, world" :: IO ()

      We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

      -
      The empty tuple is a value of () and it also has a type of ().
      +

      The empty tuple is a value of () and it also has a type of ().

      So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

      Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

      @@ -146,7 +146,7 @@ 

      Hello, world!

      reverseWords = unwords . map reverse . words

      To get a feel of what it does, you can run it before we go over the code.

      -
      Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.
      +

      Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

      First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

      What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

      Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

      @@ -182,7 +182,7 @@

      Hello, world!

      putStrLn $ a ++ " " ++ b

      When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

      -
      A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.
      +

      A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.

      Before we move on to files, let’s take a look at some functions that are useful when dealing with I/O.

      putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

      @@ -1053,7 +1053,7 @@ 

      Randomness

      jack of diamonds

      We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

      -
      Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.
      +

      Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

      We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

      main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

      Here’s our program in action!

      diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index cf3a86a..ba274b1 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -517,7 +517,7 @@

      Type synonyms

      type AssocList k v = [(k,v)]

      Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

      -
      Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!
      +

      Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

      Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

       type IntMap v = Map Int v
      @@ -527,7 +527,7 @@ 

      Type synonyms

      type IntMap = Map Int

      Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

      -
      Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.
      +

      Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.

      Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

      Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

      @@ -719,9 +719,9 @@ 

      Typeclasses 102

      x /= y = not (x == y)

      Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

      -
      Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.
      +

      Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.

      Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

      -
      If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.
      +

      If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.

      So once we have a class, what can we do with it? Well, not much, really. But once we start making types instances of that class, we start getting some nice functionality. So check out this type:

       data TrafficLight = Red | Yellow | Green
      @@ -796,7 +796,7 @@ 

      Typeclasses 102

      We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

      Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

      When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

      -
      Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.
      +

      Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

      Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

      A yes-no typeclass

      yesno @@ -950,10 +950,10 @@

      The Functor typeclass

      Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

      This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.

      -

      Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

      Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.
      +

      Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

      Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

      Try figuring out how Map k is made an instance of Functor by yourself!

      With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

      -
      Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.
      +

      Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

      Kinds and some type-foo

      TYPE FOO MASTER

      Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

      diff --git a/docs/modules.html b/docs/modules.html index c1318cd..2152a39 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -560,7 +560,7 @@

      Data.Map

      findKey :: (Eq k) => k -> [(k,v)] -> Maybe v findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
      -
      Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.
      +

      Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.

       ghci> findKey "tenzing" phoneBook
       Just "853-2492"
      diff --git a/docs/recursion.html b/docs/recursion.html
      index b683b79..873e2e0 100644
      --- a/docs/recursion.html
      +++ b/docs/recursion.html
      @@ -70,7 +70,7 @@ 

      A few more recursive functions

      | otherwise = x:replicate' (n-1) x

      We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

      -
      Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.
      +

      Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.

      Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

       take' :: (Num i, Ord i) => i -> [a] -> [a]
      diff --git a/docs/starting-out.html b/docs/starting-out.html
      index dea6b80..8a2856e 100644
      --- a/docs/starting-out.html
      +++ b/docs/starting-out.html
      @@ -244,7 +244,7 @@ 

      An intro to lists

      [1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on.

      -
      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.
      +

      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      If you want to get an element out of a list by index, use !!. The indices start at 0.

       ghci> "Steve Buscemi" !! 6
      @@ -505,7 +505,7 @@ 

      Tuples

      11 ghci> snd ("Wow", False) False
      -
      Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.
      +

      Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.

      A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.

       ghci> zip [1,2,3,4,5] [5,5,5,5,5]
      diff --git a/docs/style.css b/docs/style.css
      index 466bfc8..fe2e5e6 100644
      --- a/docs/style.css
      +++ b/docs/style.css
      @@ -80,6 +80,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;
      diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html
      index 58d5c49..4c8a56a 100644
      --- a/docs/syntax-in-functions.html
      +++ b/docs/syntax-in-functions.html
      @@ -112,7 +112,7 @@ 

      Pattern matching

      Should a pattern match fail, it will just move on to the next element.

      Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list. But since [1,2,3] is just syntactic sugar for 1:2:3:[], you can also use the former pattern. A pattern like x:xs will bind the head of the list to x and the rest of it to xs, even if there’s only one element so xs ends up being an empty list.

      -
      Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.
      +

      Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.

      If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.

      Now that we know how to pattern match against list, let’s make our own implementation of the head function.

      @@ -220,7 +220,7 @@ 

      Guards, guards!

      ghci> 3 `myCompare` 2 GT
      -
      Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.
      +

      Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.

      Where!?

      In the previous section, we defined a density calculator function and responder like this:

      diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html
      index 45c7cfe..8e57826 100644
      --- a/docs/types-and-typeclasses.html
      +++ b/docs/types-and-typeclasses.html
      @@ -140,7 +140,7 @@ 

      Typeclasses 101

      ghci> :t (==) (==) :: (Eq a) => a -> a -> Bool
      -
      Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.
      +

      Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.

      Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

      The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.

      The elem function has a type of (Eq a) => a -> [a] -> Bool because it uses == over a list to check whether some value we’re looking for is in it.

      From b403b1503e99a32789c3794cc87d1b38e5325634 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Sat, 26 Nov 2022 02:49:10 +0900 Subject: [PATCH 26/27] Change tags to and adjust CSS for inline code --- docs/a-fistful-of-monads.html | 1352 ++++++------ docs/assets/css/style.css | 4 +- docs/for-a-few-monads-more.html | 1926 ++++++++--------- docs/functionally-solving-problems.html | 178 +- ...tors-applicative-functors-and-monoids.html | 1530 ++++++------- docs/higher-order-functions.html | 360 +-- docs/input-and-output.html | 952 ++++---- docs/introduction.html | 8 +- .../making-our-own-types-and-typeclasses.html | 832 +++---- docs/modules.html | 746 +++---- docs/recursion.html | 80 +- docs/sh/Scripts/shCore.js | 5 +- docs/starting-out.html | 374 ++-- docs/style.css | 4 +- docs/syntax-in-functions.html | 234 +- docs/types-and-typeclasses.html | 194 +- docs/zippers.html | 474 ++-- 17 files changed, 4627 insertions(+), 4626 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 0b82559..204c440 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -51,113 +51,113 @@

      A Fistful of Monads

      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 +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 +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: +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. +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 +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. +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, +'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 +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. +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: +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. +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 +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 @@ -176,32 +176,32 @@

      Getting our feet wet with Maybe

      -Much to no one’s surprise, Maybe is a monad, so let’s +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 +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 +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 +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!

      @@ -209,52 +209,52 @@

      Getting our feet wet with Maybe

      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 +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! +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, >>= +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 @@ -262,60 +262,60 @@

      Getting our feet wet with Maybe

      -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 +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: +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. +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, +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. +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: +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 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 ++ " :)")
      @@ -324,36 +324,36 @@ 

      Getting our feet wet with Maybe

      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 +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. +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 +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 +Maybe value represents a computation that might have failed.

      @@ -366,7 +366,7 @@

      Getting our feet wet with Maybe

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

      @@ -374,13 +374,13 @@

      Getting our feet wet with Maybe

      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, +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
       
      @@ -391,40 +391,40 @@ 

      The Monad type class

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

      @@ -432,84 +432,84 @@

      The Monad type class

      hmmm yaes

      -The next function is >>=, or bind. It’s like +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 +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 +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. +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! +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. +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. +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: +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 >>= +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 +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. +Maybe value, because when it’s Nothing, +the result of using >>= will be Nothing as well.

      @@ -518,12 +518,12 @@

      Walk the line

      pierre

      -Now that we know how to feed a Maybe a value to a +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 +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. +>>= repeatedly to handle computations of several +Maybe a values.

      @@ -559,15 +559,15 @@

      Walk the line

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

      @@ -576,81 +576,81 @@

      Walk the line

      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: +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 +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 +start off with (0,0) and then land one bird one the left, then one on the right and finally two on the left.

      @@ -658,10 +658,10 @@

      Walk the line

      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 @@ -669,24 +669,24 @@

      Walk the line

      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 +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 +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)
      @@ -696,74 +696,74 @@ 

      Walk the line

      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 +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. +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, +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)) anymore -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. +can’t do landLeft 1 (landRight 1 (0,0)) anymore +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: +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: +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 +>>= allows us to feed a monadic value to a function that takes a normal one.

      @@ -771,71 +771,71 @@

      Walk the line

      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 >>= +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. +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 (>>=) +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 +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 @@ -851,13 +851,13 @@

      Walk the line

      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:

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

      Now we can chain it together with our bird landings. It will always cause our walker @@ -865,58 +865,58 @@

      Walk the line

      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 +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 >> +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: +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. +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: +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! @@ -924,12 +924,12 @@

      Walk the line

      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 +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
      @@ -938,36 +938,36 @@ 

      Walk the line

      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 +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 +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. +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 +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 +>>= 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. @@ -978,51 +978,51 @@

      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 +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 +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. +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, +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 +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 @@ -1030,81 +1030,81 @@

      do notation

      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 +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: +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 +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 +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 +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 >>= +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 +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, +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 of the previous ones.

      @@ -1113,86 +1113,86 @@

      do notation

      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: +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 +value is the result of the last monadic value in the do expression with all the previous ones chained into it.

      -Our tightwalker’s routine can also be expressed with do -notation. landLeft and landRight +Our tightwalker’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 tightwalker -slips, in which case a Nothing is produced. We used ->>= to chain successive steps because each one +Just, unless the tightwalker +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 +>>=, 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. +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 +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:

      +used the monadic aspects of Maybe:

      -
      +
      
       routine :: Maybe Pole
       routine =
           case Just (0,0) of
      @@ -1202,20 +1202,20 @@ 

      do notation

      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. +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 +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)
      @@ -1223,45 +1223,45 @@ 

      do notation

      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 +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. +_ <- 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 +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 +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. +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 +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: +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'. +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'.

      @@ -1269,58 +1269,58 @@

      do notation

      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 +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 +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 +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 +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 +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 @@ -1331,27 +1331,27 @@

      do notation

      The list monad

      dead cat

      -So far, we’ve seen how Maybe values can be viewed as +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. +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 +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 +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 @@ -1363,20 +1363,20 @@

      The list monad

      This context of non-determinism translates to monads very nicely. Let’s go ahead -and see what the Monad instance for lists looks like: +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 +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 @@ -1384,45 +1384,45 @@

      The list monad

      -To understand how >>= works for lists, it’s +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 +>>= 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 +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 +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 +[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 +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 +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 +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 @@ -1432,58 +1432,58 @@

      The list monad

      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. +[] 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 +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 +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 +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 +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 +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 +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 +in Maybe or resulting in more non-determinism for lists) but it does present something as its result.

      @@ -1494,119 +1494,119 @@

      The list monad

    -Here’s the previous expression rewritten in do notation: +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, +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 +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 +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 +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 +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 +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: +7:

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

    -We apply show to x to turn +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 +'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 +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. +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 +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: +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 +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 ()
    @@ -1615,71 +1615,71 @@ 

    The list monad

    [()] 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 ->>: +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 +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 () +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: +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 +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. +guard.

    A knight’s quest

    @@ -1698,20 +1698,20 @@

    A knight’s quest

    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? +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)
    @@ -1719,83 +1719,83 @@ 

    A knight’s quest

    ] 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 it’s not, +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 it’s not, it produces an empty list, which causes a failure and -return (c',r') isn’t carried out for that position. +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: +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 +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 +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: +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 +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 >>= +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.

    @@ -1805,32 +1805,32 @@

    A knight’s quest

    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 +(6,2) to (6,1) in three moves:

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

    -Yes! How about from (6,2) to -(7,3)? +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 @@ -1847,7 +1847,7 @@

    Monad laws

    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 +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. @@ -1856,7 +1856,7 @@

    Monad laws

    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 +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 @@ -1867,19 +1867,19 @@

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

      -
    • return x >>= f is the same damn - thing as f x
    • +
    • return x >>= f is the same damn + thing as f x

    If you look at monadic values as values with a context and -return as taking a value and putting it in a default +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 @@ -1887,97 +1887,97 @@

    Left identity

    -For the Maybe monad return -is defined as Just. The Maybe +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: +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 +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 +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. +IO as well.

    Right identity

    The second law states that if we have a monadic value and we use ->>= to feed it to return, +>>= to feed it to return, the result is our original monadic value. Formally:

      -
    • m >>= return is no different than - just m
    • +
    • m >>= return is no different than + just m

    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 +>>=, 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 +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: +>>= 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 +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 +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.

    @@ -1986,23 +1986,23 @@

    Associativity

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

      -
    • Doing (m >>= f) >>= g is just like - doing m >>= (\x -> f x >>= g)
    • +
    • Doing (m >>= f) >>= g is just like + doing m >>= (\x -> f x >>= g)

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

    @@ -2013,119 +2013,119 @@

    Associativity

    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 +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 +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), +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: +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, +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 +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 +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 +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. +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. +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 +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 fe2e5e6..5e3c34e 100644 --- a/docs/assets/css/style.css +++ b/docs/assets/css/style.css @@ -58,7 +58,7 @@ img.center { margin:10px auto 25px auto; display:block; } -pre.code { +pre > code { color:white; /*background-color:#522812;*/ background-color:black; @@ -105,7 +105,7 @@ 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; diff --git a/docs/for-a-few-monads-more.html b/docs/for-a-few-monads-more.html index d9f18a9..9d96c5e 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -37,16 +37,16 @@

    For a Few Monads More

    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 +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 +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 +learned how to work in the IO monad, even before we knew what a monad was!

    @@ -58,30 +58,30 @@

    For a Few Monads More

    -The monads that we’ll be exploring are all part of the mtl +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 +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 +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. +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 +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 +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 +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.

    @@ -93,127 +93,127 @@

    Writer? I hardly know her!

    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 +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: +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 +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 +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 +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 +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. +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 +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 +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: +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, +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 +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 +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: +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: +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 +See how inside the lambda, x is just a normal string +and not a tuple and how applyLog takes care of appending the logs.

    @@ -224,62 +224,62 @@

    Monoids to the rescue

    -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 +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 +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 +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: +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 +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: +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 +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
    @@ -289,187 +289,187 @@ 

    Monoids to the rescue

    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 +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 +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 +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 +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 +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 +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). +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 +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 +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 +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: +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. +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. +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 +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 +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 >>= +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 +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 +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. +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. +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 +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 +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 +do notation with Writer to multiply two numbers:

    -
    +
    
     import Control.Monad.Writer
     
     logNumber :: Int -> Writer [String] Int
    @@ -480,83 +480,83 @@ 

    Using do notation with Writer

    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 +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 +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 +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. +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 +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 @@ -572,27 +572,27 @@

    Adding logging to programs

    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' +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
    @@ -603,109 +603,109 @@ 

    Adding logging to programs

    | 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: +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. +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 +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 +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: +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 +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). +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 +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. 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 +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: +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 @@ -714,12 +714,12 @@

    Inefficient list construction

    -The following function works like gcd', only it logs stuff in +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
    @@ -731,26 +731,26 @@ 

    Inefficient list construction

    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. +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 +++ to the left instead of to the right.

    @@ -764,37 +764,37 @@

    Difference lists

    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. +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 +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 +++ 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 +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 +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 @@ -802,27 +802,27 @@

    Difference lists

    -Let’s make a newtype wrapper for difference lists so +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 +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 @@ -832,34 +832,34 @@

    Difference lists

    -Here’s the Monoid instance: +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 +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 +gcdReverse function by making it use difference lists instead of normal lists:

    -
    +
    
     import Control.Monad.Writer
     
     gcd' :: Int -> Int -> Writer (DiffList String) Int
    @@ -871,29 +871,29 @@ 

    Difference lists

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

    @@ -902,61 +902,61 @@

    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 +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 +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: +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. @@ -981,22 +981,22 @@

    Reader? Ugh, not this joke again.

    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 +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: +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 @@ -1004,26 +1004,26 @@

    Reader? Ugh, not this joke again.

    Here’s an example:

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

    -The expression (+) <$> (*2) <*> (+10) +The expression (+) <$> (*2) <*> (+10) makes a function that takes a number, gives that number to -(*2) and (+10) and then adds +(*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. +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 +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 @@ -1034,49 +1034,49 @@

    Reader? Ugh, not this joke again.

    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 +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 +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 >>= +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 +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 +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 +(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. +w as well.

    -If you don’t understand how >>= works at this point, don’t +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: +a do expression that utilizes this monad:

    -
    +
    
     import Control.Monad.Instances
     
     addStuff :: Int -> Int
    @@ -1084,44 +1084,44 @@ 

    Reader? Ugh, not this joke again.

    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 +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, +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 +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 +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 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 @@ -1130,7 +1130,7 @@

    Reader? Ugh, not this joke again.

    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 >>= +extract their future results and the >>= implementation will make sure that it all works out.

    @@ -1156,22 +1156,22 @@

    Tasteful stateful computations

    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 +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 +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 @@ -1194,24 +1194,24 @@

    Tasteful stateful computations

    function would have the following type:

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

    -s is the type of the state and -a the result of the stateful computations. +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 +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 +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 +result (in this case 5) and a new state, which would be all the previous variable mappings plus the newly assigned variable.

    @@ -1237,15 +1237,15 @@

    Stacks and stones

    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 +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, +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)
    @@ -1253,69 +1253,69 @@ 

    Stacks and stones

    push :: Int -> Stack -> ((),Stack) push a xs = ((),a:xs) -
    +

    -We used () as the result when pushing to the stack +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 +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 +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 +

    + +

    +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 +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 +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 @@ -1326,82 +1326,82 @@

    Stacks and stones

    The State monad

    -The Control.Monad.State module provides a -newtype that wraps stateful computations. Here’s its definition: +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. +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: +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 +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 +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 +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 +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 +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 +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 +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 +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: +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
    @@ -1409,17 +1409,17 @@ 

    The State monad

    push :: Int -> State Stack () push a = State $ \xs -> ((),a:xs) -
    +

    -pop is already a stateful computation and -push takes an Int and +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 +pushing 3 onto the stack and then popping two numbers off like this:

    -
    +
    
     import Control.Monad.State
     
     stackManip :: State Stack Int
    @@ -1427,42 +1427,42 @@ 

    The State monad

    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 +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 +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 +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
    @@ -1471,106 +1471,106 @@ 

    The State monad

    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 +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 +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: +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 +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: +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 +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, >>= 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 >>= +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 +State s, so if that s was +different, we’d be using >>= between two different monads.

    @@ -1585,36 +1585,36 @@

    Randomness and the state monad

    -The random function from System.Random +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 +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: +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
     
    @@ -1624,21 +1624,21 @@ 

    Randomness and the state monad

    b <- randomSt c <- randomSt return (a,b,c) -
    +

    -threeCoins is now a stateful computation and after +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 +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 @@ -1649,125 +1649,125 @@

    Randomness and the state monad

    Error error on the wall

    -We know by now that Maybe is used to add a +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 +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 +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 +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, +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: +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 +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. +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 +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 +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 +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 +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. +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, +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! +function produced a Left value anyway!

    -When we try to feed a Right value to a function that +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:
    @@ -1775,22 +1775,22 @@ 

    Error error on the wall

    `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 +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! @@ -1798,8 +1798,8 @@

    Error error on the wall

    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 +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 @@ -1815,7 +1815,7 @@

    Some useful monadic functions

    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. +know, like filter and foldl. Let’s see what they are then!

    @@ -1835,47 +1835,47 @@

    liftM and friends

    So every monad is an applicative functor and every applicative functor is a -functor. The Applicative type class has a class +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. +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 +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: +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: +And this is the type of fmap:

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

    -If the Functor and Monad +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: +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)
    @@ -1888,113 +1888,113 @@ 

    liftM and friends

    (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 +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 +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]). +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: +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: +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 +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 +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 +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: +<$> 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 +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 +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 +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: +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. +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 +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
    @@ -2003,43 +2003,43 @@ 

    liftM and friends

    [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. +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 +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. +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 +have Functor and Applicative instances, so we examined the monadic equivalents of the functions that functors and applicative functors use.

    @@ -2050,40 +2050,40 @@

    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 +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 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: +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 +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. @@ -2094,185 +2094,185 @@

    The join function

    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. +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 +"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: +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 +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 on to the top. +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 on to the top.

    -The implementation for join is as follows: +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 +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, +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): +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 +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 +>>= 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). +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 +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 >>=. +out how to implement >>=.

    filterM

    -The filter function is pretty much the bread of -Haskell programming (map being the butter). It takes +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 +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 +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 +["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 +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. +Bool came with would be lost.

    -The filterM function from Control.Monad +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 +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: +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 +True or False result, also provides a log of what it did. Of course, we’ll be using the -Writer monad for this: +Writer monad for this:

    -
    +
    
     keepSmall :: Int -> Writer [String] Bool
     keepSmall x
         | x < 4 = do
    @@ -2281,35 +2281,35 @@ 

    filterM

    | 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 +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. +4 we report that we’re keeping it and then +return True.

    -Now, let’s give it to filterM along with a list. +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. +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, +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
    @@ -2317,22 +2317,22 @@ 

    filterM

    Keeping 2 10 is too large, throwing it away Keeping 3 -
    +

    -Awesome. So just by providing a monadic predicate to filterM, +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 +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], its powerset would include the following sets:

    -
    +
    
     [1,2,3]
     [1,2]
     [1,3]
    @@ -2341,29 +2341,29 @@ 

    filterM

    [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] +keeping and throwing out elements from a set. [2,3] is like the original set, only we excluded the number -1. +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 +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 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 @@ -2372,10 +2372,10 @@

    filterM

    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 @@ -2386,26 +2386,26 @@

    filterM

    foldM

    -The monadic counterpart to foldl is -foldM. If you remember your folds from the folds section, you know that foldl takes a binary +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 +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: +type of foldl is this:

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

    -Whereas foldM has the following type: +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 @@ -2413,56 +2413,56 @@

    foldM

    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 +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 +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, +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 +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: +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 +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.

    @@ -2476,13 +2476,13 @@

    Making a safe RPN calculator

    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. +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 +"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. @@ -2492,12 +2492,12 @@

    Making a safe RPN calculator

    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 @@ -2505,17 +2505,17 @@

    Making a safe RPN calculator

    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 +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 @@ -2528,68 +2528,68 @@

    Making a safe RPN calculator

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

    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 +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. +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] "-"
    @@ -2600,45 +2600,45 @@ 

    Making a safe RPN calculator

    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! +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 +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. +Maybe value.

    Let’s give it a shot:

    -
    +
    
     ghci> solveRPN "1 2 * 4 +"
     Just 6.0
     ghci> solveRPN "1 2 * 4 + 5 *"
    @@ -2647,64 +2647,64 @@ 

    Making a safe RPN calculator

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

    @@ -2712,62 +2712,62 @@

    Composing monadic functions

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

    @@ -2776,30 +2776,30 @@

    Making monads

    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 +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. +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 +[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 +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 +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 +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!

    @@ -2808,9 +2808,9 @@

    Making monads

    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 @@ -2818,52 +2818,52 @@

    Making monads

    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, +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: +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’use throw out our floating points and use -Rational for our probabilities: +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 +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 +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 @@ -2873,27 +2873,27 @@

    Making monads

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

    @@ -2901,27 +2901,27 @@

    Making monads

    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 +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. +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! +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 +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 +'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' +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:

    @@ -2932,12 +2932,12 @@

    Making monads

    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 +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 +'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 +'d' would also happen three times out of eight. If we sum all the probabilities, they still add up to one.

    @@ -2945,76 +2945,76 @@

    Making monads

    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 +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 +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 +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 +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 +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 +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 +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, +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. +monad and because multiplication is associative. 1%2 * (1%3 +* 1%5) is equal to (1%2 * 1%3) * 1%5.

    @@ -3031,7 +3031,7 @@

    Making monads

    probability values for a normal coin flip and for a loaded one:

    -
    +
    
     data Coin = Heads | Tails deriving (Show, Eq)
     
     coin :: Prob Coin
    @@ -3039,13 +3039,13 @@ 

    Making monads

    loadedCoin :: Prob Coin loadedCoin = Prob [(Heads,1%10),(Tails,9%10)] -
    +

    And finally, the coin throwing action:

    -
    +
    
     import Data.List (all)
     
     flipThree :: Prob Bool
    @@ -3054,23 +3054,23 @@ 

    Making monads

    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 +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!) diff --git a/docs/functionally-solving-problems.html b/docs/functionally-solving-problems.html index 8261603..c644f6d 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -34,51 +34,51 @@

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

    +

    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 on to 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 on to 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 a 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.

    +

    Let’s go over the expression 10 4 3 + 2 * - together! First we push 10 on to 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 a 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.

    Protip: 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","*","-"].

    +

    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!

    +

    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:

    -
    +
    +

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

    -
    +
    +

    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 on to 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 intial 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.

    +
    +

    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 on to 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 intial 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 +"
    @@ -91,10 +91,10 @@ 

    Reverse Polish notation calculator -

    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.

    -
    +
    +

    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
    @@ -107,9 +107,9 @@ 

    Reverse Polish notation calculator -

    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.

    -
    +
    +

    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 /"
    @@ -118,21 +118,21 @@ 

    Reverse Polish notation calculator -

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

    -
    +
    +

    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.

    +

    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
    @@ -145,7 +145,7 @@ 

    Heathrow to London

    10 8 0 -
    +

    To mentally parse the input file, read it in threes and mentally split the road system into sections. Each section is comprised 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:

      @@ -158,50 +158,50 @@

      Heathrow to London

      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.

      +

      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.

      -
      +
      +

      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.

      +
      +

      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.

      +
      +

      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.

      -
      +
      +

      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:

      -
      +
      +

      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

      -
      +

      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
      @@ -217,44 +217,44 @@ 

      Heathrow to London

      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.

      -
      +

      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.

      -
      +

      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.

      +
      +

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

      -
      +
      +

      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:

      -
      +
      +

      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
      @@ -266,10 +266,10 @@ 

      Heathrow to London

      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.

      +
      +

      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
      @@ -282,14 +282,14 @@ 

      Heathrow to London

      10 8 0 -
      -

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

      -
      +
      +

      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.

      +
      +

      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 index d360188..991bc12 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -32,77 +32,77 @@

      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.

      +

      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.

      -
      +

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

      -
      +
      +

      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:

      -
      +

      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.

      -
      +
      +

      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:

      -
      +

      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.

      -
      +
      +

      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
      @@ -113,25 +113,25 @@ 

      Functors redux

      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.

      +
      +

      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:

      -
      +

      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.

      +
      +

      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.

      -
      +

      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)
      @@ -142,11 +142,11 @@ 

      Functors redux

      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.

      +
      +

      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)
      @@ -159,26 +159,26 @@ 

      Functors redux

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

      -
      +
      +

      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.

      +
      +

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

      +

      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:

      -
      +

      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.

      -
      +
      +

      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"
      @@ -189,40 +189,40 @@ 

      Functors redux

      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.

      -
      +
      +

      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:

      -
      +
      +

      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.

      +
      +

      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!

      -
      +

      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')
      @@ -231,40 +231,40 @@ 

      Applicative functors

      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.

      +
      +

      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:

      -
      +
      +

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

      -
      +
      +

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

      +
      +

      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
      @@ -275,168 +275,168 @@ 

      Applicative functors

      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.

      +
      +

      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
      -
      +
      whaale -

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

      -
      +

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

      -
      +
      +

      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:

      -
      +
      +

      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.

      -
      +
      +

      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.

      -
      +
      +

      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.

      +
      +

      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:

      -
      +

      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:

      -
      +
      +

      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:

      -
      +
      +

      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.

      +

      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.

      -
      +
      +

      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:

      -
      +
      +

      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.

      -
      +
      +

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

      -
      +
      +

      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.

      -
      +
      +

      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.

      -
      +
      +

      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:

      -
      +

      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.

      -
      +
      +

      <*> 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..]
      @@ -445,43 +445,43 @@ 

      Applicative functors

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

      -
      +
      +

      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:

      -
      +
      +

      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.

      -
      +
      +

      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.

      -
      +
      +

      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.

      -
      +
      +

      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.

      +
      +

      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]
      @@ -492,28 +492,28 @@ 

      Applicative functors

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

      -
      +
      +

      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:

      -
      +
      +

      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:

      -
      +
      +

      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]]
      @@ -526,34 +526,34 @@ 

      Applicative functors

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

      +
      +

      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.

        -
      • We start off with sequenceA [[1,2],[3,4]]
      • -
      • That evaluates to (:) <$> [1,2] <*> sequenceA [[3,4]]
      • -
      • Evaluating the inner sequenceA further, we get (:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])
      • -
      • We’ve reached the edge condition, so this is now (:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
      • -
      • Now, we evaluate the (:) <$> [3,4] <*> [[]] part, which will use : with every possible value in the left list (possible values are 3 and 4) with every possible value on the right list (only possible value is []), which results in [3:[], 4:[]], which is [[3],[4]]. So now we have (:) <$> [1,2] <*> [[3],[4]]
      • -
      • Now, : is used with every possible value from the left list (1 and 2) with every possible value in the right list ([3] and [4]), which results in [1:[3], 1:[4], 2:[3], 2:[4]], which is [[1,3],[1,4],[2,3],[2,4]
      • +
      • We start off with sequenceA [[1,2],[3,4]]
      • +
      • That evaluates to (:) <$> [1,2] <*> sequenceA [[3,4]]
      • +
      • Evaluating the inner sequenceA further, we get (:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])
      • +
      • We’ve reached the edge condition, so this is now (:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
      • +
      • Now, we evaluate the (:) <$> [3,4] <*> [[]] part, which will use : with every possible value in the left list (possible values are 3 and 4) with every possible value on the right list (only possible value is []), which results in [3:[], 4:[]], which is [[3],[4]]. So now we have (:) <$> [1,2] <*> [[3],[4]]
      • +
      • Now, : is used with every possible value from the left list (1 and 2) with every possible value in the right list ([3] and [4]), which results in [1:[3], 1:[4], 2:[3], 2:[4]], which is [[1,3],[1,4],[2,3],[2,4]
      -

      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.

      -
      +

      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:

      +
      +

      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:

        -
      • pure id <*> v = v
      • -
      • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
      • -
      • pure f <*> pure x = pure (f x)
      • -
      • u <*> pure y = pure ($ y) <*> u
      • +
      • pure id <*> v = v
      • +
      • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
      • +
      • pure f <*> pure x = pure (f x)
      • +
      • u <*> pure y = pure ($ y) <*> u

      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.

      +

      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? @@ -568,59 +568,59 @@

      The newtype keyword

      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 <*> +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 +<*> 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, +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, +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: +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: +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: +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 @@ -632,13 +632,13 @@

      The newtype keyword

      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: +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, @@ -660,13 +660,13 @@

      The newtype keyword

      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 @@ -676,61 +676,61 @@

      The newtype keyword

      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. +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 "oisters"
       False
      -
      +

      In this particular newtype, the value constructor has the following type:

      -
      +
      
       CharList :: [Char] -> CharList
      -
      +

      -It takes a [Char] value, such as -"my sharona" +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 +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 +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.

      @@ -740,102 +740,102 @@

      Using newtype to make type c

      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: +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: +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 +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). +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 +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 +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 +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 +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: +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 +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

      @@ -852,95 +852,95 @@

      On newtype laziness

      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 +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, +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: +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! +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 _) +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 +undefined value, even a little, an exception is thrown.

      -Instead of using the data keyword for CoolBool, +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 +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 +helloMe to an undefined value:

      -
      +
      
       ghci> helloMe undefined
       "hello"
      -
      +
      top of the mornin to ya!!! @@ -951,7 +951,7 @@

      On newtype laziness

      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 +to the (CoolBool _) pattern because newtype types can only have one possible value constructor and one field!

      @@ -967,7 +967,7 @@

      On newtype laziness

      direct conversion from one type to another.

      -

      type vs. newtype vs. data

      +

      type vs. newtype vs. data

      At this point, you may be a bit confused about what exactly the difference @@ -981,31 +981,31 @@

      type vs.  -
      +
      
       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 +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 +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 +PhoneBook so that the type signatures of our functions were easier to read.

      @@ -1016,19 +1016,19 @@

      type vs. 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. +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.

      @@ -1050,7 +1050,7 @@

      type vs. 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. +lists and Maybe-like types to trees.

      @@ -1069,36 +1069,36 @@

      Monoids

      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 +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. +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 +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. +Functor, and so on.

      -Now consider the following: * is a function that +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 +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: []. +*, 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
      @@ -1107,12 +1107,12 @@ 

      Monoids

      [1,2,3] ghci> [] ++ [0.5, 2.5] [0.5,2.5] -
      +

      -It seems that both * together with -1 -and ++ along with [] share +It seems that both * together with +1 +and ++ along with [] share some common properties:

      @@ -1128,12 +1128,12 @@

      Monoids

      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 ++: +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))
      @@ -1142,13 +1142,13 @@ 

      Monoids

      "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) +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.

      @@ -1159,103 +1159,103 @@

      Monoids

      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 +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 +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 +Monoid, because the m in the type class definition doesn’t take any type parameters. This is different -from Functor and Applicative, +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 +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 +of like minBound from +Bounded. mempty represents the identity value for a particular monoid.

      -Next up, we have mappend, which, as you’ve probably +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, +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 +++ 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 +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 +mappend being a binary function that takes two monoid values and returns a third.

      -The last function in this type class definition is mconcat. +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. +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 +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 +mconcat, but for most instances the default implementation is just fine.

      -Before moving on to specific instances of Monoid, +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 +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:

        -
      • mempty `mappend` x = x
      • -
      • x `mappend` mempty = x
      • -
      • (x `mappend` y) `mappend` z = x `mappend` (y - `mappend` z)
      • +
      • mempty `mappend` x = x
      • +
      • x `mappend` mempty = x
      • +
      • (x `mappend` y) `mappend` z = x `mappend` (y + `mappend` z)

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

      @@ -1263,23 +1263,23 @@

      Monoids

      Lists are monoids

      -Yes, lists are monoids! Like we’ve seen, the ++ -function and the empty list [] form a monoid. The +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 +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 +Notice that we wrote instance Monoid [a] and not +instance Monoid [], because +Monoid requires a concrete type for an instance.

      @@ -1287,7 +1287,7 @@

      Lists are monoids

      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"
      @@ -1302,62 +1302,62 @@ 

      Lists are monoids

      [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 +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]) +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 +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 adjecent lists in a list.

      The monoid laws do indeed hold for the list instance. When we have several lists -and we mappend (or ++) +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, +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 +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

      +

      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 +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: ++ and the identity value 0:

      -
      +
      
       ghci> 0 + 4
       4
       ghci> 5 + 0
      @@ -1366,7 +1366,7 @@ 

      Product and Product and Data.Monoid module exports two types for this, -namely Product and Sum. -Product is defined like this: +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 +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, +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 +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
      @@ -1422,72 +1422,72 @@ 

      Product and Monoid type class, +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 +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 +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

      +

      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 +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: +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
      @@ -1496,41 +1496,41 @@ 

      Any and All< 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 +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: +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
      @@ -1539,62 +1539,62 @@ 

      Any and All< 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. +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

      +

      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 +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 +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 +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 +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 @@ -1604,28 +1604,28 @@

      The Ordering monoid

      For instance, if we were to alphabetically compare the words -"ox" and "on", we’d first +"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 +'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". +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: +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
      @@ -1634,63 +1634,63 @@ 

      The Ordering monoid

      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 +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 +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 +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 +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
      @@ -1698,177 +1698,177 @@ 

      The Ordering monoid

      (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 +"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 +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. +"zen" wins.

      -The Ordering monoid is very cool because it allows us +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

      +

      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 +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 +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 +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. +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 +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 +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 +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 +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: +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: +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: +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 the Last a type, which works like -First a, only the last -non-Nothing -value is kept when mappending and using -mconcat: +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 the 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

      @@ -1882,68 +1882,68 @@

      Using monoids to fold data struct

      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, +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. +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 +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 +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! +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 +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 +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.

      @@ -1953,99 +1953,99 @@

      Using monoids to fold data struct 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 +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: +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) +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 +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. 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 +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: +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, +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 +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. +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 +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 +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, +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 +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! +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)
      @@ -2055,78 +2055,78 @@ 

      Using monoids to fold data struct (Node 8 Empty Empty) (Node 10 Empty Empty) ) -

      +

      -It has 5 at its root and then its left node is 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, +It has 5 at its root and then its left node is 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 +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: +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 +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. +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. +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] +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 +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. +instance of Foldable.

        diff --git a/docs/higher-order-functions.html b/docs/higher-order-functions.html index e459e94..d7df2b7 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -35,54 +35,54 @@

        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:

        -
        +

        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.

        +

        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:

        -
        +
        +

        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:

        -
        +
        +

        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:

        -
        +
        +

        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.

        +
        +

        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:

        -
        +
        +

        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?

        -
        +
        +

        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))
        @@ -90,19 +90,19 @@ 

        Curried functions

        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.

        +
        +

        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:

        -
        +

        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"
        @@ -113,18 +113,18 @@ 

        Some higher-orderism is in order

        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:

        -
        +

        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:

        -
        +
        +

        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]
        @@ -135,37 +135,37 @@ 

        Some higher-orderism is in order

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

        -
        +

        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.

        -
        +
        +

        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.

        -
        +
        +

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

        -
        +
        +

        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"]
        @@ -176,18 +176,18 @@ 

        Maps and filters

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

        -
        +
        +

        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:

        -
        +
        +

        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]
        @@ -200,150 +200,150 @@ 

        Maps and filters

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

        -
        +
        +

        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.

        +

        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:

        -
        +

        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.

        +
        +

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

        -
        +
        +

        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 listOfFuns = map (*) [0..]
         ghci> (listOfFuns !! 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.

        +
        +

        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:

        -
        +

        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.

        +
        +

        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.

        +

        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!

        -
        +
        +

        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:

        -
        +
        +

        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.

        +
        +

        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.

        +

        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.

        -
        +

        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!

        +

        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.

        -
        +
        +

        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.

        +
        +

        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.

        +
        +

        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!

        +

        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.

        +

        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)
         
        @@ -361,11 +361,11 @@ 

        Only folds and horses

        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.

        -
        +
        +

        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]
        @@ -374,99 +374,99 @@ 

        Only folds and horses

        [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 roots of all natural numbers to exceed 1000? To get the squares 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.

        -
        +
        +

        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 roots of all natural numbers to exceed 1000? To get the squares 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.

        +
        +

        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:

        -
        +

        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.

        -
        +

        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:

        -
        +

        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.

        +

        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

        -
        +
        +

        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.

        +
        +

        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 right sides. 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?

        -
        +
        +

        The xs is exposed on both right sides. 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.

        -
        +
        +

        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/input-and-output.html b/docs/input-and-output.html index 194c513..85406da 100644 --- a/docs/input-and-output.html +++ b/docs/input-and-output.html @@ -38,102 +38,102 @@

          Input and Output

          Do not despair, all is not lost. It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen. With those two parts separated, we can still reason about our pure program and take advantage of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

          Hello, world!

          HELLO! -

          Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

          +

          Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

          Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

          So, for starters, punch in the following in your favorite text editor:

          -
          +
          
           main = putStrLn "hello, world"
          -
          -

          We just defined a name called main and in it we call a function called putStrLn with the parameter "hello, world". Looks pretty much run of the mill, but it isn’t, as we’ll see in just a few moments. Save that file as helloworld.hs.

          -

          And now, we’re going to do something we’ve never done before. We’re actually going to compile our program! I’m so excited! Open up your terminal and navigate to the directory where helloworld.hs is located and do the following:

          -
          +
          +

          We just defined a name called main and in it we call a function called putStrLn with the parameter "hello, world". Looks pretty much run of the mill, but it isn’t, as we’ll see in just a few moments. Save that file as helloworld.hs.

          +

          And now, we’re going to do something we’ve never done before. We’re actually going to compile our program! I’m so excited! Open up your terminal and navigate to the directory where helloworld.hs is located and do the following:

          +
          
           $ ghc --make helloworld
           [1 of 1] Compiling Main             ( helloworld.hs, helloworld.o )
           Linking helloworld ...
          -
          -

          Okay! With any luck, you got something like this and now you can run your program by doing ./helloworld.

          -
          +
          +

          Okay! With any luck, you got something like this and now you can run your program by doing ./helloworld.

          +
          
           $ ./helloworld
           hello, world
          -
          +

          And there we go, our first compiled program that printed out something to the terminal. How extraordinarily boring!

          -

          Let’s examine what we wrote. First, let’s look at the type of the function putStrLn.

          -
          +

          Let’s examine what we wrote. First, let’s look at the type of the function putStrLn.

          +
          
           ghci> :t putStrLn
           putStrLn :: String -> IO ()
           ghci> :t putStrLn "hello, world"
           putStrLn "hello, world" :: IO ()
          -
          -

          We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

          -

          The empty tuple is a value of () and it also has a type of ().

          -

          So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

          +
          +

          We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

          +

          The empty tuple is a value of () and it also has a type of ().

          +

          So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

          Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

          -
          +
          
           main = do
          -    putStrLn "Hello, what's your name?"
          +    putStrLn "Hello, what's your name?"
               name <- getLine
               putStrLn ("Hey " ++ name ++ ", you rock!")
          -
          -

          Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that’s the type of the last I/O action inside.

          -

          Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don’t usually specify a type declaration for main.

          -

          An interesting thing that we haven’t met before is the third line, which states name <- getLine. It looks like it reads a line from the input and stores it into a variable called name. Does it really? Well, let’s examine the type of getLine.

          -
          +
          +

          Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that’s the type of the last I/O action inside.

          +

          Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don’t usually specify a type declaration for main.

          +

          An interesting thing that we haven’t met before is the third line, which states name <- getLine. It looks like it reads a line from the input and stores it into a variable called name. Does it really? Well, let’s examine the type of getLine.

          +
          
           ghci> :t getLine
           getLine :: IO String
          -
          +
          luggage -

          Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

          -

          When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

          -
          +

          Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

          +

          When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

          +
          
           main = do
          -    putStrLn "Hello, what's your name?"
          +    putStrLn "Hello, what's your name?"
               name <- getLine
               putStrLn $ "Read this carefully, because this is your future: " ++ tellFortune name
          -
          -

          and tellFortune (or any of the functions it passes name to) doesn’t have to know anything about I/O, it’s just a normal String -> String function!

          +
          +

          and tellFortune (or any of the functions it passes name to) doesn’t have to know anything about I/O, it’s just a normal String -> String function!

          Take a look at this piece of code. Is it valid?

          -
          +
          
           nameTag = "Hello, my name is " ++ getLine
          -
          -

          If you said no, go eat a cookie. If you said yes, drink a bowl of molten lava. Just kidding, don’t! The reason that this doesn’t work is that ++ requires both its parameters to be lists over the same type. The left parameter has a type of String (or [Char] if you will), whilst getLine has a type of IO String. You can’t concatenate a string and an I/O action. We first have to get the result out of the I/O action to get a value of type String and the only way to do that is to say something like name <- getLine inside some other I/O action. If we want to deal with impure data, we have to do it in an impure environment. So the taint of impurity spreads around much like the undead scourge and it’s in our best interest to keep the I/O parts of our code as small as possible.

          +
          +

          If you said no, go eat a cookie. If you said yes, drink a bowl of molten lava. Just kidding, don’t! The reason that this doesn’t work is that ++ requires both its parameters to be lists over the same type. The left parameter has a type of String (or [Char] if you will), whilst getLine has a type of IO String. You can’t concatenate a string and an I/O action. We first have to get the result out of the I/O action to get a value of type String and the only way to do that is to say something like name <- getLine inside some other I/O action. If we want to deal with impure data, we have to do it in an impure environment. So the taint of impurity spreads around much like the undead scourge and it’s in our best interest to keep the I/O parts of our code as small as possible.

          Every I/O action that gets performed has a result encapsulated within it. That’s why our previous example program could also have been written like this:

          -
          +
          
           main = do
          -    foo <- putStrLn "Hello, what's your name?"
          +    foo <- putStrLn "Hello, what's your name?"
               name <- getLine
               putStrLn ("Hey " ++ name ++ ", you rock!")
          -
          -

          However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

          -

          Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

          +
          +

          However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

          +

          Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

          Beginners sometimes think that doing

          -
          +
          
           name = getLine
          -
          -

          will read from the input and then bind the value of that to name. Well, it won’t, all this does is give the getLine I/O action a different name called, well, name. Remember, to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-.

          -

          I/O actions will only be performed when they are given a name of main or when they’re inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they’ll be performed only if they eventually fall into main.

          +
          +

          will read from the input and then bind the value of that to name. Well, it won’t, all this does is give the getLine I/O action a different name called, well, name. Remember, to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-.

          +

          I/O actions will only be performed when they are given a name of main or when they’re inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they’ll be performed only if they eventually fall into main.

          Oh, right, there’s also one more case when I/O actions will be performed. When we type out an I/O action in GHCI and press return, it will be performed.

          -
          +
          
           ghci> putStrLn "HEEY"
           HEEY
          -
          -

          Even when we just punch out a number or call a function in GHCI and press return, it will evaluate it (as much as it needs) and then call show on it and then it will print that string to the terminal using putStrLn implicitly.

          -

          Remember let bindings? If you don’t, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn’t needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

          -
          +
          +

          Even when we just punch out a number or call a function in GHCI and press return, it will evaluate it (as much as it needs) and then call show on it and then it will print that string to the terminal using putStrLn implicitly.

          +

          Remember let bindings? If you don’t, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn’t needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

          +
          
           import Data.Char
           
           main = do
          -    putStrLn "What's your first name?"
          +    putStrLn "What's your first name?"
               firstName <- getLine
          -    putStrLn "What's your last name?"
          +    putStrLn "What's your last name?"
               lastName <- getLine
               let bigFirstName = map toUpper firstName
                   bigLastName = map toUpper lastName
               putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?"
          -
          -

          See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

          -

          You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action. It’s a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we’d still have to run it through a <- to perform it.

          +
          +

          See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

          +

          You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action. It’s a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we’d still have to run it through a <- to perform it.

          Now we’re going to make a program that continuously reads a line and prints out the same line with the words reversed. The program’s execution will stop when we input a blank line. This is the program:

          -
          +
          
           main = do
               line <- getLine
               if null line
          @@ -144,21 +144,21 @@ 

          Hello, world!

          reverseWords :: String -> String reverseWords = unwords . map reverse . words -
          +

          To get a feel of what it does, you can run it before we go over the code.

          -

          Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

          -

          First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

          -

          What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

          +

          Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

          +

          First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

          +

          What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

          Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

          -
          +
          
                   else (do
                       putStrLn $ reverseWords line
                       main)
          -
          -

          This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

          -

          Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

          -

          Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

          -
          +
          +

          This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

          +

          Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

          +

          Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

          +
          
           main = do
               return ()
               return "HAHAHA"
          @@ -166,72 +166,72 @@ 

          Hello, world!

          return "BLAH BLAH BLAH" return 4 putStrLn line -
          -

          All these returns do is that they make I/O actions that don’t really do anything except have an encapsulated result and that result is thrown away because it isn’t bound to a name. We can use return in combination with <- to bind stuff to names.

          -
          +
          +

          All these returns do is that they make I/O actions that don’t really do anything except have an encapsulated result and that result is thrown away because it isn’t bound to a name. We can use return in combination with <- to bind stuff to names.

          +
          
           main = do
               a <- return "hell"
               b <- return "yeah!"
               putStrLn $ a ++ " " ++ b
          -
          -

          So you see, return is sort of the opposite to <-. While return takes a value and wraps it up in a box, <- takes a box (and performs it) and takes the value out of it, binding it to a name. But doing this is kind of redundant, especially since you can use let bindings in do blocks to bind to names, like so:

          -
          +
          +

          So you see, return is sort of the opposite to <-. While return takes a value and wraps it up in a box, <- takes a box (and performs it) and takes the value out of it, binding it to a name. But doing this is kind of redundant, especially since you can use let bindings in do blocks to bind to names, like so:

          +
          
           main = do
               let a = "hell"
                   b = "yeah"
               putStrLn $ a ++ " " ++ b
          -
          -

          When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

          -

          A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.

          +
          +

          When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

          +

          A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.

          Before we move on to files, let’s take a look at some functions that are useful when dealing with I/O.

          -

          putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

          -
          +

          putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

          +
          
           main = do   putStr "Hey, "
          -            putStr "I'm "
          +            putStr "I'm "
                       putStrLn "Andy!"
          -
          -
          +
          +
          
           $ runhaskell putstr_test.hs
          -Hey, I'm Andy!
          -
          -

          Its type signature is putStr :: String -> IO (), so the result encapsulated within the resulting I/O action is the unit. A dud value, so it doesn’t make sense to bind it.

          -

          putChar takes a character and returns an I/O action that will print it out to the terminal.

          -
          -main = do   putChar 't'
          -            putChar 'e'
          -            putChar 'h'
          -
          -
          +Hey, I'm Andy!
          +
          +

          Its type signature is putStr :: String -> IO (), so the result encapsulated within the resulting I/O action is the unit. A dud value, so it doesn’t make sense to bind it.

          +

          putChar takes a character and returns an I/O action that will print it out to the terminal.

          +
          
          +main = do   putChar 't'
          +            putChar 'e'
          +            putChar 'h'
          +
          +
          
           $ runhaskell putchar_test.hs
           teh
          -
          -

          putStr is actually defined recursively with the help of putChar. The edge condition of putStr is the empty string, so if we’re printing an empty string, just return an I/O action that does nothing by using return (). If it’s not empty, then print the first character of the string by doing putChar and then print of them using putStr.

          -
          +
          +

          putStr is actually defined recursively with the help of putChar. The edge condition of putStr is the empty string, so if we’re printing an empty string, just return an I/O action that does nothing by using return (). If it’s not empty, then print the first character of the string by doing putChar and then print of them using putStr.

          +
          
           putStr :: String -> IO ()
           putStr [] = return ()
           putStr (x:xs) = do
               putChar x
               putStr xs
          -
          +

          See how we can use recursion in I/O, just like we can use it in pure code. Just like in pure code, we define the edge case and then think what the result actually is. It’s an action that first outputs the first character and then outputs the rest of the string.

          -

          print takes a value of any type that’s an instance of Show (meaning that we know how to represent it as a string), calls show with that value to stringify it and then outputs that string to the terminal. Basically, it’s just putStrLn . show. It first runs show on a value and then feeds that to putStrLn, which returns an I/O action that will print out our value.

          -
          +

          print takes a value of any type that’s an instance of Show (meaning that we know how to represent it as a string), calls show with that value to stringify it and then outputs that string to the terminal. Basically, it’s just putStrLn . show. It first runs show on a value and then feeds that to putStrLn, which returns an I/O action that will print out our value.

          +
          
           main = do   print True
                       print 2
                       print "haha"
                       print 3.2
                       print [3,4,3]
          -
          -
          +
          +
          
           $ runhaskell print_test.hs
           True
           2
           "haha"
           3.2
           [3,4,3]
          -
          -

          As you can see, it’s a very handy function. Remember how we talked about how I/O actions are performed only when they fall into main or when we try to evaluate them in the GHCI prompt? When we type out a value (like 3 or [1,2,3]) and press the return key, GHCI actually uses print on that value to display it on our terminal!

          -
          +
          +

          As you can see, it’s a very handy function. Remember how we talked about how I/O actions are performed only when they fall into main or when we try to evaluate them in the GHCI prompt? When we type out a value (like 3 or [1,2,3]) and press the return key, GHCI actually uses print on that value to display it on our terminal!

          +
          
           ghci> 3
           3
           ghci> print 3
          @@ -240,53 +240,53 @@ 

          Hello, world!

          ["hey!","ho!","woo!"] ghci> print (map (++"!") ["hey","ho","woo"]) ["hey!","ho!","woo!"] -
          -

          When we want to print out strings, we usually use putStrLn because we don’t want the quotes around them, but for printing out values of other types to the terminal, print is used the most.

          -

          getChar is an I/O action that reads a character from the input. Thus, its type signature is getChar :: IO Char, because the result contained within the I/O action is a Char. Note that due to buffering, reading of the characters won’t actually happen until the user mashes the return key.

          -
          +
          +

          When we want to print out strings, we usually use putStrLn because we don’t want the quotes around them, but for printing out values of other types to the terminal, print is used the most.

          +

          getChar is an I/O action that reads a character from the input. Thus, its type signature is getChar :: IO Char, because the result contained within the I/O action is a Char. Note that due to buffering, reading of the characters won’t actually happen until the user mashes the return key.

          +
          
           main = do
               c <- getChar
          -    if c /= ' '
          +    if c /= ' '
                   then do
                       putChar c
                       main
                   else return ()
          -
          +

          This program looks like it should read a character and then check if it’s a space. If it is, halt execution and if it isn’t, print it to the terminal and then do the same thing all over again. Well, it kind of does, only not in the way you might expect. Check this out:

          -
          +
          
           $ runhaskell getchar_test.hs
           hello sir
           hello
          -
          -

          The second line is the input. We input hello sir and then press return. Due to buffering, the execution of the program will begin only when after we’ve hit return and not after every inputted character. But once we press return, it acts on what we’ve been putting in so far. Try playing with this program to get a feel for it!

          -

          The when function is found in Control.Monad (to get access to it, do import Control.Monad). It’s interesting because in a do block it looks like a control flow statement, but it’s actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return (), action, so an I/O action that doesn’t do anything. Here’s how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

          -
          +
          +

          The second line is the input. We input hello sir and then press return. Due to buffering, the execution of the program will begin only when after we’ve hit return and not after every inputted character. But once we press return, it acts on what we’ve been putting in so far. Try playing with this program to get a feel for it!

          +

          The when function is found in Control.Monad (to get access to it, do import Control.Monad). It’s interesting because in a do block it looks like a control flow statement, but it’s actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return (), action, so an I/O action that doesn’t do anything. Here’s how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

          +
          
           import Control.Monad
           
           main = do
               c <- getChar
          -    when (c /= ' ') $ do
          +    when (c /= ' ') $ do
                   putChar c
                   main
          -
          -

          So as you can see, it’s useful for encapsulating the if something then do some I/O action else return () pattern.

          -

          sequence takes a list of I/O actions and returns an I/O actions that will perform those actions one after the other. The result contained in that I/O action will be a list of the results of all the I/O actions that were performed. Its type signature is sequence :: [IO a] -> IO [a]. Doing this:

          -
          +
          +

          So as you can see, it’s useful for encapsulating the if something then do some I/O action else return () pattern.

          +

          sequence takes a list of I/O actions and returns an I/O actions that will perform those actions one after the other. The result contained in that I/O action will be a list of the results of all the I/O actions that were performed. Its type signature is sequence :: [IO a] -> IO [a]. Doing this:

          +
          
           main = do
               a <- getLine
               b <- getLine
               c <- getLine
               print [a,b,c]
          -
          +

          Is exactly the same as doing this:.

          -
          +
          
           main = do
               rs <- sequence [getLine, getLine, getLine]
               print rs
          -
          -

          So sequence [getLine, getLine, getLine] makes an I/O action that will perform getLine three times. If we bind that action to a name, the result is a list of all the results, so in our case, a list of three things that the user entered at the prompt.

          -

          A common pattern with sequence is when we map functions like print or putStrLn over lists. Doing map print [1,2,3,4] won’t create an I/O action. It will create a list of I/O actions, because that’s like writing [print 1, print 2, print 3, print 4]. If we want to transform that list of I/O actions into an I/O action, we have to sequence it.

          -
          +
          +

          So sequence [getLine, getLine, getLine] makes an I/O action that will perform getLine three times. If we bind that action to a name, the result is a list of all the results, so in our case, a list of three things that the user entered at the prompt.

          +

          A common pattern with sequence is when we map functions like print or putStrLn over lists. Doing map print [1,2,3,4] won’t create an I/O action. It will create a list of I/O actions, because that’s like writing [print 1, print 2, print 3, print 4]. If we want to transform that list of I/O actions into an I/O action, we have to sequence it.

          +
          
           ghci> sequence (map print [1,2,3,4,5])
           1
           2
          @@ -294,10 +294,10 @@ 

          Hello, world!

          4 5 [(),(),(),(),()] -
          -

          What’s with the [(),(),(),(),()] at the end? Well, when we evaluate an I/O action in GHCI, it’s performed and then its result is printed out, unless that result is (), in which case it’s not printed out. That’s why evaluating putStrLn "hehe" in GHCI just prints out hehe (because the contained result in putStrLn "hehe" is ()). But when we do getLine in GHCI, the result of that I/O action is printed out, because getLine has a type of IO String.

          -

          Because mapping a function that returns an I/O action over a list and then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM takes a function and a list, maps the function over the list and then sequences it. mapM_ does the same, only it throws away the result later. We usually use mapM_ when we don’t care what result our sequenced I/O actions have.

          -
          +
          +

          What’s with the [(),(),(),(),()] at the end? Well, when we evaluate an I/O action in GHCI, it’s performed and then its result is printed out, unless that result is (), in which case it’s not printed out. That’s why evaluating putStrLn "hehe" in GHCI just prints out hehe (because the contained result in putStrLn "hehe" is ()). But when we do getLine in GHCI, the result of that I/O action is printed out, because getLine has a type of IO String.

          +

          Because mapping a function that returns an I/O action over a list and then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM takes a function and a list, maps the function over the list and then sequences it. mapM_ does the same, only it throws away the result later. We usually use mapM_ when we don’t care what result our sequenced I/O actions have.

          +
          
           ghci> mapM print [1,2,3]
           1
           2
          @@ -307,9 +307,9 @@ 

          Hello, world!

          1 2 3 -
          -

          forever takes an I/O action and returns an I/O action that just repeats the I/O action it got forever. It’s located in Control.Monad. This little program will indefinitely ask the user for some input and spit it back to him, CAPSLOCKED:

          -
          +
          +

          forever takes an I/O action and returns an I/O action that just repeats the I/O action it got forever. It’s located in Control.Monad. This little program will indefinitely ask the user for some input and spit it back to him, CAPSLOCKED:

          +
          
           import Control.Monad
           import Data.Char
           
          @@ -317,9 +317,9 @@ 

          Hello, world!

          putStr "Give me some input: " l <- getLine putStrLn $ map toUpper l -
          -

          forM (located in Control.Monad) is like mapM, only that it has its parameters switched around. The first parameter is the list and the second one is the function to map over that list, which is then sequenced. Why is that useful? Well, with some creative use of lambdas and do notation, we can do stuff like this:

          -
          +
          +

          forM (located in Control.Monad) is like mapM, only that it has its parameters switched around. The first parameter is the list and the second one is the function to map over that list, which is then sequenced. Why is that useful? Well, with some creative use of lambdas and do notation, we can do stuff like this:

          +
          
           import Control.Monad
           
           main = do
          @@ -329,10 +329,10 @@ 

          Hello, world!

          return color) putStrLn "The colors that you associate with 1, 2, 3 and 4 are: " mapM putStrLn colors -
          -

          The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn’t have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it’s the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

          -

          You can think of forM as meaning: make an I/O action for every element in this list. What each I/O action will do can depend on the element that was used to make the action. Finally, perform those actions and bind their results to something. We don’t have to bind it, we can also just throw it away.

          -
          +
          +

          The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn’t have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it’s the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

          +

          You can think of forM as meaning: make an I/O action for every element in this list. What each I/O action will do can depend on the element that was used to make the action. Finally, perform those actions and bind their results to something. We don’t have to bind it, we can also just throw it away.

          +
          
           $ runhaskell form_test.hs
           Which color do you associate with the number 1?
           white
          @@ -347,22 +347,22 @@ 

          Hello, world!

          blue red orange -
          -

          We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

          -

          In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

          -

          Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

          +
          +

          We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

          +

          In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

          +

          Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

          Files and streams

          streams -

          getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

          -

          getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

          -
          -I'm a lil' teapot
          -What's with that airplane food, huh?
          -It's so small, tasteless
          -
          +

          getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

          +

          getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

          +
          
          +I'm a lil' teapot
          +What's with that airplane food, huh?
          +It's so small, tasteless
          +

          Yeah, the haiku sucks, what of it? If anyone knows of any good haiku tutorials, let me know.

          -

          Now, recall the little program we wrote when we were introducing the forever function. It prompted the user for a line, returned it to him in CAPSLOCK and then did that all over again, indefinitely. Just so you don’t have to scroll all the way back, here it is again:

          -
          +

          Now, recall the little program we wrote when we were introducing the forever function. It prompted the user for a line, returned it to him in CAPSLOCK and then did that all over again, indefinitely. Just so you don’t have to scroll all the way back, here it is again:

          +
          
           import Control.Monad
           import Data.Char
           
          @@ -370,49 +370,49 @@ 

          Files and streams

          putStr "Give me some input: " l <- getLine putStrLn $ map toUpper l -
          -

          We’ll save that program as capslocker.hs or something and compile it. And then, we’re going to use a unix pipe to feed our text file directly to our little program. We’re going to use the help of the GNU cat program, which prints out a file that’s given to it as an argument. Check it out, booyaka!

          -
          +
          +

          We’ll save that program as capslocker.hs or something and compile it. And then, we’re going to use a unix pipe to feed our text file directly to our little program. We’re going to use the help of the GNU cat program, which prints out a file that’s given to it as an argument. Check it out, booyaka!

          +
          
           $ ghc --make capslocker
           [1 of 1] Compiling Main             ( capslocker.hs, capslocker.o )
           Linking capslocker ...
           $ cat haiku.txt
          -I'm a lil' teapot
          -What's with that airplane food, huh?
          -It's so small, tasteless
          +I'm a lil' teapot
          +What's with that airplane food, huh?
          +It's so small, tasteless
           $ cat haiku.txt | ./capslocker
          -I'M A LIL' TEAPOT
          -WHAT'S WITH THAT AIRPLANE FOOD, HUH?
          -IT'S SO SMALL, TASTELESS
          +I'M A LIL' TEAPOT
          +WHAT'S WITH THAT AIRPLANE FOOD, HUH?
          +IT'S SO SMALL, TASTELESS
           capslocker <stdin>: hGetLine: end of file
          -
          -

          As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

          -

          So what we’re essentially doing with that use of forever is taking the input and transforming it into some output. That’s why we can use getContents to make our program even shorter and better:

          -
          +
          +

          As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

          +

          So what we’re essentially doing with that use of forever is taking the input and transforming it into some output. That’s why we can use getContents to make our program even shorter and better:

          +
          
           import Data.Char
           
           main = do
               contents <- getContents
               putStr (map toUpper contents)
          -
          -

          We run the getContents I/O action and name the string it produces contents. Then, we map toUpper over that string and print that to the terminal. Keep in mind that because strings are basically lists, which are lazy, and getContents is I/O lazy, it won’t try to read the whole content at once and store it into memory before printing out the capslocked version. Rather, it will print out the capslocked version as it reads it, because it will only read a line from the input when it really needs to.

          -
          +
          +

          We run the getContents I/O action and name the string it produces contents. Then, we map toUpper over that string and print that to the terminal. Keep in mind that because strings are basically lists, which are lazy, and getContents is I/O lazy, it won’t try to read the whole content at once and store it into memory before printing out the capslocked version. Rather, it will print out the capslocked version as it reads it, because it will only read a line from the input when it really needs to.

          +
          
           $ cat haiku.txt | ./capslocker
          -I'M A LIL' TEAPOT
          -WHAT'S WITH THAT AIRPLANE FOOD, HUH?
          -IT'S SO SMALL, TASTELESS
          -
          +I'M A LIL' TEAPOT +WHAT'S WITH THAT AIRPLANE FOOD, HUH? +IT'S SO SMALL, TASTELESS +

          Cool, it works. What if we just run capslocker and try to type in the lines ourselves?

          -
          +
          
           $ ./capslocker
           hey ho
           HEY HO
           lets go
           LETS GO
          -
          -

          We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it’s not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that’s also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: “Hey, I need a capslocked line!”. It doesn’t have any lines yet, so it says to contents: “Hey, how about actually getting a line from the terminal?”. So that’s when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: “Hey, I need the next line, come on!” and this repeats until there’s no more input, which is signified by an end-of-file character.

          +
          +

          We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it’s not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that’s also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: “Hey, I need a capslocked line!”. It doesn’t have any lines yet, so it says to contents: “Hey, how about actually getting a line from the terminal?”. So that’s when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: “Hey, I need the next line, come on!” and this repeats until there’s no more input, which is signified by an end-of-file character.

          Let’s make program that takes some input and prints out only those lines that are shorter than 10 characters. Observe:

          -
          +
          
           main = do
               contents <- getContents
               putStr (shortLinesOnly contents)
          @@ -423,30 +423,30 @@ 

          Files and streams

          shortLines = filter (\line -> length line < 10) allLines result = unlines shortLines in result -
          +

          We’ve made our I/O part of the program as short as possible. Because our program is supposed to take some input and print out some output based on the input, we can implement it by reading the input contents, running a function on them and then printing out what the function gave back.

          -

          The shortLinesOnly function works like this: it takes a string, like "short\nlooooooooooooooong\nshort again". That string has three lines, two of them are short and the middle one is long. It runs the lines function on that string, which converts it to ["short", "looooooooooooooong", "short again"], which we then bind to the name allLines. That list of string is then filtered so that only those lines that are shorter than 10 characters remain in the list, producing ["short", "short again"]. And finally, unlines joins that list into a single newline delimited string, giving "short\nshort again". Let’s give it a go.

          -
          -i'm short
          +

          The shortLinesOnly function works like this: it takes a string, like "short\nlooooooooooooooong\nshort again". That string has three lines, two of them are short and the middle one is long. It runs the lines function on that string, which converts it to ["short", "looooooooooooooong", "short again"], which we then bind to the name allLines. That list of string is then filtered so that only those lines that are shorter than 10 characters remain in the list, producing ["short", "short again"]. And finally, unlines joins that list into a single newline delimited string, giving "short\nshort again". Let’s give it a go.

          +
          
          +i'm short
           so am i
           i am a loooooooooong line!!!
          -yeah i'm long so what hahahaha!!!!!!
          +yeah i'm long so what hahahaha!!!!!!
           short line
           loooooooooooooooooooooooooooong
           short
          -
          -
          +
          +
          
           $ ghc --make shortlinesonly
           [1 of 1] Compiling Main             ( shortlinesonly.hs, shortlinesonly.o )
           Linking shortlinesonly ...
           $ cat shortlines.txt | ./shortlinesonly
          -i'm short
          +i'm short
           so am i
           short
          -
          +

          We pipe the contents of shortlines.txt into the output of shortlinesonly and as the output, we only get the short lines.

          -

          This pattern of getting some string from the input, transforming it with a function and then outputting that is so common that there exists a function which makes that even easier, called interact. interact takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it and then print out the function’s result. Let’s modify our program to use that.

          -
          +

          This pattern of getting some string from the input, transforming it with a function and then outputting that is so common that there exists a function which makes that even easier, called interact. interact takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it and then print out the function’s result. Let’s modify our program to use that.

          +
          
           main = interact shortLinesOnly
           
           shortLinesOnly :: String -> String
          @@ -455,29 +455,29 @@ 

          Files and streams

          shortLines = filter (\line -> length line < 10) allLines result = unlines shortLines in result -
          +

          Just to show that this can be achieved in much less code (even though it will be less readable) and to demonstrate our function composition skill, we’re going to rework that a bit further.

          -
          +
          
           main = interact $ unlines . filter ((<10) . length) . lines
          -
          +

          Wow, we actually reduced that to just one line, which is pretty cool!

          -

          interact can be used to make programs that are piped some contents into them and then dump some result out or it can be used to make programs that appear to take a line of input from the user, give back some result based on that line and then take another line and so on. There isn’t actually a real distinction between the two, it just depends on how the user is supposed to use them.

          -

          Let’s make a program that continuously reads a line and then tells us if the line is a palindrome or not. We could just use getLine to read a line, tell the user if it’s a palindrome and then run main all over again. But it’s simpler if we use interact. When using interact, think about what you need to do to transform some input into the desired output. In our case, we have to replace each line of the input with either "palindrome" or "not a palindrome". So we have to write a function that transforms something like "elephant\nABCBA\nwhatever" into "not a palindrome\npalindrome\nnot a palindrome". Let’s do this!

          -
          +

          interact can be used to make programs that are piped some contents into them and then dump some result out or it can be used to make programs that appear to take a line of input from the user, give back some result based on that line and then take another line and so on. There isn’t actually a real distinction between the two, it just depends on how the user is supposed to use them.

          +

          Let’s make a program that continuously reads a line and then tells us if the line is a palindrome or not. We could just use getLine to read a line, tell the user if it’s a palindrome and then run main all over again. But it’s simpler if we use interact. When using interact, think about what you need to do to transform some input into the desired output. In our case, we have to replace each line of the input with either "palindrome" or "not a palindrome". So we have to write a function that transforms something like "elephant\nABCBA\nwhatever" into "not a palindrome\npalindrome\nnot a palindrome". Let’s do this!

          +
          
           respondPalindromes contents = unlines (map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") (lines contents))
               where   isPalindrome xs = xs == reverse xs
          -
          +

          Let’s write this in point-free.

          -
          +
          
           respondPalindromes = unlines . map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") . lines
               where   isPalindrome xs = xs == reverse xs
          -
          -

          Pretty straightforward. First it turns something like "elephant\nABCBA\nwhatever" into ["elephant", "ABCBA", "whatever"] and then it maps that lambda over it, giving ["not a palindrome", "palindrome", "not a palindrome"] and then unlines joins that list into a single, newline delimited string. Now we can do

          -
          +
          +

          Pretty straightforward. First it turns something like "elephant\nABCBA\nwhatever" into ["elephant", "ABCBA", "whatever"] and then it maps that lambda over it, giving ["not a palindrome", "palindrome", "not a palindrome"] and then unlines joins that list into a single, newline delimited string. Now we can do

          +
          
           main = interact respondPalindromes
          -
          +

          Let’s test this out:

          -
          +
          
           $ runhaskell palindromes.hs
           hehe
           not a palindrome
          @@ -486,35 +486,35 @@ 

          Files and streams

          cookie not a palindrome -
          +

          Even though we made a program that transforms one big string of input into another, it acts like we made a program that does it line by line. That’s because Haskell is lazy and it wants to print the first line of the result string, but it can’t because it doesn’t have the first line of the input yet. So as soon as we give it the first line of input, it prints the first line of the output. We get out of the program by issuing an end-of-line character.

          We can also use this program by just piping a file into it. Let’s say we have this file:

          -
          +
          
           dogaroo
           radar
           rotor
           madam
          -
          -

          and we save it as words.txt. This is what we get by piping it into our program:

          -
          +
          +

          and we save it as words.txt. This is what we get by piping it into our program:

          +
          
           $ cat words.txt | runhaskell palindromes.hs
           not a palindrome
           palindrome
           palindrome
           palindrome
          -
          -

          Again, we get the same output as if we had run our program and put in the words ourselves at the standard input. We just don’t see the input that palindromes.hs because the input came from the file and not from us typing the words in.

          +
          +

          Again, we get the same output as if we had run our program and put in the words ourselves at the standard input. We just don’t see the input that palindromes.hs because the input came from the file and not from us typing the words in.

          So now you probably see how lazy I/O works and how we can use it to our advantage. You can just think in terms of what the output is supposed to be for some given input and write a function to do that transformation. In lazy I/O, nothing is eaten from the input until it absolutely has to be because what we want to print right now depends on that input.

          -

          So far, we’ve worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we’ve already been doing that. One way to think about reading from the terminal is to imagine that it’s like reading from a (somewhat special) file. Same goes for writing to the terminal, it’s kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we’ll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

          +

          So far, we’ve worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we’ve already been doing that. One way to think about reading from the terminal is to imagine that it’s like reading from a (somewhat special) file. Same goes for writing to the terminal, it’s kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we’ll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

          We’ll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne’s #1 hit Girlfriend, and just prints it out to the terminal. Here’s girlfriend.txt:

          -
          +
          
           Hey! Hey! You! You!
          -I don't like your girlfriend!
          +I don't like your girlfriend!
           No way! No way!
           I think you need a new one!
          -
          +

          And here’s our program:

          -
          +
          
           import System.IO
           
           main = do
          @@ -522,100 +522,100 @@ 

          Files and streams

          contents <- hGetContents handle putStr contents hClose handle -
          +

          Running it, we get the expected result:

          -
          +
          
           $ runhaskell girlfriend.hs
           Hey! Hey! You! You!
          -I don't like your girlfriend!
          +I don't like your girlfriend!
           No way! No way!
           I think you need a new one!
          -
          +

          Let’s go over this line by line. The first line is just four exclamations, to get our attention. In the second line, Avril tells us that she doesn’t like our current romantic partner. The third line serves to emphasize that disapproval, whereas the fourth line suggests we should seek out a new girlfriend.

          -

          Let’s also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file’s associated handle encapsulated as its result.

          -

          FilePath is just a type synonym for String, simply defined as:

          -
          +

          Let’s also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file’s associated handle encapsulated as its result.

          +

          FilePath is just a type synonym for String, simply defined as:

          +
          
           type FilePath = String
          -
          -

          IOMode is a type that’s defined like this:

          -
          +
          +

          IOMode is a type that’s defined like this:

          +
          
           data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
          -
          +
          A FILE IN A CAKE!!! -

          Just like our type that represents the seven possible values for the days of the week, this type is an enumeration that represents what we want to do with our opened file. Very simple. Just note that this type is IOMode and not IO Mode. IO Mode would be the type of an I/O action that has a value of some type Mode as its result, but IOMode is just a simple enumeration.

          -

          Finally, it returns an I/O action that will open the specified file in the specified mode. If we bind that action to something we get a Handle. A value of type Handle represents where our file is. We’ll use that handle so we know which file to read from. It would be stupid to read a file but not bind that read to a handle because we wouldn’t be able to do anything with the file. So in our case, we bound the handle to handle.

          -

          In the next line, we see a function called hGetContents. It takes a Handle, so it knows which file to get the contents from and returns an IO String — an I/O action that holds as its result the contents of the file. This function is pretty much like getContents. The only difference is that getContents will automatically read from the standard input (that is from the terminal), whereas hGetContents takes a file handle which tells it which file to read from. In all other respects, they work the same. And just like getContents, hGetContents won’t attempt to read the file at once and store it in memory, but it will read it as needed. That’s really cool because we can treat contents as the whole contents of the file, but it’s not really loaded in memory. So if this were a really huge file, doing hGetContents wouldn’t choke up our memory, but it would read only what it needed to from the file, when it needed to.

          -

          Note the difference between the handle used to identify a file and the contents of the file, bound in our program to handle and contents. The handle is just something by which we know what our file is. If you imagine your whole file system to be a really big book and each file is a chapter in the book, the handle is a bookmark that shows where you’re currently reading (or writing) a chapter, whereas the contents are the actual chapter.

          -

          With putStr contents we just print the contents out to the standard output and then we do hClose, which takes a handle and returns an I/O action that closes the file. You have to close the file yourself after opening it with openFile!

          -

          Another way of doing what we just did is to use the withFile function, which has a type signature of withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a. It takes a path to a file, an IOMode and then it takes a function that takes a handle and returns some I/O action. What it returns is an I/O action that will open that file, do something we want with the file and then close it. The result encapsulated in the final I/O action that’s returned is the same as the result of the I/O action that the function we give it returns. This might sound a bit complicated, but it’s really simple, especially with lambdas, here’s our previous example rewritten to use withFile:

          -
          +

          Just like our type that represents the seven possible values for the days of the week, this type is an enumeration that represents what we want to do with our opened file. Very simple. Just note that this type is IOMode and not IO Mode. IO Mode would be the type of an I/O action that has a value of some type Mode as its result, but IOMode is just a simple enumeration.

          +

          Finally, it returns an I/O action that will open the specified file in the specified mode. If we bind that action to something we get a Handle. A value of type Handle represents where our file is. We’ll use that handle so we know which file to read from. It would be stupid to read a file but not bind that read to a handle because we wouldn’t be able to do anything with the file. So in our case, we bound the handle to handle.

          +

          In the next line, we see a function called hGetContents. It takes a Handle, so it knows which file to get the contents from and returns an IO String — an I/O action that holds as its result the contents of the file. This function is pretty much like getContents. The only difference is that getContents will automatically read from the standard input (that is from the terminal), whereas hGetContents takes a file handle which tells it which file to read from. In all other respects, they work the same. And just like getContents, hGetContents won’t attempt to read the file at once and store it in memory, but it will read it as needed. That’s really cool because we can treat contents as the whole contents of the file, but it’s not really loaded in memory. So if this were a really huge file, doing hGetContents wouldn’t choke up our memory, but it would read only what it needed to from the file, when it needed to.

          +

          Note the difference between the handle used to identify a file and the contents of the file, bound in our program to handle and contents. The handle is just something by which we know what our file is. If you imagine your whole file system to be a really big book and each file is a chapter in the book, the handle is a bookmark that shows where you’re currently reading (or writing) a chapter, whereas the contents are the actual chapter.

          +

          With putStr contents we just print the contents out to the standard output and then we do hClose, which takes a handle and returns an I/O action that closes the file. You have to close the file yourself after opening it with openFile!

          +

          Another way of doing what we just did is to use the withFile function, which has a type signature of withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a. It takes a path to a file, an IOMode and then it takes a function that takes a handle and returns some I/O action. What it returns is an I/O action that will open that file, do something we want with the file and then close it. The result encapsulated in the final I/O action that’s returned is the same as the result of the I/O action that the function we give it returns. This might sound a bit complicated, but it’s really simple, especially with lambdas, here’s our previous example rewritten to use withFile:

          +
          
           import System.IO
           
           main = do
               withFile "girlfriend.txt" ReadMode (\handle -> do
                   contents <- hGetContents handle
                   putStr contents)
          -
          -

          As you can see, it’s very similar to the previous piece of code. (\handle -> ... ) is the function that takes a handle and returns an I/O action and it’s usually done like this, with a lambda. The reason it has to take a function that returns an I/O action instead of just taking an I/O action to do and then close the file is because the I/O action that we’d pass to it wouldn’t know on which file to operate. This way, withFile opens the file and then passes the handle to the function we gave it. It gets an I/O action back from that function and then makes an I/O action that’s just like it, only it closes the file afterwards. Here’s how we can make our own withFile function:

          -
          -withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
          -withFile' path mode f = do
          +
          +

          As you can see, it’s very similar to the previous piece of code. (\handle -> ... ) is the function that takes a handle and returns an I/O action and it’s usually done like this, with a lambda. The reason it has to take a function that returns an I/O action instead of just taking an I/O action to do and then close the file is because the I/O action that we’d pass to it wouldn’t know on which file to operate. This way, withFile opens the file and then passes the handle to the function we gave it. It gets an I/O action back from that function and then makes an I/O action that’s just like it, only it closes the file afterwards. Here’s how we can make our own withFile function:

          +
          
          +withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
          +withFile' path mode f = do
               handle <- openFile path mode
               result <- f handle
               hClose handle
               return result
          -
          +
          butter toast -

          We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

          -

          Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

          +

          We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

          +

          Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

          Loading files and then treating their contents as strings is so common that we have these three nice little functions to make our work even easier:

          -readFile has a type signature of readFile :: FilePath -> IO String. -Remember, FilePath is just a fancy name for String. -readFile takes a path to a file and returns an I/O action that will read that file (lazily, of course) and bind its contents to something as a string. -It’s usually more handy than doing openFile and binding it to a handle and then doing hGetContents. -Here’s how we could have written our previous example with readFile: +readFile has a type signature of readFile :: FilePath -> IO String. +Remember, FilePath is just a fancy name for String. +readFile takes a path to a file and returns an I/O action that will read that file (lazily, of course) and bind its contents to something as a string. +It’s usually more handy than doing openFile and binding it to a handle and then doing hGetContents. +Here’s how we could have written our previous example with readFile:

          -
          +
          
           import System.IO
           
           main = do
               contents <- readFile "girlfriend.txt"
               putStr contents
          -
          +

          -Because we don’t get a handle with which to identify our file, we can’t close it manually, so Haskell does that for us when we use readFile. +Because we don’t get a handle with which to identify our file, we can’t close it manually, so Haskell does that for us when we use readFile.

          -writeFile has a type of writeFile :: FilePath -> String -> IO (). +writeFile has a type of writeFile :: FilePath -> String -> IO (). It takes a path to a file and a string to write to that file and returns an I/O action that will do the writing. If such a file already exists, it will be stomped down to zero length before being written on. Here’s how to turn girlfriend.txt into a CAPSLOCKED version and write it to girlfriendcaps.txt:

          -
          +
          
           import System.IO
           import Data.Char
           
           main = do
               contents <- readFile "girlfriend.txt"
               writeFile "girlfriendcaps.txt" (map toUpper contents)
          -
          -
          +
          +
          
           $ runhaskell girlfriendtocaps.hs
           $ cat girlfriendcaps.txt
           HEY! HEY! YOU! YOU!
          -I DON'T LIKE YOUR GIRLFRIEND!
          +I DON'T LIKE YOUR GIRLFRIEND!
           NO WAY! NO WAY!
           I THINK YOU NEED A NEW ONE!
          -
          -

          appendFile has a type signature that’s just like writeFile, only appendFile doesn’t truncate the file to zero length if it already exists but it appends stuff to it.

          +
          +

          appendFile has a type signature that’s just like writeFile, only appendFile doesn’t truncate the file to zero length if it already exists but it appends stuff to it.

          Let’s say we have a file todo.txt that has one task per line that we have to do. Now let’s make a program that takes a line from the standard input and adds that to our to-do list.

          -
          +
          
           import System.IO
           
           main = do
               todoItem <- getLine
               appendFile "todo.txt" (todoItem ++ "\n")
          -
          -
          +
          +
          
           $ runhaskell appendtodo.hs
           Iron the dishes
           $ runhaskell appendtodo.hs
          @@ -626,31 +626,31 @@ 

          Files and streams

          Iron the dishes Dust the dog Take salad out of the oven -
          -

          We needed to add the "\n" to the end of each line because getLine doesn’t give us a newline character at the end.

          -

          Ooh, one more thing. We talked about how doing contents <- hGetContents handle doesn’t cause the whole file to be read at once and stored in-memory. It’s I/O lazy, so doing this:

          -
          +
          +

          We needed to add the "\n" to the end of each line because getLine doesn’t give us a newline character at the end.

          +

          Ooh, one more thing. We talked about how doing contents <- hGetContents handle doesn’t cause the whole file to be read at once and stored in-memory. It’s I/O lazy, so doing this:

          +
          
           main = do
               withFile "something.txt" ReadMode (\handle -> do
                   contents <- hGetContents handle
                   putStr contents)
          -
          +

          is actually like connecting a pipe from the file to the output. Just like you can think of lists as streams, you can also think of files as streams. This will read one line at a time and print it out to the terminal as it goes along. So you may be asking, how wide is this pipe then? How often will the disk be accessed? Well, for text files, the default buffering is line-buffering usually. That means that the smallest part of the file to be read at once is one line. That’s why in this case it actually reads a line, prints it to the output, reads the next line, prints it, etc. For binary files, the default buffering is usually block-buffering. That means that it will read the file chunk by chunk. The chunk size is some size that your operating system thinks is cool.

          -

          You can control how exactly buffering is done by using the hSetBuffering function. It takes a handle and a BufferMode and returns an I/O action that sets the buffering. BufferMode is a simple enumeration data type and the possible values it can hold are: NoBuffering, LineBuffering or BlockBuffering (Maybe Int). The Maybe Int is for how big the chunk should be, in bytes. If it’s Nothing, then the operating system determines the chunk size. NoBuffering means that it will be read one character at a time. NoBuffering usually sucks as a buffering mode because it has to access the disk so much.

          +

          You can control how exactly buffering is done by using the hSetBuffering function. It takes a handle and a BufferMode and returns an I/O action that sets the buffering. BufferMode is a simple enumeration data type and the possible values it can hold are: NoBuffering, LineBuffering or BlockBuffering (Maybe Int). The Maybe Int is for how big the chunk should be, in bytes. If it’s Nothing, then the operating system determines the chunk size. NoBuffering means that it will be read one character at a time. NoBuffering usually sucks as a buffering mode because it has to access the disk so much.

          Here’s our previous piece of code, only it doesn’t read it line by line but reads the whole file in chunks of 2048 bytes.

          -
          +
          
           main = do
               withFile "something.txt" ReadMode (\handle -> do
                   hSetBuffering handle $ BlockBuffering (Just 2048)
                   contents <- hGetContents handle
                   putStr contents)
          -
          +

          Reading files in bigger chunks can help if we want to minimize disk access or when our file is actually a slow network resource.

          -

          We can also use hFlush, which is a function that takes a handle and returns an I/O action that will flush the buffer of the file associated with the handle. When we’re doing line-buffering, the buffer is flushed after every line. When we’re doing block-buffering, it’s after we’ve read a chunk. It’s also flushed after closing a handle. That means that when we’ve reached a newline character, the reading (or writing) mechanism reports all the data so far. But we can use hFlush to force that reporting of data that has been read so far. After flushing, the data is available to other programs that are running at the same time.

          -

          Think of reading a block-buffered file like this: your toilet bowl is set to flush itself after it has one gallon of water inside it. So you start pouring in water and once the gallon mark is reached, that water is automatically flushed and the data in the water that you’ve poured in so far is read. But you can flush the toilet manually too by pressing the button on the toilet. This makes the toilet flush and all the water (data) inside the toilet is read. In case you haven’t noticed, flushing the toilet manually is a metaphor for hFlush. This is not a very great analogy by programming analogy standards, but I wanted a real world object that can be flushed for the punchline.

          -

          We already made a program to add a new item to our to-do list in todo.txt, now let’s make a program to remove an item. I’ll just paste the code and then we’ll go over the program together so you see that it’s really easy. We’ll be using a few new functions from System.Directory and one new function from System.IO, but they’ll all be explained.

          +

          We can also use hFlush, which is a function that takes a handle and returns an I/O action that will flush the buffer of the file associated with the handle. When we’re doing line-buffering, the buffer is flushed after every line. When we’re doing block-buffering, it’s after we’ve read a chunk. It’s also flushed after closing a handle. That means that when we’ve reached a newline character, the reading (or writing) mechanism reports all the data so far. But we can use hFlush to force that reporting of data that has been read so far. After flushing, the data is available to other programs that are running at the same time.

          +

          Think of reading a block-buffered file like this: your toilet bowl is set to flush itself after it has one gallon of water inside it. So you start pouring in water and once the gallon mark is reached, that water is automatically flushed and the data in the water that you’ve poured in so far is read. But you can flush the toilet manually too by pressing the button on the toilet. This makes the toilet flush and all the water (data) inside the toilet is read. In case you haven’t noticed, flushing the toilet manually is a metaphor for hFlush. This is not a very great analogy by programming analogy standards, but I wanted a real world object that can be flushed for the punchline.

          +

          We already made a program to add a new item to our to-do list in todo.txt, now let’s make a program to remove an item. I’ll just paste the code and then we’ll go over the program together so you see that it’s really easy. We’ll be using a few new functions from System.Directory and one new function from System.IO, but they’ll all be explained.

          Anyway, here’s the program for removing an item from todo.txt:

          -
          +
          
           import System.IO
           import System.Directory
           import Data.List
          @@ -672,17 +672,17 @@ 

          Files and streams

          hClose tempHandle removeFile "todo.txt" renameFile tempName "todo.txt" -
          -

          At first, we just open todo.txt in read mode and bind its handle to handle.

          -

          Next up, we use a function that we haven’t met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it’s better practice to use openTempFile so you know you’re probably not overwriting anything. +

          +

          At first, we just open todo.txt in read mode and bind its handle to handle.

          +

          Next up, we use a function that we haven’t met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it’s better practice to use openTempFile so you know you’re probably not overwriting anything.

          -

          The reason we didn’t use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

          -

          Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

          -

          We ask the user which one they want to delete and wait for them to enter a number. Let’s say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

          -

          Remember the delete and !! functions from Data.List. !! returns an element from a list with some index and delete deletes the first occurrence of an element in a list and returns a new list without that occurrence. (todoTasks !! number) (number is now 1) returns "Dust the dog". We bind todoTasks without the first occurrence of "Dust the dog" to newTodoItems and then join that into a single string with unlines before writing it to the temporary file that we opened. The old file is now unchanged and the temporary file contains all the lines that the old one does, except the one we deleted.

          -

          After that we close both the original and the temporary files and then we remove the original one with removeFile, which, as you can see, takes a path to a file and deletes it. After deleting the old todo.txt, we use renameFile to rename the temporary file to todo.txt. Be careful, removeFile and renameFile (which are both in System.Directory by the way) take file paths as their parameters, not handles.

          +

          The reason we didn’t use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

          +

          Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

          +

          We ask the user which one they want to delete and wait for them to enter a number. Let’s say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

          +

          Remember the delete and !! functions from Data.List. !! returns an element from a list with some index and delete deletes the first occurrence of an element in a list and returns a new list without that occurrence. (todoTasks !! number) (number is now 1) returns "Dust the dog". We bind todoTasks without the first occurrence of "Dust the dog" to newTodoItems and then join that into a single string with unlines before writing it to the temporary file that we opened. The old file is now unchanged and the temporary file contains all the lines that the old one does, except the one we deleted.

          +

          After that we close both the original and the temporary files and then we remove the original one with removeFile, which, as you can see, takes a path to a file and deletes it. After deleting the old todo.txt, we use renameFile to rename the temporary file to todo.txt. Be careful, removeFile and renameFile (which are both in System.Directory by the way) take file paths as their parameters, not handles.

          And that’s that! We could have done this in even fewer lines, but we were very careful not to overwrite any existing files and politely asked the operating system to tell us where we can put our temporary file. Let’s give this a go!

          -
          +
          
           $ runhaskell deletetodo.hs
           These are your TO-DO items:
           0 - Iron the dishes
          @@ -704,16 +704,16 @@ 

          Files and streams

          $ cat todo.txt Take salad out of the oven -
          +

          Command line arguments

          COMMAND LINE ARGUMENTS!!! ARGH

          Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line arguments of a program.

          In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

          One way to solve that is to always ask the user which file they want to use as their to-do list. We used that approach when we wanted to know which item the user wants to delete. It works, but it’s not so good, because it requires the user to run the program, wait for the program to ask something and then tell that to the program. That’s called an interactive program and the difficult bit with interactive command line programs is this — what if you want to automate the execution of that program, like with a batch script? It’s harder to make a batch script that interacts with a program than a batch script that just calls one program or several of them.

          That’s why it’s sometimes better to have the user tell the program what they want when they run the program, instead of having the program ask the user once it’s run. And what better way to have the user tell the program what they want it to do when they run it than via command line arguments!

          -

          The System.Environment module has two cool I/O actions. One is getArgs, which has a type of getArgs :: IO [String] and is an I/O action that will get the arguments that the program was run with and have as its contained result a list with the arguments. getProgName has a type of getProgName :: IO String and is an I/O action that contains the program name.

          +

          The System.Environment module has two cool I/O actions. One is getArgs, which has a type of getArgs :: IO [String] and is an I/O action that will get the arguments that the program was run with and have as its contained result a list with the arguments. getProgName has a type of getProgName :: IO String and is an I/O action that contains the program name.

          Here’s a small program that demonstrates how these two work:

          -
          +
          
            import System.Environment
            import Data.List
           
          @@ -724,9 +724,9 @@ 

          Command line arguments

          mapM putStrLn args putStrLn "The program name is:" putStrLn progName -
          -

          We bind getArgs and progName to args and progName. We say The arguments are: and then for every argument in args, we do putStrLn. Finally, we also print out the program name. Let’s compile this as arg-test.

          -
          +
          +

          We bind getArgs and progName to args and progName. We say The arguments are: and then for every argument in args, we do putStrLn. Finally, we also print out the program name. Let’s compile this as arg-test.

          +
          
           $ ./arg-test first second w00t "multi word arg"
           The arguments are:
           first
          @@ -735,7 +735,7 @@ 

          Command line arguments

          multi word arg The program name is: arg-test -
          +

          Nice. Armed with this knowledge you could create some cool command line apps. In fact, let’s go ahead and make one. In the previous section, we made a separate program for adding tasks and a separate program for deleting them. Now, we’re going to join that into one program, what it does will depend on the command line arguments. We’re also going to make it so it can operate on different files, not just todo.txt.

          We’ll call it simply todo and it’ll be able to do (haha!) three different things:

            @@ -744,9 +744,9 @@

            Command line arguments

          • Delete tasks

          We’re not going to concern ourselves with possible bad input too much right now.

          -

          Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we’ll just do todo view todo.txt and to remove the task with the index of 2, we’ll do todo remove todo.txt 2.

          -

          We’ll start by making a dispatch association list. It’s going to be a simple association list that has command line arguments as keys and functions as their corresponding values. All these functions will be of type [String] -> IO (). They’re going to take the argument list as a parameter and return an I/O action that does the viewing, adding, deleting, etc.

          -
          +

          Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we’ll just do todo view todo.txt and to remove the task with the index of 2, we’ll do todo remove todo.txt 2.

          +

          We’ll start by making a dispatch association list. It’s going to be a simple association list that has command line arguments as keys and functions as their corresponding values. All these functions will be of type [String] -> IO (). They’re going to take the argument list as a parameter and return an I/O action that does the viewing, adding, deleting, etc.

          +
          
           import System.Environment
           import System.Directory
           import System.IO
          @@ -757,35 +757,35 @@ 

          Command line arguments

          , ("view", view) , ("remove", remove) ] -
          -

          We have yet to define main, add, view and remove, so let’s start with main:

          -
          +
          +

          We have yet to define main, add, view and remove, so let’s start with main:

          +
          
           main = do
               (command:args) <- getArgs
               let (Just action) = lookup command dispatch
               action args
          -
          -

          First, we get the arguments and bind them to (command:args). If you remember your pattern matching, this means that the first argument will get bound to command and the rest of them will get bound to args. If we call our program like todo add todo.txt "Spank the monkey", command will be "add" and args will be ["todo.txt", "Spank the monkey"].

          -

          In the next line, we look up our command in the dispatch list. Because "add" points to add, we get Just add as a result. We use pattern matching again to extract our function out of the Maybe. What happens if our command isn’t in the dispatch list? Well then the lookup will return Nothing, but we said we won’t concern ourselves with failing gracefully too much, so the pattern matching will fail and our program will throw a fit.

          -

          Finally, we call our action function with the rest of the argument list. That will return an I/O action that either adds an item, displays a list of items or deletes an item and because that action is part of the main do block, it will get performed. If we follow our concrete example so far and our action function is add, it will get called with args (so ["todo.txt", "Spank the monkey"]) and return an I/O action that adds Spank the monkey to todo.txt.

          -

          Great! All that’s left now is to implement add, view and remove. Let’s start with add:

          -
          +
          +

          First, we get the arguments and bind them to (command:args). If you remember your pattern matching, this means that the first argument will get bound to command and the rest of them will get bound to args. If we call our program like todo add todo.txt "Spank the monkey", command will be "add" and args will be ["todo.txt", "Spank the monkey"].

          +

          In the next line, we look up our command in the dispatch list. Because "add" points to add, we get Just add as a result. We use pattern matching again to extract our function out of the Maybe. What happens if our command isn’t in the dispatch list? Well then the lookup will return Nothing, but we said we won’t concern ourselves with failing gracefully too much, so the pattern matching will fail and our program will throw a fit.

          +

          Finally, we call our action function with the rest of the argument list. That will return an I/O action that either adds an item, displays a list of items or deletes an item and because that action is part of the main do block, it will get performed. If we follow our concrete example so far and our action function is add, it will get called with args (so ["todo.txt", "Spank the monkey"]) and return an I/O action that adds Spank the monkey to todo.txt.

          +

          Great! All that’s left now is to implement add, view and remove. Let’s start with add:

          +
          
           add :: [String] -> IO ()
           add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
          -
          -

          If we call our program like todo add todo.txt "Spank the monkey", the "add" will get bound to command in the first pattern match in the main block, whereas ["todo.txt", "Spank the monkey"] will get passed to the function that we get from the dispatch list. So, because we’re not dealing with bad input right now, we just pattern match against a list with those two elements right away and return an I/O action that appends that line to the end of the file, along with a newline character.

          -

          Next, let’s implement the list viewing functionality. If we want to view the items in a file, we do todo view todo.txt. So in the first pattern match, command will be "view" and args will be ["todo.txt"].

          -
          +
          +

          If we call our program like todo add todo.txt "Spank the monkey", the "add" will get bound to command in the first pattern match in the main block, whereas ["todo.txt", "Spank the monkey"] will get passed to the function that we get from the dispatch list. So, because we’re not dealing with bad input right now, we just pattern match against a list with those two elements right away and return an I/O action that appends that line to the end of the file, along with a newline character.

          +

          Next, let’s implement the list viewing functionality. If we want to view the items in a file, we do todo view todo.txt. So in the first pattern match, command will be "view" and args will be ["todo.txt"].

          +
          
           view :: [String] -> IO ()
           view [fileName] = do
               contents <- readFile fileName
               let todoTasks = lines contents
                   numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
               putStr $ unlines numberedTasks
          -
          +

          We already did pretty much the same thing in the program that only deleted tasks when we were displaying the tasks so that the user can choose one for deletion, only here we just display the tasks.

          -

          And finally, we’re going to implement remove. It’s going to be very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, check out the explanation under that program. The main difference is that we’re not hardcoding todo.txt but getting it as an argument. We’re also not prompting the user for the task number to delete, we’re getting it as an argument.

          -
          +

          And finally, we’re going to implement remove. It’s going to be very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, check out the explanation under that program. The main difference is that we’re not hardcoding todo.txt but getting it as an argument. We’re also not prompting the user for the task number to delete, we’re getting it as an argument.

          +
          
           remove :: [String] -> IO ()
           remove [fileName, numberString] = do
               handle <- openFile fileName ReadMode
          @@ -799,10 +799,10 @@ 

          Command line arguments

          hClose tempHandle removeFile fileName renameFile tempName fileName -
          -

          We opened up the file based on fileName and opened a temporary file, deleted the line with the index that the user wants to delete, wrote that to the temporary file, removed the original file and renamed the temporary file back to fileName.

          +
          +

          We opened up the file based on fileName and opened a temporary file, deleted the line with the index that the user wants to delete, wrote that to the temporary file, removed the original file and renamed the temporary file back to fileName.

          Here’s the whole program at once, in all its glory!

          -
          +
          
           import System.Environment
           import System.Directory
           import System.IO
          @@ -842,12 +842,12 @@ 

          Command line arguments

          hClose tempHandle removeFile fileName renameFile tempName fileName -
          +
          fresh baked salad

          To summarize our solution: we made a dispatch association that maps from commands to functions that take some command line arguments and return an I/O action. We see what the command is and based on that we get the appropriate function from the dispatch list. We call that function with the rest of the command line arguments to get back an I/O action that will do the appropriate thing and then just perform that action!

          In other languages, we might have implemented this with a big switch case statement or whatever, but using higher order functions allows us to just tell the dispatch list to give us the appropriate function and then tell that function to give us an I/O action for some command line arguments.

          Let’s try our app out!

          -
          +
          
           $ ./todo view todo.txt
           0 - Iron the dishes
           1 - Dust the dog
          @@ -867,68 +867,68 @@ 

          Command line arguments

          0 - Iron the dishes 1 - Dust the dog 2 - Pick up children from drycleaners -
          -

          Another cool thing about this is that it’s easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you’re laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

          -

          You could make this program fail a bit more gracefully in case of bad input (for example, if someone runs todo UP YOURS HAHAHAHA) by making an I/O action that just reports there has been an error (say, errorExit :: IO ()) and then check for possible erroneous input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

          +
          +

          Another cool thing about this is that it’s easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you’re laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

          +

          You could make this program fail a bit more gracefully in case of bad input (for example, if someone runs todo UP YOURS HAHAHAHA) by making an I/O action that just reports there has been an error (say, errorExit :: IO ()) and then check for possible erroneous input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

          Randomness

          this picture is the ultimate source of randomness and wackiness

          Many times while programming, you need to get some random data. Maybe you’re making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of uses for random data when programming. Well, actually, pseudo-random, because we all know that the only true source of randomness is a monkey on a unicycle with a cheese in one hand and its butt in the other. In this section, we’ll take a look at how to make Haskell generate seemingly random data.

          In most other programming languages, you have functions that give you back some random number. Each time you call that function, you get back a (hopefully) different random number. How about Haskell? Well, remember, Haskell is a pure functional language. What that means is that it has referential transparency. What THAT means is that a function, if given the same parameters twice, must produce the same result twice. That’s really cool because it allows us to reason differently about programs and it enables us to defer evaluation until we really need it. If I call a function, I can be sure that it won’t do any funny stuff before giving me the results. All that matters are its results. However, this makes it a bit tricky for getting random numbers. If I have a function like this:

          -
          +
          
           randomNumber :: (Num a) => a
           randomNumber = 4
          -
          -

          It’s not very useful as a random number function because it will always return 4, even though I can assure you that the 4 is completely random, because I used a die to determine it.

          +
          +

          It’s not very useful as a random number function because it will always return 4, even though I can assure you that the 4 is completely random, because I used a die to determine it.

          How do other languages make seemingly random numbers? Well, they take various info from your computer, like the current time, how much and where you moved your mouse and what kind of noises you made behind your computer and based on that, give a number that looks really random. The combination of those factors (that randomness) is probably different in any given moment in time, so you get a different random number.

          Ah. So in Haskell, we can make a random number then if we make a function that takes as its parameter that randomness and based on that returns some number (or other data type).

          -

          Enter the System.Random module. It has all the functions that satisfy our need for randomness. Let’s just dive into one of the functions it exports then, namely random. Here’s its type: random :: (RandomGen g, Random a) => g -> (a, g). Whoa! Some new typeclasses in this type declaration up in here! The RandomGen typeclass is for types that can act as sources of randomness. The Random typeclass is for things that can take on random values. A boolean value can take on a random value, namely True or False. A number can also take up a plethora of different random values. Can a function take on a random value? I don’t think so, probably not! If we try to translate the type declaration of random to English, we get something like: it takes a random generator (that’s our source of randomness) and returns a random value and a new random generator. Why does it also return a new generator as well as a random value? Well, we’ll see in a moment.

          -

          To use our random function, we have to get our hands on one of those random generators. The System.Random module exports a cool type, namely StdGen that is an instance of the RandomGen typeclass. We can either make a StdGen manually or we can tell the system to give us one based on a multitude of sort of random stuff.

          -

          To manually make a random generator, use the mkStdGen function. It has a type of mkStdGen :: Int -> StdGen. It takes an integer and based on that, gives us a random generator. Okay then, let’s try using random and mkStdGen in tandem to get a (hardly random) number.

          -
          +

          Enter the System.Random module. It has all the functions that satisfy our need for randomness. Let’s just dive into one of the functions it exports then, namely random. Here’s its type: random :: (RandomGen g, Random a) => g -> (a, g). Whoa! Some new typeclasses in this type declaration up in here! The RandomGen typeclass is for types that can act as sources of randomness. The Random typeclass is for things that can take on random values. A boolean value can take on a random value, namely True or False. A number can also take up a plethora of different random values. Can a function take on a random value? I don’t think so, probably not! If we try to translate the type declaration of random to English, we get something like: it takes a random generator (that’s our source of randomness) and returns a random value and a new random generator. Why does it also return a new generator as well as a random value? Well, we’ll see in a moment.

          +

          To use our random function, we have to get our hands on one of those random generators. The System.Random module exports a cool type, namely StdGen that is an instance of the RandomGen typeclass. We can either make a StdGen manually or we can tell the system to give us one based on a multitude of sort of random stuff.

          +

          To manually make a random generator, use the mkStdGen function. It has a type of mkStdGen :: Int -> StdGen. It takes an integer and based on that, gives us a random generator. Okay then, let’s try using random and mkStdGen in tandem to get a (hardly random) number.

          +
          
           ghci> random (mkStdGen 100)
          -
          -
          +
          +
          
           <interactive>:1:0:
          -    Ambiguous type variable `a' in the constraint:
          -      `Random a' arising from a use of `random' at <interactive>:1:0-20
          +    Ambiguous type variable `a' in the constraint:
          +      `Random a' arising from a use of `random' at <interactive>:1:0-20
               Probable fix: add a type signature that fixes these type variable(s)
          -
          -

          What’s this? Ah, right, the random function can return a value of any type that’s part of the Random typeclass, so we have to inform Haskell what kind of type we want. Also let’s not forget that it returns a random value and a random generator in a pair.

          -
          +
          +

          What’s this? Ah, right, the random function can return a value of any type that’s part of the Random typeclass, so we have to inform Haskell what kind of type we want. Also let’s not forget that it returns a random value and a random generator in a pair.

          +
          
           ghci> random (mkStdGen 100) :: (Int, StdGen)
           (-1352021624,651872571 1655838864)
          -
          -

          Finally! A number that looks kind of random! The first component of the tuple is our number whereas the second component is a textual representation of our new random generator. What happens if we call random with the same random generator again?

          -
          +
          +

          Finally! A number that looks kind of random! The first component of the tuple is our number whereas the second component is a textual representation of our new random generator. What happens if we call random with the same random generator again?

          +
          
           ghci> random (mkStdGen 100) :: (Int, StdGen)
           (-1352021624,651872571 1655838864)
          -
          +

          Of course. The same result for the same parameters. So let’s try giving it a different random generator as a parameter.

          -
          +
          
           ghci> random (mkStdGen 949494) :: (Int, StdGen)
           (539963926,466647808 1655838864)
          -
          +

          Alright, cool, great, a different number. We can use the type annotation to get different types back from that function.

          -
          +
          
           ghci> random (mkStdGen 949488) :: (Float, StdGen)
           (0.8938442,1597344447 1655838864)
           ghci> random (mkStdGen 949488) :: (Bool, StdGen)
           (False,1485632275 40692)
           ghci> random (mkStdGen 949488) :: (Integer, StdGen)
           (1691547873,1597344447 1655838864)
          -
          -

          Let’s make a function that simulates tossing a coin three times. If random didn’t return a new generator along with a random value, we’d have to make this function take three random generators as a parameter and then return coin tosses for each of them. But that sounds wrong because if one generator can make a random value of type Int (which can take on a load of different values), it should be able to make three coin tosses (which can take on precisely eight combinations). So this is where random returning a new generator along with a value really comes in handy.

          -

          We’ll represent a coin with a simple Bool. True is tails, False is heads.

          -
          +
          +

          Let’s make a function that simulates tossing a coin three times. If random didn’t return a new generator along with a random value, we’d have to make this function take three random generators as a parameter and then return coin tosses for each of them. But that sounds wrong because if one generator can make a random value of type Int (which can take on a load of different values), it should be able to make three coin tosses (which can take on precisely eight combinations). So this is where random returning a new generator along with a value really comes in handy.

          +

          We’ll represent a coin with a simple Bool. True is tails, False is heads.

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

          We call random with the generator we got as a parameter to get a coin and a new generator. Then we call it again, only this time with our new generator, to get the second coin. We do the same for the third coin. Had we called it with the same generator every time, all the coins would have had the same value and we’d only be able to get (False, False, False) or (True, True, True) as a result.

          -
          +
          +

          We call random with the generator we got as a parameter to get a coin and a new generator. Then we call it again, only this time with our new generator, to get the second coin. We do the same for the third coin. Had we called it with the same generator every time, all the coins would have had the same value and we’d only be able to get (False, False, False) or (True, True, True) as a result.

          +
          
           ghci> threeCoins (mkStdGen 21)
           (True,True,True)
           ghci> threeCoins (mkStdGen 22)
          @@ -937,56 +937,56 @@ 

          Randomness

          (True,False,True) ghci> threeCoins (mkStdGen 944) (True,True,True) -
          -

          Notice that we didn’t have to do random gen :: (Bool, StdGen). That’s because we already specified that we want booleans in the type declaration of the function. That’s why Haskell can infer that we want a boolean value in this case.

          -

          So what if we want to flip four coins? Or five? Well, there’s a function called randoms that takes a generator and returns an infinite sequence of values based on that generator.

          -
          +
          +

          Notice that we didn’t have to do random gen :: (Bool, StdGen). That’s because we already specified that we want booleans in the type declaration of the function. That’s why Haskell can infer that we want a boolean value in this case.

          +

          So what if we want to flip four coins? Or five? Well, there’s a function called randoms that takes a generator and returns an infinite sequence of values based on that generator.

          +
          
           ghci> take 5 $ randoms (mkStdGen 11) :: [Int]
           [-1807975507,545074951,-1015194702,-1622477312,-502893664]
           ghci> take 5 $ randoms (mkStdGen 11) :: [Bool]
           [True,True,True,True,False]
           ghci> take 5 $ randoms (mkStdGen 11) :: [Float]
           [7.904789e-2,0.62691015,0.26363158,0.12223756,0.38291094]
          -
          -

          Why doesn’t randoms return a new generator as well as a list? We could implement the randoms function very easily like this:

          -
          -randoms' :: (RandomGen g, Random a) => g -> [a]
          -randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
          -
          +
          +

          Why doesn’t randoms return a new generator as well as a list? We could implement the randoms function very easily like this:

          +
          
          +randoms' :: (RandomGen g, Random a) => g -> [a]
          +randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
          +

          A recursive definition. We get a random value and a new generator from the current generator and then make a list that has the value as its head and random numbers based on the new generator as its tail. Because we have to be able to potentially generate an infinite amount of numbers, we can’t give the new random generator back.

          We could make a function that generates a finite stream of numbers and a new generator like this:

          -
          +
          
           finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)
           finiteRandoms 0 gen = ([], gen)
           finiteRandoms n gen =
               let (value, newGen) = random gen
                   (restOfList, finalGen) = finiteRandoms (n-1) newGen
               in  (value:restOfList, finalGen)
          -
          +

          Again, a recursive definition. We say that if we want 0 numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

          -

          What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it’s kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

          -
          +

          What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it’s kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

          +
          
           ghci> randomR (1,6) (mkStdGen 359353)
           (6,1494289578 40692)
           ghci> randomR (1,6) (mkStdGen 35935335)
           (3,1250031057 40692)
          -
          -

          There’s also randomRs, which produces a stream of random values within our defined ranges. Check this out:

          -
          -ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
          +
          +

          There’s also randomRs, which produces a stream of random values within our defined ranges. Check this out:

          +
          
          +ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
           "ndkxbvmomg"
          -
          +

          Nice, looks like a super secret password or something.

          -

          You may be asking yourself, what does this section have to do with I/O anyway? We haven’t done anything concerning I/O so far. Well, so far we’ve always made our random number generator manually by making it with some arbitrary integer. The problem is, if we do that in our real programs, they will always return the same random numbers, which is no good for us. That’s why System.Random offers the getStdGen I/O action, which has a type of IO StdGen. When your program starts, it asks the system for a good random number generator and stores that in a so-called global generator. getStdGen fetches you that global random generator when you bind it to something.

          +

          You may be asking yourself, what does this section have to do with I/O anyway? We haven’t done anything concerning I/O so far. Well, so far we’ve always made our random number generator manually by making it with some arbitrary integer. The problem is, if we do that in our real programs, they will always return the same random numbers, which is no good for us. That’s why System.Random offers the getStdGen I/O action, which has a type of IO StdGen. When your program starts, it asks the system for a good random number generator and stores that in a so-called global generator. getStdGen fetches you that global random generator when you bind it to something.

          Here’s a simple program that generates a random string.

          -
          +
          
           import System.Random
           
           main = do
               gen <- getStdGen
          -    putStr $ take 20 (randomRs ('a','z') gen)
          -
          -
          +    putStr $ take 20 (randomRs ('a','z') gen)
          +
          +
          
           $ runhaskell random_string.hs
           pybphhzzhuepknbykxhe
           $ runhaskell random_string.hs
          @@ -995,43 +995,43 @@ 

          Randomness

          nzdceoconysdgcyqjruo $ runhaskell random_string.hs bakzhnnuzrkgvesqplrx -
          -

          Be careful though, just performing getStdGen twice will ask the system for the same global generator twice. If you do this:

          -
          +
          +

          Be careful though, just performing getStdGen twice will ask the system for the same global generator twice. If you do this:

          +
          
           import System.Random
           
           main = do
               gen <- getStdGen
          -    putStrLn $ take 20 (randomRs ('a','z') gen)
          +    putStrLn $ take 20 (randomRs ('a','z') gen)
               gen2 <- getStdGen
          -    putStr $ take 20 (randomRs ('a','z') gen2)
          -
          -

          you will get the same string printed out twice! One way to get two different strings of length 20 is to set up an infinite stream and then take the first 20 characters and print them out in one line and then take the second set of 20 characters and print them out in the second line. For this, we can use the splitAt function from Data.List, which splits a list at some index and returns a tuple that has the first part as the first component and the second part as the second component.

          -
          +    putStr $ take 20 (randomRs ('a','z') gen2)
          +
          +

          you will get the same string printed out twice! One way to get two different strings of length 20 is to set up an infinite stream and then take the first 20 characters and print them out in one line and then take the second set of 20 characters and print them out in the second line. For this, we can use the splitAt function from Data.List, which splits a list at some index and returns a tuple that has the first part as the first component and the second part as the second component.

          +
          
           import System.Random
           import Data.List
           
           main = do
               gen <- getStdGen
          -    let randomChars = randomRs ('a','z') gen
          +    let randomChars = randomRs ('a','z') gen
                   (first20, rest) = splitAt 20 randomChars
                   (second20, _) = splitAt 20 rest
               putStrLn first20
               putStr second20
          -
          -

          Another way is to use the newStdGen action, which splits our current random generator into two generators. It updates the global random generator with one of them and encapsulates the other as its result.

          -
          +
          +

          Another way is to use the newStdGen action, which splits our current random generator into two generators. It updates the global random generator with one of them and encapsulates the other as its result.

          +
          
           import System.Random
           
           main = do
               gen <- getStdGen
          -    putStrLn $ take 20 (randomRs ('a','z') gen)
          -    gen' <- newStdGen
          -    putStr $ take 20 (randomRs ('a','z') gen')
          -
          -

          Not only do we get a new random generator when we bind newStdGen to something, the global one gets updated as well, so if we do getStdGen again and bind it to something, we’ll get a generator that’s not the same as gen.

          + putStrLn $ take 20 (randomRs ('a','z') gen) + gen' <- newStdGen + putStr $ take 20 (randomRs ('a','z') gen') +
          +

          Not only do we get a new random generator when we bind newStdGen to something, the global one gets updated as well, so if we do getStdGen again and bind it to something, we’ll get a generator that’s not the same as gen.

          Here’s a little program that will make the user guess which number it’s thinking of.

          -
          +
          
           import System.Random
           import Control.Monad(when)
           
          @@ -1050,14 +1050,14 @@ 

          Randomness

          then putStrLn "You are correct!" else putStrLn $ "Sorry, it was " ++ show randNumber askForNumber newGen -
          +
          jack of diamonds -

          We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

          -

          Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

          -

          We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

          -

          main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

          +

          We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

          +

          Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

          +

          We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

          +

          main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

          Here’s our program in action!

          -
          +
          
           $ runhaskell guess_the_number.hs
           Which number in the range from 1 to 10 am I thinking of? 4
           Sorry, it was 3
          @@ -1069,9 +1069,9 @@ 

          Randomness

          Sorry, it was 10 Which number in the range from 1 to 10 am I thinking of? -
          +

          Another way to make this same program is like this:

          -
          +
          
           import System.Random
           import Control.Monad(when)
           
          @@ -1087,55 +1087,55 @@ 

          Randomness

          else putStrLn $ "Sorry, it was " ++ show randNumber newStdGen main -
          -

          It’s very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

          +
          +

          It’s very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

          Bytestrings

          like normal string, only they byte … what a pedestrian pun this is

          Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

          -

          However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

          +

          However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

          That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

          -

          Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

          -

          The other variety of bytestrings resides in Data.ByteString.Lazy. They’re lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That’s what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it’s just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won’t cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU’s L2 cache.

          -

          If you look through the documentation for Data.ByteString.Lazy, you’ll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we’re going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

          -
          +

          Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

          +

          The other variety of bytestrings resides in Data.ByteString.Lazy. They’re lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That’s what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it’s just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won’t cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU’s L2 cache.

          +

          If you look through the documentation for Data.ByteString.Lazy, you’ll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we’re going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

          +
          
           import qualified Data.ByteString.Lazy as B
           import qualified Data.ByteString as S
          -
          -

          B has lazy bytestring types and functions, whereas S has strict ones. We’ll mostly be using the lazy version.

          -

          The function pack has the type signature pack :: [Word8] -> ByteString. What that means is that it takes a list of bytes of type Word8 and returns a ByteString. You can think of it as taking a list, which is lazy, and making it less lazy, so that it’s lazy only at 32 KiB intervals.

          -

          What’s the deal with that Word8 type? Well, it’s like Int, only that it has a much smaller range, namely 0-255. It represents an 8-bit number. And just like Int, it’s in the Num typeclass. For instance, we know that the value 5 is polymorphic in that it can act like any numeral type. Well, it can also take the type of Word8.

          -
          +
          +

          B has lazy bytestring types and functions, whereas S has strict ones. We’ll mostly be using the lazy version.

          +

          The function pack has the type signature pack :: [Word8] -> ByteString. What that means is that it takes a list of bytes of type Word8 and returns a ByteString. You can think of it as taking a list, which is lazy, and making it less lazy, so that it’s lazy only at 32 KiB intervals.

          +

          What’s the deal with that Word8 type? Well, it’s like Int, only that it has a much smaller range, namely 0-255. It represents an 8-bit number. And just like Int, it’s in the Num typeclass. For instance, we know that the value 5 is polymorphic in that it can act like any numeral type. Well, it can also take the type of Word8.

          +
          
           ghci> B.pack [99,97,110]
           Chunk "can" Empty
           ghci> B.pack [98..120]
           Chunk "bcdefghijklmnopqrstuvwx" Empty
          -
          -

          As you can see, you usually don’t have to worry about the Word8 too much, because the type system can make the numbers choose that type. If you try to use a big number, like 336 as a Word8, it will just wrap around to 80.

          -

          We packed only a handful of values into a ByteString, so they fit inside one chunk. The Empty is like the [] for lists.

          -

          unpack is the inverse function of pack. It takes a bytestring and turns it into a list of bytes.

          -

          fromChunks takes a list of strict bytestrings and converts it to a lazy bytestring. toChunks takes a lazy bytestring and converts it to a list of strict ones.

          -
          +
          +

          As you can see, you usually don’t have to worry about the Word8 too much, because the type system can make the numbers choose that type. If you try to use a big number, like 336 as a Word8, it will just wrap around to 80.

          +

          We packed only a handful of values into a ByteString, so they fit inside one chunk. The Empty is like the [] for lists.

          +

          unpack is the inverse function of pack. It takes a bytestring and turns it into a list of bytes.

          +

          fromChunks takes a list of strict bytestrings and converts it to a lazy bytestring. toChunks takes a lazy bytestring and converts it to a list of strict ones.

          +
          
           ghci> B.fromChunks [S.pack [40,41,42], S.pack [43,44,45], S.pack [46,47,48]]
           Chunk "()*" (Chunk "+,-" (Chunk "./0" Empty))
          -
          +

          This is good if you have a lot of small strict bytestrings and you want to process them efficiently without joining them into one big strict bytestring in memory first.

          -

          The bytestring version of : is called cons It takes a byte and a bytestring and puts the byte at the beginning. It’s lazy though, so it will make a new chunk even if the first chunk in the bytestring isn’t full. That’s why it’s better to use the strict version of cons, cons' if you’re going to be inserting a lot of bytes at the beginning of a bytestring.

          -
          +

          The bytestring version of : is called cons It takes a byte and a bytestring and puts the byte at the beginning. It’s lazy though, so it will make a new chunk even if the first chunk in the bytestring isn’t full. That’s why it’s better to use the strict version of cons, cons' if you’re going to be inserting a lot of bytes at the beginning of a bytestring.

          +
          
           ghci> B.cons 85 $ B.pack [80,81,82,84]
           Chunk "U" (Chunk "PQRT" Empty)
          -ghci> B.cons' 85 $ B.pack [80,81,82,84]
          +ghci> B.cons' 85 $ B.pack [80,81,82,84]
           Chunk "UPQRT" Empty
           ghci> foldr B.cons B.empty [50..60]
           Chunk "2" (Chunk "3" (Chunk "4" (Chunk "5" (Chunk "6" (Chunk "7" (Chunk "8" (Chunk "9" (Chunk ":" (Chunk ";" (Chunk "<"
           Empty))))))))))
          -ghci> foldr B.cons' B.empty [50..60]
          +ghci> foldr B.cons' B.empty [50..60]
           Chunk "23456789:;<" Empty
          -
          -

          As you can see, empty makes an empty bytestring. See the difference between cons and cons'? With the foldr, we started with an empty bytestring and then went over the list of numbers from the right, adding each number to the beginning of the bytestring. When we used cons, we ended up with one chunk for every byte, which kind of defeats the purpose.

          -

          Otherwise, the bytestring modules have a load of functions that are analogous to those in Data.List, including, but not limited to, head, tail, init, null, length, map, reverse, foldl, foldr, concat, takeWhile, filter, etc.

          -

          It also has functions that have the same name and behave the same as some functions found in System.IO, only Strings are replaced with ByteStrings. For instance, the readFile function in System.IO has a type of readFile :: FilePath -> IO String, while the readFile from the bytestring modules has a type of readFile :: FilePath -> IO ByteString. Watch out, if you’re using strict bytestrings and you attempt to read a file, it will read it into memory at once! With lazy bytestrings, it will read it into neat chunks.

          -

          Let’s make a simple program that takes two filenames as command-line arguments and copies the first file into the second file. Note that System.Directory already has a function called copyFile, but we’re going to implement our own file copying function and program anyway.

          -
          +
          +

          As you can see, empty makes an empty bytestring. See the difference between cons and cons'? With the foldr, we started with an empty bytestring and then went over the list of numbers from the right, adding each number to the beginning of the bytestring. When we used cons, we ended up with one chunk for every byte, which kind of defeats the purpose.

          +

          Otherwise, the bytestring modules have a load of functions that are analogous to those in Data.List, including, but not limited to, head, tail, init, null, length, map, reverse, foldl, foldr, concat, takeWhile, filter, etc.

          +

          It also has functions that have the same name and behave the same as some functions found in System.IO, only Strings are replaced with ByteStrings. For instance, the readFile function in System.IO has a type of readFile :: FilePath -> IO String, while the readFile from the bytestring modules has a type of readFile :: FilePath -> IO ByteString. Watch out, if you’re using strict bytestrings and you attempt to read a file, it will read it into memory at once! With lazy bytestrings, it will read it into neat chunks.

          +

          Let’s make a simple program that takes two filenames as command-line arguments and copies the first file into the second file. Note that System.Directory already has a function called copyFile, but we’re going to implement our own file copying function and program anyway.

          +
          
           import System.Environment
           import qualified Data.ByteString.Lazy as B
           
          @@ -1147,44 +1147,44 @@ 

          Bytestrings

          copyFile source dest = do contents <- B.readFile source B.writeFile dest contents -
          -

          We make our own function that takes two FilePaths (remember, FilePath is just a synonym for String) and returns an I/O action that will copy one file into another using bytestring. In the main function, we just get the arguments and call our function with them to get the I/O action, which is then performed.

          -
          +
          +

          We make our own function that takes two FilePaths (remember, FilePath is just a synonym for String) and returns an I/O action that will copy one file into another using bytestring. In the main function, we just get the arguments and call our function with them to get the I/O action, which is then performed.

          +
          
           $ runhaskell bytestringcopy.hs something.txt ../../something.txt
          -
          -

          Notice that a program that doesn’t use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that’s not hard.

          +
          +

          Notice that a program that doesn’t use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that’s not hard.

          Whenever you need better performance in a program that reads a lot of data into strings, give bytestrings a try, chances are you’ll get some good performance boosts with very little effort on your part. I usually write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

          Exceptions

          timberr!!!! -

          All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn’t be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we’ve defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

          -

          Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we’re not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell’s type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

          +

          All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn’t be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we’ve defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

          +

          Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we’re not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell’s type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

          Despite having expressive types that support failed computations, Haskell still has support for exceptions, because they make more sense in I/O contexts. A lot of things can go wrong when dealing with the outside world because it is so unreliable. For instance, when opening a file, a bunch of things can go wrong. The file might be locked, it might not be there at all or the hard disk drive or something might not be there at all. So it’s good to be able to jump to some error handling part of our code when such an error occurs.

          -

          Okay, so I/O code (i.e. impure code) can throw exceptions. It makes sense. But what about pure code? Well, it can throw exceptions too. Think about the div and head functions. They have types of (Integral a) => a -> a -> a and [a] -> a, respectively. No Maybe or Either in their return type and yet they can both fail! div explodes in your face if you try to divide by zero and head throws a tantrum when you give it an empty list.

          -
          +

          Okay, so I/O code (i.e. impure code) can throw exceptions. It makes sense. But what about pure code? Well, it can throw exceptions too. Think about the div and head functions. They have types of (Integral a) => a -> a -> a and [a] -> a, respectively. No Maybe or Either in their return type and yet they can both fail! div explodes in your face if you try to divide by zero and head throws a tantrum when you give it an empty list.

          +
          
           ghci> 4 `div` 0
           *** Exception: divide by zero
           ghci> head []
           *** Exception: Prelude.head: empty list
          -
          +
          Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail. -

          Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

          -

          Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can’t do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don’t know when they will be evaluated and that it really shouldn’t matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That’s why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that’s bad, because we want to keep the I/O part as small as possible. However, if we don’t catch them in the I/O part of our code, our program crashes. The solution? Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful type system and use types like Either and Maybe to represent results that may have failed.

          -

          That’s why we’ll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that’s part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

          -
          +

          Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

          +

          Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can’t do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don’t know when they will be evaluated and that it really shouldn’t matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That’s why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that’s bad, because we want to keep the I/O part as small as possible. However, if we don’t catch them in the I/O part of our code, our program crashes. The solution? Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful type system and use types like Either and Maybe to represent results that may have failed.

          +

          That’s why we’ll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that’s part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

          +
          
           import System.Environment
           import System.IO
           
           main = do (fileName:_) <- getArgs
                     contents <- readFile fileName
                     putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
          -
          -

          A very simple program. We perform the getArgs I/O action and bind the first string in the list that it yields to fileName. Then we call the contents of the file with that name contents. Lastly, we apply lines to those contents to get a list of lines and then we get the length of that list and give it to show to get a string representation of that number. It works as expected, but what happens when we give it the name of a file that doesn’t exist?

          -
          +
          +

          A very simple program. We perform the getArgs I/O action and bind the first string in the list that it yields to fileName. Then we call the contents of the file with that name contents. Lastly, we apply lines to those contents to get a list of lines and then we get the length of that list and give it to show to get a string representation of that number. It works as expected, but what happens when we give it the name of a file that doesn’t exist?

          +
          
           $ runhaskell linecount.hs i_dont_exist.txt
           linecount.hs: i_dont_exist.txt: openFile: does not exist (No such file or directory)
          -
          -

          Aha, we get an error from GHC, telling us that the file does not exist. Our program crashes. What if we wanted to print out a nicer message if the file doesn’t exist? One way to do that is to check if the file exists before trying to open it by using the doesFileExist function from System.Directory.

          -
          +
          +

          Aha, we get an error from GHC, telling us that the file does not exist. Our program crashes. What if we wanted to print out a nicer message if the file doesn’t exist? One way to do that is to check if the file exists before trying to open it by using the doesFileExist function from System.Directory.

          +
          
           import System.Environment
           import System.IO
           import System.Directory
          @@ -1194,16 +1194,16 @@ 

          Exceptions

          if fileExists then do contents <- readFile fileName putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!" - else do putStrLn "The file doesn't exist!" -
          -

          We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

          + else do putStrLn "The file doesn't exist!" +
          +

          We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

          Another solution here would be to use exceptions. It’s perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

          -

          To deal with this by using exceptions, we’re going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

          +

          To deal with this by using exceptions, we’re going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

          non sequitor -

          If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

          -

          The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

          -

          So let’s put our new friend catch to use!

          -
          +

          If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

          +

          The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

          +

          So let’s put our new friend catch to use!

          +
          
           import System.Environment
           import System.IO
           import System.IO.Error
          @@ -1217,18 +1217,18 @@ 

          Exceptions

          handler :: IOError -> IO () handler e = putStrLn "Whoops, had some trouble!" -
          -

          First of all, you’ll see that put backticks around it so that we can use it as an infix function, because it takes two parameters. Using it as an infix function makes it more readable. So toTry `catch` handler is the same as catch toTry handler, which fits well with its type. toTry is the I/O action that we try to carry out and handler is the function that takes an IOError and returns an action to be carried out in case of an exception.

          +
          +

          First of all, you’ll see that put backticks around it so that we can use it as an infix function, because it takes two parameters. Using it as an infix function makes it more readable. So toTry `catch` handler is the same as catch toTry handler, which fits well with its type. toTry is the I/O action that we try to carry out and handler is the function that takes an IOError and returns an action to be carried out in case of an exception.

          Let’s give this a go:

          -
          +
          
           $ runhaskell count_lines.hs i_exist.txt
           The file has 3 lines!
           
           $ runhaskell count_lines.hs i_dont_exist.txt
           Whoops, had some trouble!
          -
          -

          In the handler, we didn’t check to see what kind of IOError we got. We just say "Whoops, had some trouble!" for any kind of error. Just catching all types of exceptions in one handler is bad practice in Haskell just like it is in most other languages. What if some other exception happens that we don’t want to catch, like us interrupting the program or something? That’s why we’re going to do the same thing that’s usually done in other languages as well: we’ll check to see what kind of exception we got. If it’s the kind of exception we’re waiting to catch, we do our stuff. If it’s not, we throw that exception back into the wild. Let’s modify our program to catch only the exceptions caused by a file not existing.

          -
          +
          +

          In the handler, we didn’t check to see what kind of IOError we got. We just say "Whoops, had some trouble!" for any kind of error. Just catching all types of exceptions in one handler is bad practice in Haskell just like it is in most other languages. What if some other exception happens that we don’t want to catch, like us interrupting the program or something? That’s why we’re going to do the same thing that’s usually done in other languages as well: we’ll check to see what kind of exception we got. If it’s the kind of exception we’re waiting to catch, we do our stuff. If it’s not, we throw that exception back into the wild. Let’s modify our program to catch only the exceptions caused by a file not existing.

          +
          
           import System.Environment
           import System.IO
           import System.IO.Error
          @@ -1242,35 +1242,35 @@ 

          Exceptions

          handler :: IOError -> IO () handler e - | isDoesNotExistError e = putStrLn "The file doesn't exist!" + | isDoesNotExistError e = putStrLn "The file doesn't exist!" | otherwise = ioError e -
          -

          Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

          -

          So if the exception thrown in the toTry I/O action that we glued together with a do block isn’t caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

          -

          There are several predicates that act on IOError and if a guard doesn’t evaluate to True, evaluation falls through to the next guard. The predicates that act on IOError are:

          +
          +

          Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

          +

          So if the exception thrown in the toTry I/O action that we glued together with a do block isn’t caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

          +

          There are several predicates that act on IOError and if a guard doesn’t evaluate to True, evaluation falls through to the next guard. The predicates that act on IOError are:

            -
          • isAlreadyExistsError
          • -
          • isDoesNotExistError
          • -
          • isAlreadyInUseError
          • -
          • isFullError
          • -
          • isEOFError
          • -
          • isIllegalOperation
          • -
          • isPermissionError
          • -
          • isUserError
          • +
          • isAlreadyExistsError
          • +
          • isDoesNotExistError
          • +
          • isAlreadyInUseError
          • +
          • isFullError
          • +
          • isEOFError
          • +
          • isIllegalOperation
          • +
          • isPermissionError
          • +
          • isUserError
          -

          Most of these are pretty self-explanatory. isUserError evaluates to True when we use the function userError to make the exception, which is used for making exceptions from our code and equipping them with a string. For instance, you can do ioError $ userError "remote computer unplugged!", although it’s preferred you use types like Either and Maybe to express possible failure instead of throwing exceptions yourself with userError.

          +

          Most of these are pretty self-explanatory. isUserError evaluates to True when we use the function userError to make the exception, which is used for making exceptions from our code and equipping them with a string. For instance, you can do ioError $ userError "remote computer unplugged!", although it’s preferred you use types like Either and Maybe to express possible failure instead of throwing exceptions yourself with userError.

          So you could have a handler that looks something like this:

          -
          +
          
           handler :: IOError -> IO ()
           handler e
          -    | isDoesNotExistError e = putStrLn "The file doesn't exist!"
          +    | isDoesNotExistError e = putStrLn "The file doesn't exist!"
               | isFullError e = freeSomeSpace
               | isIllegalOperation e = notifyCops
               | otherwise = ioError e
          -
          -

          Where notifyCops and freeSomeSpace are some I/O actions that you define. Be sure to re-throw exceptions if they don’t match any of your criteria, otherwise you’re causing your program to fail silently in some cases where it shouldn’t.

          -

          System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can’t print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn’t know about anything else. A function depends only on the parameters it was called with. That’s why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it’s kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let’s modify our program to print out the file path that’s responsible for the exception occurring.

          -
          +
          +

          Where notifyCops and freeSomeSpace are some I/O actions that you define. Be sure to re-throw exceptions if they don’t match any of your criteria, otherwise you’re causing your program to fail silently in some cases where it shouldn’t.

          +

          System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can’t print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn’t know about anything else. A function depends only on the parameters it was called with. That’s why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it’s kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let’s modify our program to print out the file path that’s responsible for the exception occurring.

          +
          
           import System.Environment
           import System.IO
           import System.IO.Error
          @@ -1288,16 +1288,16 @@ 

          Exceptions

          case ioeGetFileName e of Just path -> putStrLn $ "Whoops! File does not exist at: " ++ path Nothing -> putStrLn "Whoops! File does not exist at unknown location!" | otherwise = ioError e -
          -

          In the guard where isDoesNotExistError is True, we used a case expression to call ioeGetFileName with e and then pattern match against the Maybe value that it returned. Using case expressions is commonly used when you want to pattern match against something without bringing in a new function.

          -

          You don’t have to use one handler to catch exceptions in your whole I/O part. You can just cover certain parts of your I/O code with catch or you can cover several of them with catch and use different handlers for them, like so:

          -
          +
          +

          In the guard where isDoesNotExistError is True, we used a case expression to call ioeGetFileName with e and then pattern match against the Maybe value that it returned. Using case expressions is commonly used when you want to pattern match against something without bringing in a new function.

          +

          You don’t have to use one handler to catch exceptions in your whole I/O part. You can just cover certain parts of your I/O code with catch or you can cover several of them with catch and use different handlers for them, like so:

          +
          
           main = do toTry `catch` handler1
                     thenTryThis `catch` handler2
                     launchRockets
          -
          -

          Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn’t a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

          -

          Now you know how to deal with I/O exceptions! Throwing exceptions from pure code and dealing with them hasn’t been covered here, mainly because, like we said, Haskell offers much better ways to indicate errors than reverting to I/O to catch them. Even when glueing together I/O actions that might fail, I prefer to have their type be something like IO (Either a b), meaning that they’re normal I/O actions but the result that they yield when performed is of type Either a b, meaning it’s either Left a or Right b.

          +
          +

          Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn’t a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

          +

          Now you know how to deal with I/O exceptions! Throwing exceptions from pure code and dealing with them hasn’t been covered here, mainly because, like we said, Haskell offers much better ways to indicate errors than reverting to I/O to catch them. Even when glueing together I/O actions that might fail, I prefer to have their type be something like IO (Either a b), meaning that they’re normal I/O actions but the result that they yield when performed is of type Either a b, meaning it’s either Left a or Right b.

          • diff --git a/docs/introduction.html b/docs/introduction.html index 50cab3d..a181fb8 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -54,15 +54,15 @@

            So what’s Haskell?

            fx Haskell is a purely functional programming language. -In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together. +In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together.

            lazy -Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you “Yeah yeah, I’ll do it later!”. But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end. +Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you “Yeah yeah, I’ll do it later!”. But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end.

            boat -Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers. +Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers.

            Haskell is elegant and concise. Because it uses a lot of high level concepts, Haskell programs are usually shorter than their imperative equivalents. And shorter programs are easier to maintain than longer ones and have less bugs. @@ -75,7 +75,7 @@

            What you need to dive in

            A text editor and a Haskell compiler. You probably already have your favorite text editor installed so we won’t waste time on that. For the purposes of this tutorial we’ll be using GHC, the most widely used Haskell compiler. The best way to get started is to download GHCup, which is the recommended Haskell installer.

            -GHC can take a Haskell file (they usually have a .hs extension) and compile it but it also has an interactive mode which allows you to interactively interact with files. Interactively. You can call functions from files that you load and the results are displayed immediately. For learning it’s a lot easier and faster than compiling every time you make a change and then running the program from the prompt. The interactive mode is invoked by typing in ghci at your prompt. If you have defined some functions in a file called, say, myfunctions.hs, you load up those functions by typing in :l myfunctions and then you can play with them, provided myfunctions.hs is in the same folder from which ghci was invoked. If you change the .hs file, just run :l myfunctions again or do :r, which is equivalent because it reloads the current file. The usual workflow for me when playing around in stuff is defining some functions in a .hs file, loading it up and messing around with them and then changing the .hs file, loading it up again and so on. This is also what we’ll be doing here. +GHC can take a Haskell file (they usually have a .hs extension) and compile it but it also has an interactive mode which allows you to interactively interact with files. Interactively. You can call functions from files that you load and the results are displayed immediately. For learning it’s a lot easier and faster than compiling every time you make a change and then running the program from the prompt. The interactive mode is invoked by typing in ghci at your prompt. If you have defined some functions in a file called, say, myfunctions.hs, you load up those functions by typing in :l myfunctions and then you can play with them, provided myfunctions.hs is in the same folder from which ghci was invoked. If you change the .hs file, just run :l myfunctions again or do :r, which is equivalent because it reloads the current file. The usual workflow for me when playing around in stuff is defining some functions in a .hs file, loading it up and messing around with them and then changing the .hs file, loading it up again and so on. This is also what we’ll be doing here.

              diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index ba274b1..2c596b1 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -34,104 +34,104 @@

              Making Our Own Types and Typeclasses

              In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we’ll learn how to make our own and how to put them to work!

              Algebraic data types intro

              -

              So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

              -
              +

              So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

              +
              
               data Bool = False | True
              -
              -

              data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

              -

              In a similar fashion, we can think of the Int type as being defined like this:

              -
              +
              +

              data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

              +

              In a similar fashion, we can think of the Int type as being defined like this:

              +
              
               data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
              -
              +
              caveman -

              The first and last value constructors are the minimum and maximum possible values of Int. It’s not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so this is just for illustrative purposes.

              -

              Now, let’s think about how we would represent a shape in Haskell. One way would be to use tuples. A circle could be denoted as (43.1, 55.0, 10.4) where the first and second fields are the coordinates of the circle’s center and the third field is the radius. Sounds OK, but those could also represent a 3D vector or anything else. A better solution would be to make our own type to represent a shape. Let’s say that a shape can be a circle or a rectangle. Here it is:

              -
              +

              The first and last value constructors are the minimum and maximum possible values of Int. It’s not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so this is just for illustrative purposes.

              +

              Now, let’s think about how we would represent a shape in Haskell. One way would be to use tuples. A circle could be denoted as (43.1, 55.0, 10.4) where the first and second fields are the coordinates of the circle’s center and the third field is the radius. Sounds OK, but those could also represent a 3D vector or anything else. A better solution would be to make our own type to represent a shape. Let’s say that a shape can be a circle or a rectangle. Here it is:

              +
              
               data Shape = Circle Float Float Float | Rectangle Float Float Float Float
              -
              +

              -Now what’s this? Think of it like this. The Circle value constructor has three fields, which take floats. So when we write a value constructor, we can optionally add some types after it and those types define the values it will contain. Here, the first two fields are the coordinates of its center, the third one its radius. The Rectangle value constructor has four fields which accept floats. The first two are the coordinates to its upper left corner and the second two are coordinates to its lower right one. +Now what’s this? Think of it like this. The Circle value constructor has three fields, which take floats. So when we write a value constructor, we can optionally add some types after it and those types define the values it will contain. Here, the first two fields are the coordinates of its center, the third one its radius. The Rectangle value constructor has four fields which accept floats. The first two are the coordinates to its upper left corner and the second two are coordinates to its lower right one.

              Now when I say fields, I actually mean parameters. Value constructors are actually functions that ultimately return a value of a data type. Let’s take a look at the type signatures for these two value constructors.

              -
              +
              
               ghci> :t Circle
               Circle :: Float -> Float -> Float -> Shape
               ghci> :t Rectangle
               Rectangle :: Float -> Float -> Float -> Float -> Shape
              -
              +

              Cool, so value constructors are functions like everything else. Who would have thought? Let’s make a function that takes a shape and returns its surface.

              -
              +
              
               surface :: Shape -> Float
               surface (Circle _ _ r) = pi * r ^ 2
               surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
              -
              -

              The first notable thing here is the type declaration. It says that the function takes a shape and returns a float. We couldn’t write a type declaration of Circle -> Float because Circle is not a type, Shape is. Just like we can’t write a function with a type declaration of True -> Int. The next thing we notice here is that we can pattern match against constructors. We pattern matched against constructors before (all the time actually) when we pattern matched against values like [] or False or 5, only those values didn’t have any fields. We just write a constructor and then bind its fields to names. Because we’re interested in the radius, we don’t actually care about the first two fields, which tell us where the circle is.

              -
              +
              +

              The first notable thing here is the type declaration. It says that the function takes a shape and returns a float. We couldn’t write a type declaration of Circle -> Float because Circle is not a type, Shape is. Just like we can’t write a function with a type declaration of True -> Int. The next thing we notice here is that we can pattern match against constructors. We pattern matched against constructors before (all the time actually) when we pattern matched against values like [] or False or 5, only those values didn’t have any fields. We just write a constructor and then bind its fields to names. Because we’re interested in the radius, we don’t actually care about the first two fields, which tell us where the circle is.

              +
              
               ghci> surface $ Circle 10 20 10
               314.15927
               ghci> surface $ Rectangle 0 0 100 100
               10000.0
              -
              -

              Yay, it works! But if we try to just print out Circle 10 20 5 in the prompt, we’ll get an error. That’s because Haskell doesn’t know how to display our data type as a string (yet). Remember, when we try to print a value out in the prompt, Haskell first runs the show function to get the string representation of our value and then it prints that out to the terminal. To make our Shape type part of the Show typeclass, we modify it like this:

              -
              +
              +

              Yay, it works! But if we try to just print out Circle 10 20 5 in the prompt, we’ll get an error. That’s because Haskell doesn’t know how to display our data type as a string (yet). Remember, when we try to print a value out in the prompt, Haskell first runs the show function to get the string representation of our value and then it prints that out to the terminal. To make our Shape type part of the Show typeclass, we modify it like this:

              +
              
               data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)
              -
              -

              We won’t concern ourselves with deriving too much for now. Let’s just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

              -
              +
              +

              We won’t concern ourselves with deriving too much for now. Let’s just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

              +
              
               ghci> Circle 10 20 5
               Circle 10.0 20.0 5.0
               ghci> Rectangle 50 230 60 90
               Rectangle 50.0 230.0 60.0 90.0
              -
              +

              Value constructors are functions, so we can map them and partially apply them and everything. If we want a list of concentric circles with different radii, we can do this.

              -
              +
              
               ghci> map (Circle 10 20) [4,5,6,6]
               [Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]
              -
              +

              Our data type is good, although it could be better. Let’s make an intermediate data type that defines a point in two-dimensional space. Then we can use that to make our shapes more understandable.

              -
              +
              
               data Point = Point Float Float deriving (Show)
               data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
              -
              -

              Notice that when defining a point, we used the same name for the data type and the value constructor. This has no special meaning, although it’s common to use the same name as the type if there’s only one value constructor. So now the Circle has two fields, one is of type Point and the other of type Float. This makes it easier to understand what’s what. Same goes for the rectangle. We have to adjust our surface function to reflect these changes.

              -
              +
              +

              Notice that when defining a point, we used the same name for the data type and the value constructor. This has no special meaning, although it’s common to use the same name as the type if there’s only one value constructor. So now the Circle has two fields, one is of type Point and the other of type Float. This makes it easier to understand what’s what. Same goes for the rectangle. We have to adjust our surface function to reflect these changes.

              +
              
               surface :: Shape -> Float
               surface (Circle _ r) = pi * r ^ 2
               surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
              -
              +

              The only thing we had to change were the patterns. We disregarded the whole point in the circle pattern. In the rectangle pattern, we just used a nested pattern matching to get the fields of the points. If we wanted to reference the points themselves for some reason, we could have used as-patterns.

              -
              +
              
               ghci> surface (Rectangle (Point 0 0) (Point 100 100))
               10000.0
               ghci> surface (Circle (Point 0 0) 24)
               1809.5574
              -
              +

              How about a function that nudges a shape? It takes a shape, the amount to move it on the x axis and the amount to move it on the y axis and then returns a new shape that has the same dimensions, only it’s located somewhere else.

              -
              +
              
               nudge :: Shape -> Float -> Float -> Shape
               nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
               nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))
              -
              +

              Pretty straightforward. We add the nudge amounts to the points that denote the position of the shape.

              -
              +
              
               ghci> nudge (Circle (Point 34 34) 10) 5 10
               Circle (Point 39.0 44.0) 10.0
              -
              +

              If we don’t want to deal directly with points, we can make some auxiliary functions that create shapes of some size at the zero coordinates and then nudge those.

              -
              +
              
               baseCircle :: Float -> Shape
               baseCircle r = Circle (Point 0 0) r
               
               baseRect :: Float -> Float -> Shape
               baseRect width height = Rectangle (Point 0 0) (Point width height)
              -
              -
              +
              +
              
               ghci> nudge (baseRect 40 100) 60 23
               Rectangle (Point 60.0 23.0) (Point 100.0 123.0)
              -
              -

              You can, of course, export your data types in your modules. To do that, just write your type along with the functions you are exporting and then add some parentheses and in them specify the value constructors that you want to export for it, separated by commas. If you want to export all the value constructors for a given type, just write ...

              +
              +

              You can, of course, export your data types in your modules. To do that, just write your type along with the functions you are exporting and then add some parentheses and in them specify the value constructors that you want to export for it, separated by commas. If you want to export all the value constructors for a given type, just write ...

              If we wanted to export the functions and types that we defined here in a module, we could start it off like this:

              -
              +
              
               module Shapes
               ( Point(..)
               , Shape(..)
              @@ -140,24 +140,24 @@ 

              Algebraic data types intro

              , baseCircle , baseRect ) where -
              -

              By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It’s the same as writing Shape (Rectangle, Circle).

              -

              We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can’t create a map by doing Map.Map [(1,2),(3,4)] because it doesn’t export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

              +
              +

              By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It’s the same as writing Shape (Rectangle, Circle).

              +

              We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can’t create a map by doing Map.Map [(1,2),(3,4)] because it doesn’t export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

              Not exporting the value constructors of a data types makes them more abstract in such a way that we hide their implementation. Also, whoever uses our module can’t pattern match against the value constructors.

              Record syntax

              record

              OK, we’ve been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I don’t know about you, but that’s all I ever want to know about a person. Let’s give it a go!

              -
              +
              
               data Person = Person String String Int Float String String deriving (Show)
              -
              +

              O-kay. The first field is the first name, the second is the last name, the third is the age and so on. Let’s make a person.

              -
              +
              
               ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
               ghci> guy
               Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
              -
              +

              That’s kind of cool, although slightly unreadable. What if we want to create a function to get separate info from a person? A function that gets some person’s first name, a function that gets some person’s last name, etc. Well, we’d have to define them kind of like this.

              -
              +
              
               firstName :: Person -> String
               firstName (Person firstname _ _ _ _ _) = firstname
               
              @@ -175,9 +175,9 @@ 

              Record syntax

              flavor :: Person -> String flavor (Person _ _ _ _ _ flavor) = flavor -
              +

              Whew! I certainly did not enjoy writing that! Despite being very cumbersome and BORING to write, this method works.

              -
              +
              
               ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
               ghci> firstName guy
               "Buddy"
              @@ -185,53 +185,53 @@ 

              Record syntax

              184.2 ghci> flavor guy "Chocolate" -
              +

              There must be a better way, you say! Well no, there isn’t, sorry.

              Just kidding, there is. Hahaha! The makers of Haskell were very smart and anticipated this scenario. They included an alternative way to write data types. Here’s how we could achieve the above functionality with record syntax.

              -
              +
              
               data Person = Person { firstName :: String
                                    , lastName :: String
                                    , age :: Int
                                    , height :: Float
                                    , phoneNumber :: String
                                    , flavor :: String
              -                     } deriving (Show) 
              -

              So instead of just naming the field types one after another and separating them with spaces, we use curly brackets. First we write the name of the field, for instance, firstName and then we write a double colon :: (also called Paamayim Nekudotayim, haha) and then we specify the type. The resulting data type is exactly the same. The main benefit of this is that it creates functions that lookup fields in the data type. By using record syntax to create this data type, Haskell automatically made these functions: firstName, lastName, age, height, phoneNumber and flavor.

              -
              +                     } deriving (Show) 
              +

              So instead of just naming the field types one after another and separating them with spaces, we use curly brackets. First we write the name of the field, for instance, firstName and then we write a double colon :: (also called Paamayim Nekudotayim, haha) and then we specify the type. The resulting data type is exactly the same. The main benefit of this is that it creates functions that lookup fields in the data type. By using record syntax to create this data type, Haskell automatically made these functions: firstName, lastName, age, height, phoneNumber and flavor.

              +
              
               ghci> :t flavor
               flavor :: Person -> String
               ghci> :t firstName
               firstName :: Person -> String
              -
              -

              There’s another benefit to using record syntax. When we derive Show for the type, it displays it differently if we use record syntax to define and instantiate the type. Say we have a type that represents a car. We want to keep track of the company that made it, the model name and its year of production. Watch.

              -
              +
              +

              There’s another benefit to using record syntax. When we derive Show for the type, it displays it differently if we use record syntax to define and instantiate the type. Say we have a type that represents a car. We want to keep track of the company that made it, the model name and its year of production. Watch.

              +
              
               data Car = Car String String Int deriving (Show)
              -
              -
              +
              +
              
               ghci> Car "Ford" "Mustang" 1967
               Car "Ford" "Mustang" 1967
              -
              +

              If we define it using record syntax, we can make a new car like this.

              -
              +
              
               data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)
              -
              -
              +
              +
              
               ghci> Car {company="Ford", model="Mustang", year=1967}
               Car {company = "Ford", model = "Mustang", year = 1967}
              -
              +

              When making a new car, we don’t have to necessarily put the fields in the proper order, as long as we list all of them. But if we don’t use record syntax, we have to specify them in order.

              -

              Use record syntax when a constructor has several fields and it’s not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it’s pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn’t so obvious and we greatly benefited from using record syntax.

              +

              Use record syntax when a constructor has several fields and it’s not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it’s pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn’t so obvious and we greatly benefited from using record syntax.

              Type parameters

              -

              A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

              -
              +

              A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

              +
              
               data Maybe a = Nothing | Just a
              -
              +
              yeti -

              The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that’s not a type per se, it’s a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

              -

              So if we pass Char as the type parameter to Maybe, we get a type of Maybe Char. The value Just 'a' has a type of Maybe Char, for example.

              -

              You might not know it, but we used a type that has a type parameter before we used Maybe. That type is the list type. Although there’s some syntactic sugar in play, the list type takes a parameter to produce a concrete type. Values can have an [Int] type, a [Char] type, a [[String]] type, but you can’t have a value that just has a type of [].

              -

              Let’s play around with the Maybe type.

              -
              +

              The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that’s not a type per se, it’s a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

              +

              So if we pass Char as the type parameter to Maybe, we get a type of Maybe Char. The value Just 'a' has a type of Maybe Char, for example.

              +

              You might not know it, but we used a type that has a type parameter before we used Maybe. That type is the list type. Although there’s some syntactic sugar in play, the list type takes a parameter to produce a concrete type. Values can have an [Int] type, a [Char] type, a [[String]] type, but you can’t have a value that just has a type of [].

              +

              Let’s play around with the Maybe type.

              +
              
               ghci> Just "Haha"
               Just "Haha"
               ghci> Just 84
              @@ -244,40 +244,40 @@ 

              Type parameters

              Nothing :: Maybe a ghci> Just 10 :: Maybe Double Just 10.0 -
              -

              Type parameters are useful because we can make different types with them depending on what kind of types we want contained in our data type. When we do :t Just "Haha", the type inference engine figures it out to be of the type Maybe [Char], because if the a in the Just a is a string, then the a in Maybe a must also be a string.

              -

              Notice that the type of Nothing is Maybe a. Its type is polymorphic. If some function requires a Maybe Int as a parameter, we can give it a Nothing, because a Nothing doesn’t contain a value anyway and so it doesn’t matter. The Maybe a type can act like a Maybe Int if it has to, just like 5 can act like an Int or a Double. Similarly, the type of the empty list is [a]. An empty list can act like a list of anything. That’s why we can do [1,2,3] ++ [] and ["ha","ha","ha"] ++ [].

              -

              Using type parameters is very beneficial, but only when using them makes sense. Usually we use them when our data type would work regardless of the type of the value it then holds inside it, like with our Maybe a type. If our type acts as some kind of box, it’s good to use them. We could change our Car data type from this:

              -
              +
              +

              Type parameters are useful because we can make different types with them depending on what kind of types we want contained in our data type. When we do :t Just "Haha", the type inference engine figures it out to be of the type Maybe [Char], because if the a in the Just a is a string, then the a in Maybe a must also be a string.

              +

              Notice that the type of Nothing is Maybe a. Its type is polymorphic. If some function requires a Maybe Int as a parameter, we can give it a Nothing, because a Nothing doesn’t contain a value anyway and so it doesn’t matter. The Maybe a type can act like a Maybe Int if it has to, just like 5 can act like an Int or a Double. Similarly, the type of the empty list is [a]. An empty list can act like a list of anything. That’s why we can do [1,2,3] ++ [] and ["ha","ha","ha"] ++ [].

              +

              Using type parameters is very beneficial, but only when using them makes sense. Usually we use them when our data type would work regardless of the type of the value it then holds inside it, like with our Maybe a type. If our type acts as some kind of box, it’s good to use them. We could change our Car data type from this:

              +
              
               data Car = Car { company :: String
                              , model :: String
                              , year :: Int
                              } deriving (Show)
              -
              +

              To this:

              -
              +
              
               data Car a b c = Car { company :: a
                                    , model :: b
                                    , year :: c
                                    } deriving (Show)
              -
              -

              But would we really benefit? The answer is: probably no, because we’d just end up defining functions that only work on the Car String String Int type. For instance, given our first definition of Car, we could make a function that displays the car’s properties in a nice little text.

              -
              +
              +

              But would we really benefit? The answer is: probably no, because we’d just end up defining functions that only work on the Car String String Int type. For instance, given our first definition of Car, we could make a function that displays the car’s properties in a nice little text.

              +
              
               tellCar :: Car -> String
               tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
              -
              -
              +
              +
              
               ghci> let stang = Car {company="Ford", model="Mustang", year=1967}
               ghci> tellCar stang
               "This Ford Mustang was made in 1967"
              -
              -

              A cute little function! The type declaration is cute and it works nicely. Now what if Car was Car a b c?

              -
              +
              +

              A cute little function! The type declaration is cute and it works nicely. Now what if Car was Car a b c?

              +
              
               tellCar :: (Show a) => Car String String a -> String
               tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
              -
              -

              We’d have to force this function to take a Car type of (Show a) => Car String String a. You can see that the type signature is more complicated and the only benefit we’d actually get would be that we can use any type that’s an instance of the Show typeclass as the type for c.

              -
              +
              +

              We’d have to force this function to take a Car type of (Show a) => Car String String a. You can see that the type signature is more complicated and the only benefit we’d actually get would be that we can use any type that’s an instance of the Show typeclass as the type for c.

              +
              
               ghci> tellCar (Car "Ford" "Mustang" 1967)
               "This Ford Mustang was made in 1967"
               ghci> tellCar (Car "Ford" "Mustang" "nineteen sixty seven")
              @@ -286,18 +286,18 @@ 

              Type parameters

              Car "Ford" "Mustang" 1967 :: (Num t) => Car [Char] [Char] t ghci> :t Car "Ford" "Mustang" "nineteen sixty seven" Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char] -
              +
              meekrat -

              In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We usually use type parameters when the type that’s contained inside the data type’s various value constructors isn’t really that important for the type to work. A list of stuff is a list of stuff and it doesn’t matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn’t matter what the type of that something is.

              +

              In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We usually use type parameters when the type that’s contained inside the data type’s various value constructors isn’t really that important for the type to work. A list of stuff is a list of stuff and it doesn’t matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn’t matter what the type of that something is.

              -

              Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

              -
              +

              Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

              +
              
               data (Ord k) => Map k v = ...
              -
              -

              However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

              +
              +

              However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

              So don’t put type constraints into data declarations even if it seems to make sense, because you’ll have to put them into the function type declarations either way.

              Let’s implement a 3D vector type and add some operations for it. We’ll be using a parameterized type because even though it will usually contain numeric types, it will still support several of them.

              -
              +
              
               data Vector a = Vector a a a deriving (Show)
               
               vplus :: (Num t) => Vector t -> Vector t -> Vector t
              @@ -308,10 +308,10 @@ 

              Type parameters

              scalarMult :: (Num t) => Vector t -> Vector t -> t (Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n -
              -

              vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

              -

              Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

              -
              +
              +

              vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

              +

              Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

              +
              
               ghci> Vector 3 5 8 `vplus` Vector 9 2 8
               Vector 12 7 16
               ghci> Vector 3 5 8 `vplus` Vector 9 2 8 `vplus` Vector 0 2 3
              @@ -322,28 +322,28 @@ 

              Type parameters

              74.0 ghci> Vector 2 9 3 `vectMult` (Vector 4 9 5 `scalarMult` Vector 9 2 4) Vector 148 666 222 -
              +

              Derived instances

              gob -

              In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

              -

              We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

              -

              In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

              +

              In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

              +

              We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

              +

              In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

              Consider this data type:

              -
              +
              
               data Person = Person { firstName :: String
                                    , lastName :: String
                                    , age :: Int
                                    }
              -
              -

              It describes a person. Let’s assume that no two people have the same combination of first name, last name and age. Now, if we have records for two people, does it make sense to see if they represent the same person? Sure it does. We can try to equate them and see if they’re equal or not. That’s why it would make sense for this type to be part of the Eq typeclass. We’ll derive the instance.

              -
              +
              +

              It describes a person. Let’s assume that no two people have the same combination of first name, last name and age. Now, if we have records for two people, does it make sense to see if they represent the same person? Sure it does. We can try to equate them and see if they’re equal or not. That’s why it would make sense for this type to be part of the Eq typeclass. We’ll derive the instance.

              +
              
               data Person = Person { firstName :: String
                                    , lastName :: String
                                    , age :: Int
                                    } deriving (Eq)
              -
              -

              When we derive the Eq instance for a type and then try to compare two values of that type with == or /=, Haskell will see if the value constructors match (there’s only one value constructor here though) and then it will check if all the data contained inside matches by testing each pair of fields with ==. There’s only one catch though, the types of all the fields also have to be part of the Eq typeclass. But since both String and Int are, we’re OK. Let’s test our Eq instance.

              -
              +
              +

              When we derive the Eq instance for a type and then try to compare two values of that type with == or /=, Haskell will see if the value constructors match (there’s only one value constructor here though) and then it will check if all the data contained inside matches by testing each pair of fields with ==. There’s only one catch though, the types of all the fields also have to be part of the Eq typeclass. But since both String and Int are, we’re OK. Let’s test our Eq instance.

              +
              
               ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
               ghci> let adRock = Person {firstName = "Adam", lastName = "Horovitz", age = 41}
               ghci> let mca = Person {firstName = "Adam", lastName = "Yauch", age = 44}
              @@ -355,63 +355,63 @@ 

              Derived instances

              True ghci> mikeD == Person {firstName = "Michael", lastName = "Diamond", age = 43} True -
              -

              Of course, since Person is now in Eq, we can use it as the a for all functions that have a class constraint of Eq a in their type signature, such as elem.

              -
              +
              +

              Of course, since Person is now in Eq, we can use it as the a for all functions that have a class constraint of Eq a in their type signature, such as elem.

              +
              
               ghci> let beastieBoys = [mca, adRock, mikeD]
               ghci> mikeD `elem` beastieBoys
               True
              -
              -

              The Show and Read typeclasses are for things that can be converted to or from strings, respectively. Like with Eq, if a type’s constructors have fields, their type has to be a part of Show or Read if we want to make our type an instance of them. Let’s make our Person data type a part of Show and Read as well.

              -
              +
              +

              The Show and Read typeclasses are for things that can be converted to or from strings, respectively. Like with Eq, if a type’s constructors have fields, their type has to be a part of Show or Read if we want to make our type an instance of them. Let’s make our Person data type a part of Show and Read as well.

              +
              
               data Person = Person { firstName :: String
                                    , lastName :: String
                                    , age :: Int
                                    } deriving (Eq, Show, Read)
              -
              +

              Now we can print a person out to the terminal.

              -
              +
              
               ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
               ghci> mikeD
               Person {firstName = "Michael", lastName = "Diamond", age = 43}
               ghci> "mikeD is: " ++ show mikeD
               "mikeD is: Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}"
              -
              -

              Had we tried to print a person on the terminal before making the Person data type part of Show, Haskell would have complained at us, claiming it doesn’t know how to represent a person as a string. But now that we’ve derived a Show instance for it, it does know.

              -

              Read is pretty much the inverse typeclass of Show. Show is for converting values of our a type to a string, Read is for converting strings to values of our type. Remember though, when we use the read function, we have to use an explicit type annotation to tell Haskell which type we want to get as a result. If we don’t make the type we want as a result explicit, Haskell doesn’t know which type we want.

              -
              +
              +

              Had we tried to print a person on the terminal before making the Person data type part of Show, Haskell would have complained at us, claiming it doesn’t know how to represent a person as a string. But now that we’ve derived a Show instance for it, it does know.

              +

              Read is pretty much the inverse typeclass of Show. Show is for converting values of our a type to a string, Read is for converting strings to values of our type. Remember though, when we use the read function, we have to use an explicit type annotation to tell Haskell which type we want to get as a result. If we don’t make the type we want as a result explicit, Haskell doesn’t know which type we want.

              +
              
               ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" :: Person
               Person {firstName = "Michael", lastName = "Diamond", age = 43}
              -
              -

              If we use the result of our read later on in a way that Haskell can infer that it should read it as a person, we don’t have to use type annotation.

              -
              +
              +

              If we use the result of our read later on in a way that Haskell can infer that it should read it as a person, we don’t have to use type annotation.

              +
              
               ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" == mikeD
               True
              -
              -

              We can also read parameterized types, but we have to fill in the type parameters. So we can’t do read "Just 't'" :: Maybe a, but we can do read "Just 't'" :: Maybe Char.

              -

              We can derive instances for the Ord type class, +

              +

              We can also read parameterized types, but we have to fill in the type parameters. So we can’t do read "Just 't'" :: Maybe a, but we can do read "Just 't'" :: Maybe Char.

              +

              We can derive instances for the Ord type class, which is for types that have values that can be ordered. If we compare two values of the same type that were made using different constructors, the value which was made with a constructor that’s defined first is considered smaller. -For instance, consider the Bool -type, which can have a value of either False or -True. For the purpose of seeing how it behaves when +For instance, consider the Bool +type, which can have a value of either False or +True. For the purpose of seeing how it behaves when compared, we can think of it as being implemented like this:

              -
              +
              
               data Bool = False | True deriving (Ord)
              -
              -

              Because the False value constructor is specified first and the True value constructor is specified after it, we can consider True as greater than False.

              -
              +
              +

              Because the False value constructor is specified first and the True value constructor is specified after it, we can consider True as greater than False.

              +
              
               ghci> True `compare` False
               GT
               ghci> True > False
               True
               ghci> True < False
               False
              -
              -

              In the Maybe a data type, the Nothing value constructor is specified before the Just value constructor, so a value of Nothing is always smaller than a value of Just something, even if that something is minus one billion trillion. But if we compare two Just values, then it goes to compare what’s inside them.

              -
              +
              +

              In the Maybe a data type, the Nothing value constructor is specified before the Just value constructor, so a value of Nothing is always smaller than a value of Just something, even if that something is minus one billion trillion. But if we compare two Just values, then it goes to compare what’s inside them.

              +
              
               ghci> Nothing < Just 100
               True
               ghci> Nothing > Just (-49999)
              @@ -420,28 +420,28 @@ 

              Derived instances

              GT ghci> Just 100 > Just 50 True -
              -

              But we can’t do something like Just (*3) > Just (*2), because (*3) and (*2) are functions, which aren’t instances of Ord.

              -

              We can easily use algebraic data types to make enumerations and the Enum and Bounded typeclasses help us with that. Consider the following data type:

              -
              +
              +

              But we can’t do something like Just (*3) > Just (*2), because (*3) and (*2) are functions, which aren’t instances of Ord.

              +

              We can easily use algebraic data types to make enumerations and the Enum and Bounded typeclasses help us with that. Consider the following data type:

              +
              
               data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
              -
              -

              Because all the value constructors are nullary (take no parameters, i.e. fields), we can make it part of the Enum typeclass. The Enum typeclass is for things that have predecessors and successors. We can also make it part of the Bounded typeclass, which is for things that have a lowest possible value and highest possible value. And while we’re at it, let’s also make it an instance of all the other derivable typeclasses and see what we can do with it.

              -
              +
              +

              Because all the value constructors are nullary (take no parameters, i.e. fields), we can make it part of the Enum typeclass. The Enum typeclass is for things that have predecessors and successors. We can also make it part of the Bounded typeclass, which is for things that have a lowest possible value and highest possible value. And while we’re at it, let’s also make it an instance of all the other derivable typeclasses and see what we can do with it.

              +
              
               data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
                          deriving (Eq, Ord, Show, Read, Bounded, Enum)
              -
              -

              Because it’s part of the Show and Read typeclasses, we can convert values of this type to and from strings.

              -
              +
              +

              Because it’s part of the Show and Read typeclasses, we can convert values of this type to and from strings.

              +
              
               ghci> Wednesday
               Wednesday
               ghci> show Wednesday
               "Wednesday"
               ghci> read "Saturday" :: Day
               Saturday
              -
              -

              Because it’s part of the Eq and Ord typeclasses, we can compare or equate days.

              -
              +
              +

              Because it’s part of the Eq and Ord typeclasses, we can compare or equate days.

              +
              
               ghci> Saturday == Sunday
               False
               ghci> Saturday == Saturday
              @@ -450,16 +450,16 @@ 

              Derived instances

              True ghci> Monday `compare` Wednesday LT -
              -

              It’s also part of Bounded, so we can get the lowest and highest day.

              -
              +
              +

              It’s also part of Bounded, so we can get the lowest and highest day.

              +
              
               ghci> minBound :: Day
               Monday
               ghci> maxBound :: Day
               Sunday
              -
              -

              It’s also an instance of Enum. We can get predecessors and successors of days and we can make list ranges from them!

              -
              +
              +

              It’s also an instance of Enum. We can get predecessors and successors of days and we can make list ranges from them!

              +
              
               ghci> succ Monday
               Tuesday
               ghci> pred Saturday
              @@ -468,24 +468,24 @@ 

              Derived instances

              [Thursday,Friday,Saturday,Sunday] ghci> [minBound .. maxBound] :: [Day] [Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday] -
              +

              That’s pretty awesome.

              Type synonyms

              - Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char]. + Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char].

              -
              + 
              
                type String = [Char]
              - 
              +
              chicken

              We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a synonym for an already existing type.

              -

              If we make a function that converts a string to uppercase and call it toUpperString or something, we can give it a type declaration of toUpperString :: [Char] -> [Char] or toUpperString :: String -> String. Both of these are essentially the same, only the latter is nicer to read.

              +

              If we make a function that converts a string to uppercase and call it toUpperString or something, we can give it a type declaration of toUpperString :: [Char] -> [Char] or toUpperString :: String -> String. Both of these are essentially the same, only the latter is nicer to read.

              - When we were dealing with the Data.Map module, we first represented a phonebook with an association list before converting it into a map. As we’ve already found out, an association list is a list of key-value pairs. Let’s look at a phonebook that we had. + When we were dealing with the Data.Map module, we first represented a phonebook with an association list before converting it into a map. As we’ve already found out, an association list is a list of key-value pairs. Let’s look at a phonebook that we had.

              -
              +
              
               phoneBook :: [(String,String)]
               phoneBook =
                   [("amelia","555-2938")
              @@ -494,47 +494,47 @@ 

              Type synonyms

              ,("neil","205-2928") ,("roald","939-8282") ,("tenzing","853-2492") - ]
              -

              We see that the type of phoneBook is [(String,String)]. That tells us that it’s an association list that maps from strings to strings, but not much else. Let’s make a type synonym to convey some more information in the type declaration.

              -
              +    ]
              +

              We see that the type of phoneBook is [(String,String)]. That tells us that it’s an association list that maps from strings to strings, but not much else. Let’s make a type synonym to convey some more information in the type declaration.

              +
              
               type PhoneBook = [(String,String)]
              -
              -

              Now the type declaration for our phonebook can be phoneBook :: PhoneBook. Let’s make a type synonym for String as well.

              -
              +
              +

              Now the type declaration for our phonebook can be phoneBook :: PhoneBook. Let’s make a type synonym for String as well.

              +
              
               type PhoneNumber = String
               type Name = String
               type PhoneBook = [(Name,PhoneNumber)]
              -
              -

              Giving the String type synonyms is something that Haskell programmers do when they want to convey more information about what strings in their functions should be used as and what they represent.

              +
              +

              Giving the String type synonyms is something that Haskell programmers do when they want to convey more information about what strings in their functions should be used as and what they represent.

              So now, when we implement a function that takes a name and a number and sees if that name and number combination is in our phonebook, we can give it a very pretty and descriptive type declaration.

              -
              +
              
               inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool
               inPhoneBook name pnumber pbook = (name,pnumber) `elem` pbook
              -
              -

              If we decided not to use type synonyms, our function would have a type of String -> String -> [(String,String)] -> Bool. In this case, the type declaration that took advantage of type synonyms is easier to understand. However, you shouldn’t go overboard with them. We introduce type synonyms either to describe what some existing type represents in our functions (and thus our type declarations become better documentation) or when something has a long-ish type that’s repeated a lot (like [(String,String)]) but represents something more specific in the context of our functions.

              +
              +

              If we decided not to use type synonyms, our function would have a type of String -> String -> [(String,String)] -> Bool. In this case, the type declaration that took advantage of type synonyms is easier to understand. However, you shouldn’t go overboard with them. We introduce type synonyms either to describe what some existing type represents in our functions (and thus our type declarations become better documentation) or when something has a long-ish type that’s repeated a lot (like [(String,String)]) but represents something more specific in the context of our functions.

              Type synonyms can also be parameterized. If we want a type that represents an association list type but still want it to be general so it can use any type as the keys and values, we can do this:

              -
              +
              
               type AssocList k v = [(k,v)]
              -
              -

              Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

              -

              Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

              -

              Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

              -
              +
              +

              Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

              +

              Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

              +

              Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

              +
              
               type IntMap v = Map Int v
              -
              +

              Or we could do it like this:

              -
              +
              
               type IntMap = Map Int
              -
              -

              Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

              -

              Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.

              -

              Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

              -

              Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

              -
              +
              +

              Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

              +

              Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.

              +

              Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

              +

              Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

              +
              
               data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
              -
              -

              It has two value constructors. If the Left is used, then its contents are of type a and if Right is used, then its contents are of type b. So we can use this type to encapsulate a value of one type or another and then when we get a value of type Either a b, we usually pattern match on both Left and Right and we different stuff based on which one of them it was.

              -
              +
              +

              It has two value constructors. If the Left is used, then its contents are of type a and if Right is used, then its contents are of type b. So we can use this type to encapsulate a value of one type or another and then when we get a value of type Either a b, we usually pattern match on both Left and Right and we different stuff based on which one of them it was.

              +
              
               ghci> Right 20
               Right 20
               ghci> Left "w00t"
              @@ -543,10 +543,10 @@ 

              Type synonyms

              Right 'a' :: Either a Char ghci> :t Left True Left True :: Either Bool b -
              -

              So far, we’ve seen that Maybe a was mostly used to represent the results of computations that could have either failed or not. But sometimes, Maybe a isn’t good enough because Nothing doesn’t really convey much information other than that something has failed. That’s cool for functions that can fail in only one way or if we’re just not interested in how and why they failed. A Data.Map lookup fails only if the key we were looking for wasn’t in the map, so we know exactly what happened. However, when we’re interested in how some function failed or why, we usually use the result type of Either a b, where a is some sort of type that can tell us something about the possible failure and b is the type of a successful computation. Hence, errors use the Left value constructor while results use Right.

              -

              An example: a high-school has lockers so that students have some place to put their Guns’n’Roses posters. Each locker has a code combination. When a student wants a new locker, they tell the locker supervisor which locker number they want and he gives them the code. However, if someone is already using that locker, he can’t tell them the code for the locker and they have to pick a different one. We’ll use a map from Data.Map to represent the lockers. It’ll map from locker numbers to a pair of whether the locker is in use or not and the locker code.

              -
              +
              +

              So far, we’ve seen that Maybe a was mostly used to represent the results of computations that could have either failed or not. But sometimes, Maybe a isn’t good enough because Nothing doesn’t really convey much information other than that something has failed. That’s cool for functions that can fail in only one way or if we’re just not interested in how and why they failed. A Data.Map lookup fails only if the key we were looking for wasn’t in the map, so we know exactly what happened. However, when we’re interested in how some function failed or why, we usually use the result type of Either a b, where a is some sort of type that can tell us something about the possible failure and b is the type of a successful computation. Hence, errors use the Left value constructor while results use Right.

              +

              An example: a high-school has lockers so that students have some place to put their Guns’n’Roses posters. Each locker has a code combination. When a student wants a new locker, they tell the locker supervisor which locker number they want and he gives them the code. However, if someone is already using that locker, he can’t tell them the code for the locker and they have to pick a different one. We’ll use a map from Data.Map to represent the lockers. It’ll map from locker numbers to a pair of whether the locker is in use or not and the locker code.

              +
              
               import qualified Data.Map as Map
               
               data LockerState = Taken | Free deriving (Show, Eq)
              @@ -554,18 +554,18 @@ 

              Type synonyms

              type Code = String type LockerMap = Map.Map Int (LockerState, Code) -
              -

              Simple stuff. We introduce a new data type to represent whether a locker is taken or free and we make a type synonym for the locker code. We also make a type synonym for the type that maps from integers to pairs of locker state and code. And now, we’re going to make a function that searches for the code in a locker map. We’re going to use an Either String Code type to represent our result, because our lookup can fail in two ways — the locker can be taken, in which case we can’t tell the code or the locker number might not exist at all. If the lookup fails, we’re just going to use a String to tell what’s happened.

              -
              +
              +

              Simple stuff. We introduce a new data type to represent whether a locker is taken or free and we make a type synonym for the locker code. We also make a type synonym for the type that maps from integers to pairs of locker state and code. And now, we’re going to make a function that searches for the code in a locker map. We’re going to use an Either String Code type to represent our result, because our lookup can fail in two ways — the locker can be taken, in which case we can’t tell the code or the locker number might not exist at all. If the lookup fails, we’re just going to use a String to tell what’s happened.

              +
              
               lockerLookup :: Int -> LockerMap -> Either String Code
               lockerLookup lockerNumber map =
                   case Map.lookup lockerNumber map of
                       Nothing -> Left $ "Locker number " ++ show lockerNumber ++ " doesn't exist!"
                       Just (state, code) -> if state /= Taken
                                               then Right code
              -                                else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"
              -

              We do a normal lookup in the map. If we get a Nothing, we return a value of type Left String, saying that the locker doesn’t exist at all. If we do find it, then we do an additional check to see if the locker is taken. If it is, return a Left saying that it’s already taken. If it isn’t, then return a value of type Right Code, in which we give the student the correct code for the locker. It’s actually a Right String, but we introduced that type synonym to introduce some additional documentation into the type declaration. Here’s an example map:

              -
              +                                else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"
              +

              We do a normal lookup in the map. If we get a Nothing, we return a value of type Left String, saying that the locker doesn’t exist at all. If we do find it, then we do an additional check to see if the locker is taken. If it is, return a Left saying that it’s already taken. If it isn’t, then return a value of type Right Code, in which we give the student the correct code for the locker. It’s actually a Right String, but we introduced that type synonym to introduce some additional documentation into the type declaration. Here’s an example map:

              +
              
               lockers :: LockerMap
               lockers = Map.fromList
                   [(100,(Taken,"ZD39I"))
              @@ -575,9 +575,9 @@ 

              Type synonyms

              ,(109,(Taken,"893JJ")) ,(110,(Taken,"99292")) ] -
              +

              Now let’s try looking up some locker codes.

              -
              +
              
               ghci> lockerLookup 101 lockers
               Right "JAH3I"
               ghci> lockerLookup 100 lockers
              @@ -588,23 +588,23 @@ 

              Type synonyms

              Left "Locker 110 is already taken!" ghci> lockerLookup 105 lockers Right "QOTSA" -
              -

              We could have used a Maybe a to represent the result but then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

              +
              +

              We could have used a Maybe a to represent the result but then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

              Recursive data structures

              the fonz

              As we’ve seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have fields that are of the same type! Using that, we can create recursive data types, where one value of some type contains values of that type, which in turn contain more values of the same type and so on.

              -

              Think about this list: [5]. That’s just syntactic sugar for 5:[]. On the left side of the :, there’s a value and on the right side, there’s a list. And in this case, it’s an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

              -

              We could say that a list can be an empty list or it can be an element joined together with a : with another list (that can be either the empty list or not).

              +

              Think about this list: [5]. That’s just syntactic sugar for 5:[]. On the left side of the :, there’s a value and on the right side, there’s a list. And in this case, it’s an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

              +

              We could say that a list can be an empty list or it can be an element joined together with a : with another list (that can be either the empty list or not).

              Let’s use algebraic data types to implement our own list then!

              -
              +
              
               data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
              -
              +

              This reads just like our definition of lists from one of the previous paragraphs. It’s either an empty list or a combination of a head with some value and a list. If you’re confused about this, you might find it easier to understand in record syntax.

              -
              +
              
               data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)
              -
              -

              You might also be confused about the Cons constructor here. cons is another word for :. You see, in lists, : is actually a constructor that takes a value and another list and returns a list. We can already use our new list type! In other words, it has two fields. One field is of the type of a and the other is of the type [a].

              -
              +
              +

              You might also be confused about the Cons constructor here. cons is another word for :. You see, in lists, : is actually a constructor that takes a value and another list and returns a list. We can already use our new list type! In other words, it has two fields. One field is of the type of a and the other is of the type [a].

              +
              
               ghci> Empty
               Empty
               ghci> 5 `Cons` Empty
              @@ -613,57 +613,57 @@ 

              Recursive data structures

              Cons 4 (Cons 5 Empty) ghci> 3 `Cons` (4 `Cons` (5 `Cons` Empty)) Cons 3 (Cons 4 (Cons 5 Empty)) -
              -

              We called our Cons constructor in an infix manner so you can see how it’s just like :. Empty is like [] and 4 `Cons` (5 `Cons` Empty) is like 4:(5:[]).

              +
              +

              We called our Cons constructor in an infix manner so you can see how it’s just like :. Empty is like [] and 4 `Cons` (5 `Cons` Empty) is like 4:(5:[]).

              We can define functions to be automatically infix by making them comprised of only special characters. We can also do the same with constructors, since they’re just functions that return a data type. So check this out.

              -
              +
              
               infixr 5 :-:
               data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)
              -
              -

              First off, we notice a new syntactic construct, the fixity declarations. When we define functions as operators, we can use that to give them a fixity (but we don’t have to). A fixity states how tightly the operator binds and whether it’s left-associative or right-associative. For instance, *’s fixity is infixl 7 * and +’s fixity is infixl 6. That means that they’re both left-associative (4 * 3 * 2 is (4 * 3) * 2) but * binds tighter than +, because it has a greater fixity, so 5 * 4 + 3 is (5 * 4) + 3.

              -

              Otherwise, we just wrote a :-: (List a) instead of Cons a (List a). Now, we can write out lists in our list type like so:

              -
              +
              +

              First off, we notice a new syntactic construct, the fixity declarations. When we define functions as operators, we can use that to give them a fixity (but we don’t have to). A fixity states how tightly the operator binds and whether it’s left-associative or right-associative. For instance, *’s fixity is infixl 7 * and +’s fixity is infixl 6. That means that they’re both left-associative (4 * 3 * 2 is (4 * 3) * 2) but * binds tighter than +, because it has a greater fixity, so 5 * 4 + 3 is (5 * 4) + 3.

              +

              Otherwise, we just wrote a :-: (List a) instead of Cons a (List a). Now, we can write out lists in our list type like so:

              +
              
               ghci> 3 :-: 4 :-: 5 :-: Empty
               (:-:) 3 ((:-:) 4 ((:-:) 5 Empty))
               ghci> let a = 3 :-: 4 :-: 5 :-: Empty
               ghci> 100 :-: a
               (:-:) 100 ((:-:) 3 ((:-:) 4 ((:-:) 5 Empty)))
              -
              -

              When deriving Show for our type, Haskell will still display it as if the constructor was a prefix function, hence the parentheses around the operator (remember, 4 + 3 is (+) 4 3).

              -

              Let’s make a function that adds two of our lists together. This is how ++ is defined for normal lists:

              -
              +
              +

              When deriving Show for our type, Haskell will still display it as if the constructor was a prefix function, hence the parentheses around the operator (remember, 4 + 3 is (+) 4 3).

              +

              Let’s make a function that adds two of our lists together. This is how ++ is defined for normal lists:

              +
              
               infixr 5  ++
               (++) :: [a] -> [a] -> [a]
               []     ++ ys = ys
               (x:xs) ++ ys = x : (xs ++ ys)
              -
              -

              So we’ll just steal that for our own list. We’ll name the function .++.

              -
              +
              +

              So we’ll just steal that for our own list. We’ll name the function .++.

              +
              
               infixr 5  .++
               (.++) :: List a -> List a -> List a
               Empty .++ ys = ys
               (x :-: xs) .++ ys = x :-: (xs .++ ys)
              -
              +

              And let’s see if it works …

              -
              +
              
               ghci> let a = 3 :-: 4 :-: 5 :-: Empty
               ghci> let b = 6 :-: 7 :-: Empty
               ghci> a .++ b
               (:-:) 3 ((:-:) 4 ((:-:) 5 ((:-:) 6 ((:-:) 7 Empty))))
              -
              +

              Nice. Is nice. If we wanted, we could implement all of the functions that operate on lists on our own list type.

              -

              Notice how we pattern matched on (x :-: xs). That works because pattern matching is actually about matching constructors. We can match on :-: because it is a constructor for our own list type and we can also match on : because it is a constructor for the built-in list type. Same goes for []. Because pattern matching works (only) on constructors, we can match for stuff like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

              +

              Notice how we pattern matched on (x :-: xs). That works because pattern matching is actually about matching constructors. We can match on :-: because it is a constructor for our own list type and we can also match on : because it is a constructor for the built-in list type. Same goes for []. Because pattern matching works (only) on constructors, we can match for stuff like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

              binary search tree

              Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we’d start at 5 and then because 8 is greater than 5, we’d go right. We’re now at 7 and because 8 is greater than 7, we go right again. And we’ve found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

              -

              Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we’ll just be implementing normal binary search trees.

              +

              Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we’ll just be implementing normal binary search trees.

              Here’s what we’re going to say: a tree is either an empty tree or it’s an element that contains some value and two trees. Sounds like a perfect fit for an algebraic data type!

              -
              +
              
               data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
              -
              +

              Okay, good, this is good. Instead of manually building a tree, we’re going to make a function that takes a tree and an element and inserts an element. We do this by comparing the value we want to insert to the root node and then if it’s smaller, we go left, if it’s larger, we go right. We do the same for every subsequent node until we reach an empty tree. Once we’ve reached an empty tree, we just insert a node with that value instead of the empty tree.

              -

              In languages like C, we’d do this by modifying the pointers and values inside the tree. In Haskell, we can’t really modify our tree, so we have to make a new subtree each time we decide to go left or right and in the end the insertion function returns a completely new tree, because Haskell doesn’t really have a concept of pointer, just values. Hence, the type for our insertion function is going to be something like a -> Tree a -> Tree a. It takes an element and a tree and returns a new tree that has that element inside. This might seem like it’s inefficient but laziness takes care of that problem.

              +

              In languages like C, we’d do this by modifying the pointers and values inside the tree. In Haskell, we can’t really modify our tree, so we have to make a new subtree each time we decide to go left or right and in the end the insertion function returns a completely new tree, because Haskell doesn’t really have a concept of pointer, just values. Hence, the type for our insertion function is going to be something like a -> Tree a -> Tree a. It takes an element and a tree and returns a new tree that has that element inside. This might seem like it’s inefficient but laziness takes care of that problem.

              So, here are two functions. One is a utility function for making a singleton tree (a tree with just one node) and a function to insert an element into a tree.

              -
              +
              
               singleton :: a -> Tree a
               singleton x = Node x EmptyTree EmptyTree
               
              @@ -673,26 +673,26 @@ 

              Recursive data structures

              | x == a = Node x left right | x < a = Node a (treeInsert x left) right | x > a = Node a left (treeInsert x right) -
              -

              The singleton function is just a shortcut for making a node that has something and then two empty subtrees. In the insertion function, we first have the edge condition as a pattern. If we’ve reached an empty subtree, that means we’re where we want and instead of the empty tree, we put a singleton tree with our element. If we’re not inserting into an empty tree, then we have to check some things. First off, if the element we’re inserting is equal to the root element, just return a tree that’s the same. If it’s smaller, return a tree that has the same root value, the same right subtree but instead of its left subtree, put a tree that has our value inserted into it. Same (but the other way around) goes if our value is bigger than the root element.

              +
              +

              The singleton function is just a shortcut for making a node that has something and then two empty subtrees. In the insertion function, we first have the edge condition as a pattern. If we’ve reached an empty subtree, that means we’re where we want and instead of the empty tree, we put a singleton tree with our element. If we’re not inserting into an empty tree, then we have to check some things. First off, if the element we’re inserting is equal to the root element, just return a tree that’s the same. If it’s smaller, return a tree that has the same root value, the same right subtree but instead of its left subtree, put a tree that has our value inserted into it. Same (but the other way around) goes if our value is bigger than the root element.

              Next up, we’re going to make a function that checks if some element is in the tree. First, let’s define the edge condition. If we’re looking for an element in an empty tree, then it’s certainly not there. Okay. Notice how this is the same as the edge condition when searching for elements in lists. If we’re looking for an element in an empty list, it’s not there. Anyway, if we’re not looking for an element in an empty tree, then we check some things. If the element in the root node is what we’re looking for, great! If it’s not, what then? Well, we can take advantage of knowing that all the left elements are smaller than the root node. So if the element we’re looking for is smaller than the root node, check to see if it’s in the left subtree. If it’s bigger, check to see if it’s in the right subtree.

              -
              +
              
               treeElem :: (Ord a) => a -> Tree a -> Bool
               treeElem x EmptyTree = False
               treeElem x (Node a left right)
                   | x == a = True
                   | x < a  = treeElem x left
                   | x > a  = treeElem x right
              -
              +

              All we had to do was write up the previous paragraph in code. Let’s have some fun with our trees! Instead of manually building one (although we could), we’ll use a fold to build up a tree from a list. Remember, pretty much everything that traverses a list one by one and then returns some sort of value can be implemented with a fold! We’re going to start with the empty tree and then approach a list from the right and just insert element after element into our accumulator tree.

              -
              +
              
               ghci> let nums = [8,6,4,1,7,3,5]
               ghci> let numsTree = foldr treeInsert EmptyTree nums
               ghci> numsTree
              -Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))
              -

              In that foldr, treeInsert was the folding function (it takes a tree and a list element and produces a new tree) and EmptyTree was the starting accumulator. nums, of course, was the list we were folding over.

              +Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))
              +

              In that foldr, treeInsert was the folding function (it takes a tree and a list element and produces a new tree) and EmptyTree was the starting accumulator. nums, of course, was the list we were folding over.

              When we print our tree to the console, it’s not very readable, but if we try, we can make out its structure. We see that the root node is 5 and then it has two subtrees, one of which has the root node of 3 and the other a 7, etc.

              -
              +
              
               ghci> 8 `treeElem` numsTree
               True
               ghci> 100 `treeElem` numsTree
              @@ -701,7 +701,7 @@ 

              Recursive data structures

              True ghci> 10 `treeElem` numsTree False -
              +

              Checking for membership also works nicely. Cool.

              So as you can see, algebraic data structures are a really cool and powerful concept in Haskell. We can use them to make anything from boolean values and weekday enumerations to binary search trees and more!

              Typeclasses 102

              @@ -709,49 +709,49 @@

              Typeclasses 102

              So far, we’ve learned about some of the standard Haskell typeclasses and we’ve seen which types are in them. We’ve also learned how to automatically make our own types instances of the standard typeclasses by asking Haskell to derive the instances for us. In this section, we’re going to learn how to make our own typeclasses and how to make types instances of them by hand.

              A quick recap on typeclasses: typeclasses are like interfaces. A typeclass defines some behavior (like comparing for equality, comparing for ordering, enumeration) and then types that can behave in that way are made instances of that typeclass. The behavior of typeclasses is achieved by defining functions or just type declarations that we then implement. So when we say that a type is an instance of a typeclass, we mean that we can use the functions that the typeclass defines with that type.

              Typeclasses have pretty much nothing to do with classes in languages like Java or Python. This confuses many people, so I want you to forget everything you know about classes in imperative languages right now.

              -

              For example, the Eq typeclass is for stuff that can be equated. It defines the functions == and /=. If we have a type (say, Car) and comparing two cars with the equality function == makes sense, then it makes sense for Car to be an instance of Eq.

              -

              This is how the Eq class is defined in the standard prelude:

              -
              +

              For example, the Eq typeclass is for stuff that can be equated. It defines the functions == and /=. If we have a type (say, Car) and comparing two cars with the equality function == makes sense, then it makes sense for Car to be an instance of Eq.

              +

              This is how the Eq class is defined in the standard prelude:

              +
              
               class Eq a where
                   (==) :: a -> a -> Bool
                   (/=) :: a -> a -> Bool
                   x == y = not (x /= y)
                   x /= y = not (x == y)
              -
              -

              Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

              -

              Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.

              -

              Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

              -

              If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.

              +
              +

              Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

              +

              Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.

              +

              Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

              +

              If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.

              So once we have a class, what can we do with it? Well, not much, really. But once we start making types instances of that class, we start getting some nice functionality. So check out this type:

              -
              +
              
               data TrafficLight = Red | Yellow | Green
              -
              -

              It defines the states of a traffic light. Notice how we didn’t derive any class instances for it. That’s because we’re going to write up some instances by hand, even though we could derive them for types like Eq and Show. Here’s how we make it an instance of Eq.

              -
              +
              +

              It defines the states of a traffic light. Notice how we didn’t derive any class instances for it. That’s because we’re going to write up some instances by hand, even though we could derive them for types like Eq and Show. Here’s how we make it an instance of Eq.

              +
              
               instance Eq TrafficLight where
                   Red == Red = True
                   Green == Green = True
                   Yellow == Yellow = True
                   _ == _ = False
              -
              -

              We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we’re making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

              -

              Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That’s called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

              -
              +
              +

              We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we’re making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

              +

              Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That’s called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

              +
              
               class Eq a where
                   (==) :: a -> a -> Bool
                   (/=) :: a -> a -> Bool
              -
              -

              we’d have to implement both of these functions when making a type an instance of it, because Haskell wouldn’t know how these two functions are related. The minimal complete definition would then be: both == and /=.

              -

              You can see that we implemented == simply by doing pattern matching. Since there are many more cases where two lights aren’t equal, we specified the ones that are equal and then just did a catch-all pattern saying that if it’s none of the previous combinations, then two lights aren’t equal.

              -

              Let’s make this an instance of Show by hand, too. To satisfy the minimal complete definition for Show, we just have to implement its show function, which takes a value and turns it into a string.

              -
              +
              +

              we’d have to implement both of these functions when making a type an instance of it, because Haskell wouldn’t know how these two functions are related. The minimal complete definition would then be: both == and /=.

              +

              You can see that we implemented == simply by doing pattern matching. Since there are many more cases where two lights aren’t equal, we specified the ones that are equal and then just did a catch-all pattern saying that if it’s none of the previous combinations, then two lights aren’t equal.

              +

              Let’s make this an instance of Show by hand, too. To satisfy the minimal complete definition for Show, we just have to implement its show function, which takes a value and turns it into a string.

              +
              
               instance Show TrafficLight where
                   show Red = "Red light"
                   show Yellow = "Yellow light"
                   show Green = "Green light"
              -
              +

              Once again, we used pattern matching to achieve our goals. Let’s see how it works in action:

              -
              +
              
               ghci> Red == Red
               True
               ghci> Red == Yellow
              @@ -760,92 +760,92 @@ 

              Typeclasses 102

              True ghci> [Red, Yellow, Green] [Red light,Yellow light,Green light] -
              -

              Nice. We could have just derived Eq and it would have had the same effect (but we didn’t for educational purposes). However, deriving Show would have just directly translated the value constructors to strings. But if we want lights to appear like "Red light", then we have to make the instance declaration by hand.

              -

              You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here’s the first part:

              -
              +
              +

              Nice. We could have just derived Eq and it would have had the same effect (but we didn’t for educational purposes). However, deriving Show would have just directly translated the value constructors to strings. But if we want lights to appear like "Red light", then we have to make the instance declaration by hand.

              +

              You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here’s the first part:

              +
              
               class (Eq a) => Num a where
              -   ...  
              -

              As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We’re essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That’s all there is to subclassing really, it’s just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

              -

              But how are the Maybe or list types made as instances of typeclasses? What makes Maybe different from, say, TrafficLight is that Maybe in itself isn’t a concrete type, it’s a type constructor that takes one type parameter (like Char or something) to produce a concrete type (like Maybe Char). Let’s take a look at the Eq typeclass again:

              -
              +   ...  
              +

              As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We’re essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That’s all there is to subclassing really, it’s just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

              +

              But how are the Maybe or list types made as instances of typeclasses? What makes Maybe different from, say, TrafficLight is that Maybe in itself isn’t a concrete type, it’s a type constructor that takes one type parameter (like Char or something) to produce a concrete type (like Maybe Char). Let’s take a look at the Eq typeclass again:

              +
              
               class Eq a where
                   (==) :: a -> a -> Bool
                   (/=) :: a -> a -> Bool
                   x == y = not (x /= y)
              -    x /= y = not (x == y) 
              -

              From the type declarations, we see that the a is used as a concrete type because all the types in functions have to be concrete (remember, you can’t have a function of the type a -> Maybe but you can have a function of a -> Maybe a or Maybe Int -> Maybe String). That’s why we can’t do something like

              -
              +    x /= y = not (x == y) 
              +

              From the type declarations, we see that the a is used as a concrete type because all the types in functions have to be concrete (remember, you can’t have a function of the type a -> Maybe but you can have a function of a -> Maybe a or Maybe Int -> Maybe String). That’s why we can’t do something like

              +
              
               instance Eq Maybe where
              -    ...  
              -

              Because like we’ve seen, the a has to be a concrete type but Maybe isn’t a concrete type. It’s a type constructor that takes one parameter and then produces a concrete type. It would also be tedious to write instance Eq (Maybe Int) where, instance Eq (Maybe Char) where, etc. for every type ever. So we could write it out like so:

              -
              +    ...  
              +

              Because like we’ve seen, the a has to be a concrete type but Maybe isn’t a concrete type. It’s a type constructor that takes one parameter and then produces a concrete type. It would also be tedious to write instance Eq (Maybe Int) where, instance Eq (Maybe Char) where, etc. for every type ever. So we could write it out like so:

              +
              
               instance Eq (Maybe m) where
                   Just x == Just y = x == y
                   Nothing == Nothing = True
                   _ == _ = False
              -      
              -

              This is like saying that we want to make all types of the form Maybe something an instance of Eq. We actually could have written (Maybe something), but we usually opt for single letters to be true to the Haskell style. The (Maybe m) here plays the role of the a from class Eq a where. While Maybe isn’t a concrete type, Maybe m is. By specifying a type parameter (m, which is in lowercase), we said that we want all types that are in the form of Maybe m, where m is any type, to be an instance of Eq.

              -

              There’s one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That’s why we have to modify our instance declaration like this:

              -
              +      
              +

              This is like saying that we want to make all types of the form Maybe something an instance of Eq. We actually could have written (Maybe something), but we usually opt for single letters to be true to the Haskell style. The (Maybe m) here plays the role of the a from class Eq a where. While Maybe isn’t a concrete type, Maybe m is. By specifying a type parameter (m, which is in lowercase), we said that we want all types that are in the form of Maybe m, where m is any type, to be an instance of Eq.

              +

              There’s one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That’s why we have to modify our instance declaration like this:

              +
              
               instance (Eq m) => Eq (Maybe m) where
                   Just x == Just y = x == y
                   Nothing == Nothing = True
                   _ == _ = False
              -      
              -

              We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

              -

              Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

              -

              When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

              -

              Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

              -

              Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

              +
              +

              We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

              +

              Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

              +

              When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

              +

              Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

              +

              Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

              A yes-no typeclass

              yesno -

              In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

              -

              Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

              -
              +

              In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

              +

              Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

              +
              
               class YesNo a where
                   yesno :: a -> Bool
              -
              -

              Pretty simple. The YesNo typeclass defines one function. That function takes one value of a type that can be considered to hold some concept of true-ness and tells us for sure if it’s true or not. Notice that from the way we use the a in the function, a has to be a concrete type.

              +
              +

              Pretty simple. The YesNo typeclass defines one function. That function takes one value of a type that can be considered to hold some concept of true-ness and tells us for sure if it’s true or not. Notice that from the way we use the a in the function, a has to be a concrete type.

              Next up, let’s define some instances. For numbers, we’ll assume that (like in JavaScript) any number that isn’t 0 is true-ish and 0 is false-ish.

              -
              +
              
               instance YesNo Int where
                   yesno 0 = False
                   yesno _ = True
              -
              +

              Empty lists (and by extensions, strings) are a no-ish value, while non-empty lists are a yes-ish value.

              -
              +
              
               instance YesNo [a] where
                   yesno [] = False
                   yesno _ = True
              -
              -

              Notice how we just put in a type parameter a in there to make the list a concrete type, even though we don’t make any assumptions about the type that’s contained in the list. What else, hmm … I know, Bool itself also holds true-ness and false-ness and it’s pretty obvious which is which.

              -
              +
              +

              Notice how we just put in a type parameter a in there to make the list a concrete type, even though we don’t make any assumptions about the type that’s contained in the list. What else, hmm … I know, Bool itself also holds true-ness and false-ness and it’s pretty obvious which is which.

              +
              
               instance YesNo Bool where
                   yesno = id
              -
              -

              Huh? What’s id? It’s just a standard library function that takes a parameter and returns the same thing, which is what we would be writing here anyway.

              -

              Let’s make Maybe a an instance too.

              -
              +
              +

              Huh? What’s id? It’s just a standard library function that takes a parameter and returns the same thing, which is what we would be writing here anyway.

              +

              Let’s make Maybe a an instance too.

              +
              
               instance YesNo (Maybe a) where
                   yesno (Just _) = True
                   yesno Nothing = False
              -
              -

              We didn’t need a class constraint because we made no assumptions about the contents of the Maybe. We just said that it’s true-ish if it’s a Just value and false-ish if it’s a Nothing. We still had to write out (Maybe a) instead of just Maybe because if you think about it, a Maybe -> Bool function can’t exist (because Maybe isn’t a concrete type), whereas a Maybe a -> Bool is fine and dandy. Still, this is really cool because now, any type of the form Maybe something is part of YesNo and it doesn’t matter what that something is.

              -

              Previously, we defined a Tree a type, that represented a binary search tree. We can say an empty tree is false-ish and anything that’s not an empty tree is true-ish.

              -
              +
              +

              We didn’t need a class constraint because we made no assumptions about the contents of the Maybe. We just said that it’s true-ish if it’s a Just value and false-ish if it’s a Nothing. We still had to write out (Maybe a) instead of just Maybe because if you think about it, a Maybe -> Bool function can’t exist (because Maybe isn’t a concrete type), whereas a Maybe a -> Bool is fine and dandy. Still, this is really cool because now, any type of the form Maybe something is part of YesNo and it doesn’t matter what that something is.

              +

              Previously, we defined a Tree a type, that represented a binary search tree. We can say an empty tree is false-ish and anything that’s not an empty tree is true-ish.

              +
              
               instance YesNo (Tree a) where
                   yesno EmptyTree = False
                   yesno _ = True
              -
              +

              Can a traffic light be a yes or no value? Sure. If it’s red, you stop. If it’s green, you go. If it’s yellow? Eh, I usually run the yellows because I live for adrenaline.

              -
              +
              
               instance YesNo TrafficLight where
                   yesno Red = False
                   yesno _ = True
              -
              +

              Cool, now that we have some instances, let’s go play!

              -
              +
              
               ghci> yesno $ length []
               False
               ghci> yesno "haha"
              @@ -864,14 +864,14 @@ 

              A yes-no typeclass

              True ghci> :t yesno yesno :: (YesNo a) => a -> Bool -
              -

              Right, it works! Let’s make a function that mimics the if statement, but it works with YesNo values.

              -
              +
              +

              Right, it works! Let’s make a function that mimics the if statement, but it works with YesNo values.

              +
              
               yesnoIf :: (YesNo y) => y -> a -> a -> a
               yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult
              -
              +

              Pretty straightforward. It takes a yes-no-ish value and two things. If the yes-no-ish value is more of a yes, it returns the first of the two things, otherwise it returns the second of them.

              -
              +
              
               ghci> yesnoIf [] "YEAH!" "NO!"
               "NO!"
               ghci> yesnoIf [2,3,4] "YEAH!" "NO!"
              @@ -882,41 +882,41 @@ 

              A yes-no typeclass

              "YEAH!" ghci> yesnoIf Nothing "YEAH!" "NO!" "NO!" -
              +

              The Functor typeclass

              -

              So far, we’ve encountered a lot of the typeclasses in the standard library. We’ve played with Ord, which is for stuff that can be ordered. We’ve palled around with Eq, which is for things that can be equated. We’ve seen Show, which presents an interface for types whose values can be displayed as strings. Our good friend Read is there whenever we need to convert a string to a value of some type. And now, we’re going to take a look at the Functor typeclass, which is basically for things that can be mapped over. You’re probably thinking about lists now, since mapping over lists is such a dominant idiom in Haskell. And you’re right, the list type is part of the Functor typeclass.

              -

              What better way to get to know the Functor typeclass than to see how it’s implemented? Let’s take a peek.

              -
              +

              So far, we’ve encountered a lot of the typeclasses in the standard library. We’ve played with Ord, which is for stuff that can be ordered. We’ve palled around with Eq, which is for things that can be equated. We’ve seen Show, which presents an interface for types whose values can be displayed as strings. Our good friend Read is there whenever we need to convert a string to a value of some type. And now, we’re going to take a look at the Functor typeclass, which is basically for things that can be mapped over. You’re probably thinking about lists now, since mapping over lists is such a dominant idiom in Haskell. And you’re right, the list type is part of the Functor typeclass.

              +

              What better way to get to know the Functor typeclass than to see how it’s implemented? Let’s take a peek.

              +
              
               class Functor f where
                   fmap :: (a -> b) -> f a -> f b
              -
              +
              I AM FUNCTOOOOR!!! -

              Alright. We see that it defines one function, fmap, and doesn’t provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so far, the type variable that played the role of the type in the typeclass was a concrete type, like the a in (==) :: (Eq a) => a -> a -> Bool. But now, the f is not a concrete type (a type that a value can hold, like Int, Bool or Maybe String), but a type constructor that takes one type parameter. A quick refresher example: Maybe Int is a concrete type, but Maybe is a type constructor that takes one type as the parameter. Anyway, we see that fmap takes a function from one type to another and a functor applied with one type and returns a functor applied with another type.

              -

              If this sounds a bit confusing, don’t worry. All will be revealed soon when we check out a few examples. Hmm, this type declaration for fmap reminds me of something. If you don’t know what the type signature of map is, it’s: map :: (a -> b) -> [a] -> [b].

              -

              Ah, interesting! It takes a function from one type to another and a list of one type and returns a list of another type. My friends, I think we have ourselves a functor! In fact, map is just a fmap that works only on lists. Here’s how the list is an instance of the Functor typeclass.

              -
              +

              Alright. We see that it defines one function, fmap, and doesn’t provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so far, the type variable that played the role of the type in the typeclass was a concrete type, like the a in (==) :: (Eq a) => a -> a -> Bool. But now, the f is not a concrete type (a type that a value can hold, like Int, Bool or Maybe String), but a type constructor that takes one type parameter. A quick refresher example: Maybe Int is a concrete type, but Maybe is a type constructor that takes one type as the parameter. Anyway, we see that fmap takes a function from one type to another and a functor applied with one type and returns a functor applied with another type.

              +

              If this sounds a bit confusing, don’t worry. All will be revealed soon when we check out a few examples. Hmm, this type declaration for fmap reminds me of something. If you don’t know what the type signature of map is, it’s: map :: (a -> b) -> [a] -> [b].

              +

              Ah, interesting! It takes a function from one type to another and a list of one type and returns a list of another type. My friends, I think we have ourselves a functor! In fact, map is just a fmap that works only on lists. Here’s how the list is an instance of the Functor typeclass.

              +
              
               instance Functor [] where
                   fmap = map
              -
              -

              That’s it! Notice how we didn’t write instance Functor [a] where, because from fmap :: (a -> b) -> f a -> f b, we see that the f has to be a type constructor that takes one type. [a] is already a concrete type (of a list with any type inside it), while [] is a type constructor that takes one type and can produce types such as [Int], [String] or even [[String]].

              -

              Since for lists, fmap is just map, we get the same results when using them on lists.

              -
              +
              +

              That’s it! Notice how we didn’t write instance Functor [a] where, because from fmap :: (a -> b) -> f a -> f b, we see that the f has to be a type constructor that takes one type. [a] is already a concrete type (of a list with any type inside it), while [] is a type constructor that takes one type and can produce types such as [Int], [String] or even [[String]].

              +

              Since for lists, fmap is just map, we get the same results when using them on lists.

              +
              
               map :: (a -> b) -> [a] -> [b]
               ghci> fmap (*2) [1..3]
               [2,4,6]
               ghci> map (*2) [1..3]
               [2,4,6]
              -
              -

              What happens when we map or fmap over an empty list? Well, of course, we get an empty list. It just turns an empty list of type [a] into an empty list of type [b].

              -

              Types that can act like a box can be functors. You can think of a list as a box that has an infinite amount of little compartments and they can all be empty, one can be full and the others empty or a number of them can be full. So, what else has the properties of being like a box? For one, the Maybe a type. In a way, it’s like a box that can either hold nothing, in which case it has the value of Nothing, or it can hold one item, like "HAHA", in which case it has a value of Just "HAHA". Here’s how Maybe is a functor.

              -
              +
              +

              What happens when we map or fmap over an empty list? Well, of course, we get an empty list. It just turns an empty list of type [a] into an empty list of type [b].

              +

              Types that can act like a box can be functors. You can think of a list as a box that has an infinite amount of little compartments and they can all be empty, one can be full and the others empty or a number of them can be full. So, what else has the properties of being like a box? For one, the Maybe a type. In a way, it’s like a box that can either hold nothing, in which case it has the value of Nothing, or it can hold one item, like "HAHA", in which case it has a value of Just "HAHA". Here’s how Maybe is a functor.

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

              Again, notice how we wrote instance Functor Maybe where instead of instance Functor (Maybe m) where, like we did when we were dealing with Maybe and YesNo. Functor wants a type constructor that takes one type and not a concrete type. If you mentally replace the fs with Maybes, fmap acts like a (a -> b) -> Maybe a -> Maybe b for this particular type, which looks OK. But if you replace f with (Maybe m), then it would seem to act like a (a -> b) -> Maybe m a -> Maybe m b, which doesn’t make any damn sense because Maybe takes just one type parameter.

              -

              Anyway, the fmap implementation is pretty simple. If it’s an empty value of Nothing, then just return a Nothing. If we map over an empty box, we get an empty box. It makes sense. Just like if we map over an empty list, we get back an empty list. If it’s not an empty value, but rather a single value packed up in a Just, then we apply the function on the contents of the Just.

              -
              +
              +

              Again, notice how we wrote instance Functor Maybe where instead of instance Functor (Maybe m) where, like we did when we were dealing with Maybe and YesNo. Functor wants a type constructor that takes one type and not a concrete type. If you mentally replace the fs with Maybes, fmap acts like a (a -> b) -> Maybe a -> Maybe b for this particular type, which looks OK. But if you replace f with (Maybe m), then it would seem to act like a (a -> b) -> Maybe m a -> Maybe m b, which doesn’t make any damn sense because Maybe takes just one type parameter.

              +

              Anyway, the fmap implementation is pretty simple. If it’s an empty value of Nothing, then just return a Nothing. If we map over an empty box, we get an empty box. It makes sense. Just like if we map over an empty list, we get back an empty list. If it’s not an empty value, but rather a single value packed up in a Just, then we apply the function on the contents of the Just.

              +
              
               ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something serious.")
               Just "Something serious. HEY GUYS IM INSIDE THE JUST"
               ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") Nothing
              @@ -925,122 +925,122 @@ 

              The Functor typeclass

              Just 400 ghci> fmap (*2) Nothing Nothing -
              -

              Another thing that can be mapped over and made an instance of Functor is our Tree a type. It can be thought of as a box in a way (holds several or no values) and the Tree type constructor takes exactly one type parameter. If you look at fmap as if it were a function made only for Tree, its type signature would look like (a -> b) -> Tree a -> Tree b. We’re going to use recursion on this one. Mapping over an empty tree will produce an empty tree. Mapping over a non-empty tree will be a tree consisting of our function applied to the root value and its left and right subtrees will be the previous subtrees, only our function will be mapped over them.

              -
              +
              +

              Another thing that can be mapped over and made an instance of Functor is our Tree a type. It can be thought of as a box in a way (holds several or no values) and the Tree type constructor takes exactly one type parameter. If you look at fmap as if it were a function made only for Tree, its type signature would look like (a -> b) -> Tree a -> Tree b. We’re going to use recursion on this one. Mapping over an empty tree will produce an empty tree. Mapping over a non-empty tree will be a tree consisting of our function applied to the root value and its left and right subtrees will be the previous subtrees, only our function will be mapped over them.

              +
              
               instance Functor Tree where
                   fmap f EmptyTree = EmptyTree
                   fmap f (Node x leftsub rightsub) = Node (f x) (fmap f leftsub) (fmap f rightsub)
              -
              -
              +
              +
              
               ghci> fmap (*2) EmptyTree
               EmptyTree
               ghci> fmap (*4) (foldr treeInsert EmptyTree [5,7,3,2,1,7])
               Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree
              -
              -

              Nice! Now how about Either a b? Can this be made a functor? The Functor typeclass wants a type constructor that takes only one type parameter but Either takes two. Hmmm! I know, we’ll partially apply Either by feeding it only one parameter so that it has one free parameter. Here’s how Either a is a functor in the standard libraries:

              -
              +
              +

              Nice! Now how about Either a b? Can this be made a functor? The Functor typeclass wants a type constructor that takes only one type parameter but Either takes two. Hmmm! I know, we’ll partially apply Either by feeding it only one parameter so that it has one free parameter. Here’s how Either a is a functor in the standard libraries:

              +
              
               instance Functor (Either a) where
                   fmap f (Right x) = Right (f x)
                   fmap f (Left x) = Left x
              -
              -

              Well well, what did we do here? You can see how we made Either a an instance instead of just Either. That’s because Either a is a type constructor that takes one parameter, whereas Either takes two. If fmap was specifically for Either a, the type signature would then be (b -> c) -> Either a b -> Either a c because that’s the same as (b -> c) -> (Either a) b -> (Either a) c. In the implementation, we mapped in the case of a Right value constructor, but we didn’t in the case of a Left. Why is that? Well, if we look back at how the Either a b type is defined, it’s kind of like:

              -
              +
              +

              Well well, what did we do here? You can see how we made Either a an instance instead of just Either. That’s because Either a is a type constructor that takes one parameter, whereas Either takes two. If fmap was specifically for Either a, the type signature would then be (b -> c) -> Either a b -> Either a c because that’s the same as (b -> c) -> (Either a) b -> (Either a) c. In the implementation, we mapped in the case of a Right value constructor, but we didn’t in the case of a Left. Why is that? Well, if we look back at how the Either a b type is defined, it’s kind of like:

              +
              
               data Either a b = Left a | Right b
              -
              -

              Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

              -

              This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.

              -

              Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

              Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

              -

              Try figuring out how Map k is made an instance of Functor by yourself!

              -

              With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

              -

              Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

              +
              +

              Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

              +

              This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.

              +

              Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

              Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

              +

              Try figuring out how Map k is made an instance of Functor by yourself!

              +

              With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

              +

              Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

              Kinds and some type-foo

              TYPE FOO MASTER -

              Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

              -

              So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

              -

              What are kinds and what are they good for? Well, let’s examine the kind of a type by using the :k command in GHCI.

              -
              +

              Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

              +

              So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

              +

              What are kinds and what are they good for? Well, let’s examine the kind of a type by using the :k command in GHCI.

              +
              
               ghci> :k Int
               Int :: *
              -
              -

              A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn’t take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven’t had to do that so far), I’d say star or just type.

              -

              Okay, now let’s see what the kind of Maybe is.

              -
              +
              +

              A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn’t take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven’t had to do that so far), I’d say star or just type.

              +

              Okay, now let’s see what the kind of Maybe is.

              +
              
               ghci> :k Maybe
               Maybe :: * -> *
              -
              -

              The Maybe type constructor takes one concrete type (like Int) and then returns a concrete type like Maybe Int. And that’s what this kind tells us. Just like Int -> Int means that a function takes an Int and returns an Int, * -> * means that the type constructor takes one concrete type and returns a concrete type. Let’s apply the type parameter to Maybe and see what the kind of that type is.

              -
              +
              +

              The Maybe type constructor takes one concrete type (like Int) and then returns a concrete type like Maybe Int. And that’s what this kind tells us. Just like Int -> Int means that a function takes an Int and returns an Int, * -> * means that the type constructor takes one concrete type and returns a concrete type. Let’s apply the type parameter to Maybe and see what the kind of that type is.

              +
              
               ghci> :k Maybe Int
               Maybe Int :: *
              -
              -

              Just like I expected! We applied the type parameter to Maybe and got back a concrete type (that’s what * -> * means). A parallel (although not equivalent, types and kinds are two different things) to this is if we do :t isUpper and :t isUpper 'A'. isUpper has a type of Char -> Bool and isUpper 'A' has a type of Bool, because its value is basically True. Both those types, however, have a kind of *.

              -

              We used :k on a type to get its kind, just like we can use :t on a value to get its type. Like we said, types are the labels of values and kinds are the labels of types and there are parallels between the two.

              +
              +

              Just like I expected! We applied the type parameter to Maybe and got back a concrete type (that’s what * -> * means). A parallel (although not equivalent, types and kinds are two different things) to this is if we do :t isUpper and :t isUpper 'A'. isUpper has a type of Char -> Bool and isUpper 'A' has a type of Bool, because its value is basically True. Both those types, however, have a kind of *.

              +

              We used :k on a type to get its kind, just like we can use :t on a value to get its type. Like we said, types are the labels of values and kinds are the labels of types and there are parallels between the two.

              Let’s look at another kind.

              -
              +
              
               ghci> :k Either
               Either :: * -> * -> *
              -
              -

              Aha, this tells us that Either takes two concrete types as type parameters to produce a concrete type. It also looks kind of like a type declaration of a function that takes two values and returns something. Type constructors are curried (just like functions), so we can partially apply them.

              -
              +
              +

              Aha, this tells us that Either takes two concrete types as type parameters to produce a concrete type. It also looks kind of like a type declaration of a function that takes two values and returns something. Type constructors are curried (just like functions), so we can partially apply them.

              +
              
               ghci> :k Either String
               Either String :: * -> *
               ghci> :k Either String Int
               Either String Int :: *
              -
              -

              When we wanted to make Either a part of the Functor typeclass, we had to partially apply it because Functor wants types that take only one parameter while Either takes two. In other words, Functor wants types of kind * -> * and so we had to partially apply Either to get a type of kind * -> * instead of its original kind * -> * -> *. If we look at the definition of Functor again

              -
              +
              +

              When we wanted to make Either a part of the Functor typeclass, we had to partially apply it because Functor wants types that take only one parameter while Either takes two. In other words, Functor wants types of kind * -> * and so we had to partially apply Either to get a type of kind * -> * instead of its original kind * -> * -> *. If we look at the definition of Functor again

              +
              
               class Functor f where
                   fmap :: (a -> b) -> f a -> f b
              -
              -

              we see that the f type variable is used as a type that takes one concrete type to produce a concrete type. We know it has to produce a concrete type because it’s used as the type of a value in a function. And from that, we can deduce that types that want to be friends with Functor have to be of kind * -> *.

              +
              +

              we see that the f type variable is used as a type that takes one concrete type to produce a concrete type. We know it has to produce a concrete type because it’s used as the type of a value in a function. And from that, we can deduce that types that want to be friends with Functor have to be of kind * -> *.

              Now, let’s do some type-foo. Take a look at this typeclass that I’m just going to make up right now:

              -
              +
              
               class Tofu t where
                   tofu :: j a -> t a j
              -
              -

              Man, that looks weird. How would we make a type that could be an instance of that strange typeclass? Well, let’s look at what its kind would have to be. Because j a is used as the type of a value that the tofu function takes as its parameter, j a has to have a kind of *. We assume * for a and so we can infer that j has to have a kind of * -> *. We see that t has to produce a concrete value too and that it takes two types. And knowing that a has a kind of * and j has a kind of * -> *, we infer that t has to have a kind of * -> (* -> *) -> *. So it takes a concrete type (a), a type constructor that takes one concrete type (j) and produces a concrete type. Wow.

              -

              OK, so let’s make a type with a kind of * -> (* -> *) -> *. Here’s one way of going about it.

              -
              +
              +

              Man, that looks weird. How would we make a type that could be an instance of that strange typeclass? Well, let’s look at what its kind would have to be. Because j a is used as the type of a value that the tofu function takes as its parameter, j a has to have a kind of *. We assume * for a and so we can infer that j has to have a kind of * -> *. We see that t has to produce a concrete value too and that it takes two types. And knowing that a has a kind of * and j has a kind of * -> *, we infer that t has to have a kind of * -> (* -> *) -> *. So it takes a concrete type (a), a type constructor that takes one concrete type (j) and produces a concrete type. Wow.

              +

              OK, so let’s make a type with a kind of * -> (* -> *) -> *. Here’s one way of going about it.

              +
              
               data Frank a b  = Frank {frankField :: b a} deriving (Show)
              -
              -

              How do we know this type has a kind of * -> (* -> *) - > *? Well, fields in ADTs are made to hold values, so they must be of kind *, obviously. We assume * for a, which means that b takes one type parameter and so its kind is * -> *. Now we know the kinds of both a and b and because they’re parameters for Frank, we see that Frank has a kind of * -> (* -> *) -> * The first * represents a and the (* -> *) represents b. Let’s make some Frank values and check out their types.

              -
              +
              +

              How do we know this type has a kind of * -> (* -> *) - > *? Well, fields in ADTs are made to hold values, so they must be of kind *, obviously. We assume * for a, which means that b takes one type parameter and so its kind is * -> *. Now we know the kinds of both a and b and because they’re parameters for Frank, we see that Frank has a kind of * -> (* -> *) -> * The first * represents a and the (* -> *) represents b. Let’s make some Frank values and check out their types.

              +
              
               ghci> :t Frank {frankField = Just "HAHA"}
               Frank {frankField = Just "HAHA"} :: Frank [Char] Maybe
               ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
               Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
               ghci> :t Frank {frankField = "YES"}
               Frank {frankField = "YES"} :: Frank Char []
              -
              -

              Hmm. Because frankField has a type of form a b, its values must have types that are of a similar form as well. So they can be Just "HAHA", which has a type of Maybe [Char] or it can have a value of ['Y','E','S'], which has a type of [Char] (if we used our own list type for this, it would have a type of List Char). And we see that the types of the Frank values correspond with the kind for Frank. [Char] has a kind of * and Maybe has a kind of * -> *. Because in order to have a value, it has to be a concrete type and thus has to be fully applied, every value of Frank blah blaah has a kind of *.

              -

              Making Frank an instance of Tofu is pretty simple. We see that tofu takes a j a (so an example type of that form would be Maybe Int) and returns a t a j. So if we replace Frank with j, the result type would be Frank Int Maybe.

              -
              +
              +

              Hmm. Because frankField has a type of form a b, its values must have types that are of a similar form as well. So they can be Just "HAHA", which has a type of Maybe [Char] or it can have a value of ['Y','E','S'], which has a type of [Char] (if we used our own list type for this, it would have a type of List Char). And we see that the types of the Frank values correspond with the kind for Frank. [Char] has a kind of * and Maybe has a kind of * -> *. Because in order to have a value, it has to be a concrete type and thus has to be fully applied, every value of Frank blah blaah has a kind of *.

              +

              Making Frank an instance of Tofu is pretty simple. We see that tofu takes a j a (so an example type of that form would be Maybe Int) and returns a t a j. So if we replace Frank with j, the result type would be Frank Int Maybe.

              +
              
               instance Tofu Frank where
                   tofu x = Frank x
              -
              -
              +
              +
              
               ghci> tofu (Just 'a') :: Frank Char Maybe
               Frank {frankField = Just 'a'}
               ghci> tofu ["HELLO"] :: Frank [Char] []
               Frank {frankField = ["HELLO"]}
              -
              +

              Not very useful, but we did flex our type muscles. Let’s do some more type-foo. We have this data type:

              -
              +
              
               data Barry t k p = Barry { yabba :: p, dabba :: t k }
              -
              -

              And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn’t look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it’s going to be something -> something -> something -> *. It’s safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let’s just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let’s check that with GHCI.

              -
              +
              +

              And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn’t look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it’s going to be something -> something -> something -> *. It’s safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let’s just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let’s check that with GHCI.

              +
              
               ghci> :k Barry
               Barry :: (* -> *) -> * -> * -> *
              -
              -

              Ah, we were right. How satisfying. Now, to make this type a part of Functor we have to partially apply the first two type parameters so that we’re left with * -> *. That means that the start of the instance declaration will be: instance Functor (Barry a b) where. If we look at fmap as if it was made specifically for Barry, it would have a type of fmap :: (a -> b) -> Barry c d a -> Barry c d b, because we just replace the Functor’s f with Barry c d. The third type parameter from Barry will have to change and we see that it’s conveniently in its own field.

              -
              +
              +

              Ah, we were right. How satisfying. Now, to make this type a part of Functor we have to partially apply the first two type parameters so that we’re left with * -> *. That means that the start of the instance declaration will be: instance Functor (Barry a b) where. If we look at fmap as if it was made specifically for Barry, it would have a type of fmap :: (a -> b) -> Barry c d a -> Barry c d b, because we just replace the Functor’s f with Barry c d. The third type parameter from Barry will have to change and we see that it’s conveniently in its own field.

              +
              
               instance Functor (Barry a b) where
                   fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}
              -
              -

              There we go! We just mapped the f over the first field.

              -

              In this section, we took a good look at how type parameters work and kind of formalized them with kinds, just like we formalized function parameters with type declarations. We saw that there are interesting parallels between functions and type constructors. They are, however, two completely different things. When working on real Haskell, you usually won’t have to mess with kinds and do kind inference by hand like we did now. Usually, you just have to partially apply your own type to * -> * or * when making it an instance of one of the standard typeclasses, but it’s good to know how and why that actually works. It’s also interesting to see that types have little types of their own. Again, you don’t really have to understand everything we did here to read on, but if you understand how kinds work, chances are that you have a very solid grasp of Haskell’s type system.

              +
              +

              There we go! We just mapped the f over the first field.

              +

              In this section, we took a good look at how type parameters work and kind of formalized them with kinds, just like we formalized function parameters with type declarations. We saw that there are interesting parallels between functions and type constructors. They are, however, two completely different things. When working on real Haskell, you usually won’t have to mess with kinds and do kind inference by hand like we did now. Usually, you just have to partially apply your own type to * -> * or * when making it an instance of one of the standard typeclasses, but it’s good to know how and why that actually works. It’s also interesting to see that types have little types of their own. Again, you don’t really have to understand everything we did here to read on, but if you understand how kinds work, chances are that you have a very solid grasp of Haskell’s type system.

              • diff --git a/docs/modules.html b/docs/modules.html index 2152a39..8bf1076 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -35,103 +35,103 @@

                Modules

                Loading modules

                modules

                A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

                -

                The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There’s a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we’ve dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we’re going to examine a few useful modules and the functions that they have. But first, we’re going to see how to import modules.

                -

                The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file. One script can, of course, import several modules. Just put each import statement into a separate line. Let’s import the Data.List module, which has a bunch of useful functions for working with lists and use a function that it exports to create a function that tells us how many unique elements a list has.

                -
                +

                The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There’s a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we’ve dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we’re going to examine a few useful modules and the functions that they have. But first, we’re going to see how to import modules.

                +

                The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file. One script can, of course, import several modules. Just put each import statement into a separate line. Let’s import the Data.List module, which has a bunch of useful functions for working with lists and use a function that it exports to create a function that tells us how many unique elements a list has.

                +
                
                 import Data.List
                 
                 numUniques :: (Eq a) => [a] -> Int
                 numUniques = length . nub
                -
                -

                When you do import Data.List, all the functions that Data.List exports become available in the global namespace, meaning that you can call them from wherever in the script. nub is a function defined in Data.List that takes a list and weeds out duplicate elements. Composing length and nub by doing length . nub produces a function that’s the equivalent of \xs -> length (nub xs).

                -

                You can also put the functions of modules into the global namespace when using GHCI. If you’re in GHCI and you want to be able to call the functions exported by Data.List, do this:

                -
                +
                +

                When you do import Data.List, all the functions that Data.List exports become available in the global namespace, meaning that you can call them from wherever in the script. nub is a function defined in Data.List that takes a list and weeds out duplicate elements. Composing length and nub by doing length . nub produces a function that’s the equivalent of \xs -> length (nub xs).

                +

                You can also put the functions of modules into the global namespace when using GHCI. If you’re in GHCI and you want to be able to call the functions exported by Data.List, do this:

                +
                
                 ghci> :m + Data.List
                -
                -

                If we want to load up the names from several modules inside GHCI, we don’t have to do :m + several times, we can just load up several modules at once.

                -
                +
                +

                If we want to load up the names from several modules inside GHCI, we don’t have to do :m + several times, we can just load up several modules at once.

                +
                
                 ghci> :m + Data.List Data.Map Data.Set
                -
                -

                However, if you’ve loaded a script that already imports a module, you don’t need to use :m + to get access to it.

                -

                If you just need a couple of functions from a module, you can selectively import just those functions. If we wanted to import only the nub and sort functions from Data.List, we’d do this:

                -
                +
                +

                However, if you’ve loaded a script that already imports a module, you don’t need to use :m + to get access to it.

                +

                If you just need a couple of functions from a module, you can selectively import just those functions. If we wanted to import only the nub and sort functions from Data.List, we’d do this:

                +
                
                 import Data.List (nub, sort)
                -
                -

                You can also choose to import all of the functions of a module except a few select ones. That’s often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that’s called nub and we want to import all the functions from Data.List except the nub function:

                -
                +
                +

                You can also choose to import all of the functions of a module except a few select ones. That’s often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that’s called nub and we want to import all the functions from Data.List except the nub function:

                +
                
                 import Data.List hiding (nub)
                -
                -

                Another way of dealing with name clashes is to do qualified imports. The Data.Map module, which offers a data structure for looking up values by key, exports a bunch of functions with the same name as Prelude functions, like filter or null. So when we import Data.Map and then call filter, Haskell won’t know which function to use. Here’s how we solve this:

                -
                +
                +

                Another way of dealing with name clashes is to do qualified imports. The Data.Map module, which offers a data structure for looking up values by key, exports a bunch of functions with the same name as Prelude functions, like filter or null. So when we import Data.Map and then call filter, Haskell won’t know which function to use. Here’s how we solve this:

                +
                
                 import qualified Data.Map
                -
                -

                This makes it so that if we want to reference Data.Map’s filter function, we have to do Data.Map.filter, whereas just filter still refers to the normal filter we all know and love. But typing out Data.Map in front of every function from that module is kind of tedious. That’s why we can rename the qualified import to something shorter:

                -
                +
                +

                This makes it so that if we want to reference Data.Map’s filter function, we have to do Data.Map.filter, whereas just filter still refers to the normal filter we all know and love. But typing out Data.Map in front of every function from that module is kind of tedious. That’s why we can rename the qualified import to something shorter:

                +
                
                 import qualified Data.Map as M
                -
                -

                Now, to reference Data.Map’s filter function, we just use M.filter.

                +
                +

                Now, to reference Data.Map’s filter function, we just use M.filter.

                Use this handy reference to see which modules are in the standard library. A great way to pick up new Haskell knowledge is to just click through the standard library reference and explore the modules and their functions. You can also view the Haskell source code for each module. Reading the source code of some modules is a really good way to learn Haskell and get a solid feel for it.

                To search for functions or to find out where they’re located, use Hoogle. It’s a really awesome Haskell search engine, you can search by name, module name or even type signature.

                Data.List

                -

                The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We’ve already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don’t have to import Data.List via a qualified import because it doesn’t clash with any Prelude names except for those that Prelude already steals from Data.List. Let’s take a look at some of the functions that we haven’t met before.

                -

                intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here’s a demonstration:

                -
                +

                The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We’ve already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don’t have to import Data.List via a qualified import because it doesn’t clash with any Prelude names except for those that Prelude already steals from Data.List. Let’s take a look at some of the functions that we haven’t met before.

                +

                intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here’s a demonstration:

                +
                
                 ghci> intersperse '.' "MONKEY"
                 "M.O.N.K.E.Y"
                 ghci> intersperse 0 [1,2,3,4,5,6]
                 [1,0,2,0,3,0,4,0,5,0,6]
                -
                -

                intercalate takes a list and a list of lists. It then inserts that list in between all those lists and then flattens the result.

                -
                +
                +

                intercalate takes a list and a list of lists. It then inserts that list in between all those lists and then flattens the result.

                +
                
                 ghci> intercalate " " ["hey","there","folks"]
                 "hey there folks"
                 ghci> intercalate [0,0,0] [[1,2,3],[4,5,6],[7,8,9]]
                 [1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]
                -
                -

                transpose transposes a list of lists. If you look at a list of lists as a 2D matrix, the columns become the rows and vice versa.

                -
                +
                +

                transpose transposes a list of lists. If you look at a list of lists as a 2D matrix, the columns become the rows and vice versa.

                +
                
                 ghci> transpose [[1,2,3],[4,5,6],[7,8,9]]
                 [[1,4,7],[2,5,8],[3,6,9]]
                 ghci> transpose ["hey","there","folks"]
                 ["htg","ehu","yey","rs","e"]
                -
                -

                Say we have the polynomials 3x2 + 5x + 9, 10x3 + 9 and 8x3 + 5x2 + x - 1 and we want to add them together. We can use the lists [0,3,5,9], [10,0,0,9] and [8,5,1,-1] to represent them in Haskell. Now, to add them, all we have to do is this:

                -
                +
                +

                Say we have the polynomials 3x2 + 5x + 9, 10x3 + 9 and 8x3 + 5x2 + x - 1 and we want to add them together. We can use the lists [0,3,5,9], [10,0,0,9] and [8,5,1,-1] to represent them in Haskell. Now, to add them, all we have to do is this:

                +
                
                 ghci> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]
                 [18,8,6,17]
                -
                -

                When we transpose these three lists, the third powers are then in the first row, the second powers in the second one and so on. Mapping sum to that produces our desired result.

                +
                +

                When we transpose these three lists, the third powers are then in the first row, the second powers in the second one and so on. Mapping sum to that produces our desired result.

                shopping lists -

                foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, you might often get a stack overflow error. The culprit for that is that due to the lazy nature of the folds, the accumulator value isn’t actually updated as the folding happens. What actually happens is that the accumulator kind of makes a promise that it will compute its value when asked to actually produce the result (also called a thunk). That happens for every intermediate accumulator and all those thunks overflow your stack. The strict folds aren’t lazy buggers and actually compute the intermediate values as they go along instead of filling up your stack with thunks. So if you ever get stack overflow errors when doing lazy folds, try switching to their strict versions.

                -

                concat flattens a list of lists into just a list of elements.

                -
                +

                foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, you might often get a stack overflow error. The culprit for that is that due to the lazy nature of the folds, the accumulator value isn’t actually updated as the folding happens. What actually happens is that the accumulator kind of makes a promise that it will compute its value when asked to actually produce the result (also called a thunk). That happens for every intermediate accumulator and all those thunks overflow your stack. The strict folds aren’t lazy buggers and actually compute the intermediate values as they go along instead of filling up your stack with thunks. So if you ever get stack overflow errors when doing lazy folds, try switching to their strict versions.

                +

                concat flattens a list of lists into just a list of elements.

                +
                
                 ghci> concat ["foo","bar","car"]
                 "foobarcar"
                 ghci> concat [[3,4,5],[2,3,4],[2,1,1]]
                 [3,4,5,2,3,4,2,1,1]
                -
                -

                It will just remove one level of nesting. So if you want to completely flatten [[[2,3],[3,4,5],[2]],[[2,3],[3,4]]], which is a list of lists of lists, you have to concatenate it twice.

                -

                Doing concatMap is the same as first mapping a function to a list and then concatenating the list with concat.

                -
                +
                +

                It will just remove one level of nesting. So if you want to completely flatten [[[2,3],[3,4,5],[2]],[[2,3],[3,4]]], which is a list of lists of lists, you have to concatenate it twice.

                +

                Doing concatMap is the same as first mapping a function to a list and then concatenating the list with concat.

                +
                
                 ghci> concatMap (replicate 4) [1..3]
                 [1,1,1,1,2,2,2,2,3,3,3,3]
                -
                -

                and takes a list of boolean values and returns True only if all the values in the list are True.

                -
                +
                +

                and takes a list of boolean values and returns True only if all the values in the list are True.

                +
                
                 ghci> and $ map (>4) [5,6,7,8]
                 True
                 ghci> and $ map (==4) [4,4,4,3,4]
                 False
                -
                -

                or is like and, only it returns True if any of the boolean values in a list is True.

                -
                +
                +

                or is like and, only it returns True if any of the boolean values in a list is True.

                +
                
                 ghci> or $ map (==4) [2,3,4,5,6,1]
                 True
                 ghci> or $ map (>4) [1,2,3]
                 False
                -
                -

                any and all take a predicate and then check if any or all the elements in a list satisfy the predicate, respectively. Usually we use these two functions instead of mapping over a list and then doing and or or.

                -
                +
                +

                any and all take a predicate and then check if any or all the elements in a list satisfy the predicate, respectively. Usually we use these two functions instead of mapping over a list and then doing and or or.

                +
                
                 ghci> any (==4) [2,3,5,6,1,4]
                 True
                 ghci> all (>4) [6,9,10]
                @@ -140,16 +140,16 @@ 

                Data.List

                False ghci> any (`elem` ['A'..'Z']) "HEYGUYSwhatsup" True -
                -

                iterate takes a function and a starting value. It applies the function to the starting value, then it applies that function to the result, then it applies the function to that result again, etc. It returns all the results in the form of an infinite list.

                -
                +
                +

                iterate takes a function and a starting value. It applies the function to the starting value, then it applies that function to the result, then it applies the function to that result again, etc. It returns all the results in the form of an infinite list.

                +
                
                 ghci> take 10 $ iterate (*2) 1
                 [1,2,4,8,16,32,64,128,256,512]
                 ghci> take 3 $ iterate (++ "haha") "haha"
                 ["haha","hahahaha","hahahahahaha"]
                -
                -

                splitAt takes a number and a list. It then splits the list at that many elements, returning the resulting two lists in a tuple.

                -
                +
                +

                splitAt takes a number and a list. It then splits the list at that many elements, returning the resulting two lists in a tuple.

                +
                
                 ghci> splitAt 3 "heyman"
                 ("hey","man")
                 ghci> splitAt 100 "heyman"
                @@ -158,91 +158,91 @@ 

                Data.List

                ("","heyman") ghci> let (a,b) = splitAt 3 "foobar" in b ++ a "barfoo" -
                -

                takeWhile is a really useful little function. It takes elements from a list while the predicate holds and then when an element is encountered that doesn’t satisfy the predicate, it’s cut off. It turns out this is very useful.

                -
                +
                +

                takeWhile is a really useful little function. It takes elements from a list while the predicate holds and then when an element is encountered that doesn’t satisfy the predicate, it’s cut off. It turns out this is very useful.

                +
                
                 ghci> takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1]
                 [6,5,4]
                 ghci> takeWhile (/=' ') "This is a sentence"
                 "This"
                -
                -

                Say we wanted to know the sum of all third powers that are under 10,000. We can’t map (^3) to [1..], apply a filter and then try to sum that up because filtering an infinite list never finishes. You may know that all the elements here are ascending but Haskell doesn’t. That’s why we can do this:

                -
                +
                +

                Say we wanted to know the sum of all third powers that are under 10,000. We can’t map (^3) to [1..], apply a filter and then try to sum that up because filtering an infinite list never finishes. You may know that all the elements here are ascending but Haskell doesn’t. That’s why we can do this:

                +
                
                 ghci> sum $ takeWhile (<10000) $ map (^3) [1..]
                 53361
                -
                -

                We apply (^3) to an infinite list and then once an element that’s over 10,000 is encountered, the list is cut off. Now we can sum it up easily.

                -

                dropWhile is similar, only it drops all the elements while the predicate is true. Once predicate equates to False, it returns the rest of the list. An extremely useful and lovely function!

                -
                +
                +

                We apply (^3) to an infinite list and then once an element that’s over 10,000 is encountered, the list is cut off. Now we can sum it up easily.

                +

                dropWhile is similar, only it drops all the elements while the predicate is true. Once predicate equates to False, it returns the rest of the list. An extremely useful and lovely function!

                +
                
                 ghci> dropWhile (/=' ') "This is a sentence"
                 " is a sentence"
                 ghci> dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1]
                 [3,4,5,4,3,2,1]
                -
                +

                We’re given a list that represents the value of a stock by date. The list is made of tuples whose first component is the stock value, the second is the year, the third is the month and the fourth is the date. We want to know when the stock value first exceeded one thousand dollars!

                -
                +
                
                 ghci> let stock = [(994.4,2008,9,1),(995.2,2008,9,2),(999.2,2008,9,3),(1001.4,2008,9,4),(998.3,2008,9,5)]
                 ghci> head (dropWhile (\(val,y,m,d) -> val < 1000) stock)
                 (1001.4,2008,9,4)
                -
                -

                span is kind of like takeWhile, only it returns a pair of lists. The first list contains everything the resulting list from takeWhile would contain if it were called with the same predicate and the same list. The second list contains the part of the list that would have been dropped.

                -
                +
                +

                span is kind of like takeWhile, only it returns a pair of lists. The first list contains everything the resulting list from takeWhile would contain if it were called with the same predicate and the same list. The second list contains the part of the list that would have been dropped.

                +
                
                 ghci> let (fw, rest) = span (/=' ') "This is a sentence" in "First word:" ++ fw ++ ", the rest:" ++ rest
                 "First word: This, the rest: is a sentence"
                -
                -

                Whereas span spans the list while the predicate is true, break breaks it when the predicate is first true. Doing break p is the equivalent of doing span (not . p).

                -
                +
                +

                Whereas span spans the list while the predicate is true, break breaks it when the predicate is first true. Doing break p is the equivalent of doing span (not . p).

                +
                
                 ghci> break (==4) [1,2,3,4,5,6,7]
                 ([1,2,3],[4,5,6,7])
                 ghci> span (/=4) [1,2,3,4,5,6,7]
                 ([1,2,3],[4,5,6,7])
                -
                -

                When using break, the second list in the result will start with the first element that satisfies the predicate.

                -

                sort simply sorts a list. The type of the elements in the list has to be part of the Ord typeclass, because if the elements of a list can’t be put in some kind of order, then the list can’t be sorted.

                -
                +
                +

                When using break, the second list in the result will start with the first element that satisfies the predicate.

                +

                sort simply sorts a list. The type of the elements in the list has to be part of the Ord typeclass, because if the elements of a list can’t be put in some kind of order, then the list can’t be sorted.

                +
                
                 ghci> sort [8,5,3,2,1,6,4,2]
                 [1,2,2,3,4,5,6,8]
                 ghci> sort "This will be sorted soon"
                 "    Tbdeehiillnooorssstw"
                -
                -

                group takes a list and groups adjacent elements into sublists if they are equal.

                -
                +
                +

                group takes a list and groups adjacent elements into sublists if they are equal.

                +
                
                 ghci> group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
                 [[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]
                -
                +

                If we sort a list before grouping it, we can find out how many times each element appears in the list.

                -
                +
                
                 ghci> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
                 [(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]
                -
                -

                inits and tails are like init and tail, only they recursively apply that to a list until there’s nothing left. Observe.

                -
                +
                +

                inits and tails are like init and tail, only they recursively apply that to a list until there’s nothing left. Observe.

                +
                
                 ghci> inits "w00t"
                 ["","w","w0","w00","w00t"]
                 ghci> tails "w00t"
                 ["w00t","00t","0t","t",""]
                 ghci> let w = "w00t" in zip (inits w) (tails w)
                 [("","w00t"),("w","00t"),("w0","0t"),("w00","t"),("w00t","")]
                -
                +

                Let’s use a fold to implement searching a list for a sublist.

                -
                +
                
                 search :: (Eq a) => [a] -> [a] -> Bool
                 search needle haystack =
                     let nlen = length needle
                     in  foldl (\acc x -> if take nlen x == needle then True else acc) False (tails haystack)
                -
                -

                First we call tails with the list in which we’re searching. Then we go over each tail and see if it starts with what we’re looking for.

                -

                With that, we actually just made a function that behaves like isInfixOf. isInfixOf searches for a sublist within a list and returns True if the sublist we’re looking for is somewhere inside the target list.

                -
                +
                +

                First we call tails with the list in which we’re searching. Then we go over each tail and see if it starts with what we’re looking for.

                +

                With that, we actually just made a function that behaves like isInfixOf. isInfixOf searches for a sublist within a list and returns True if the sublist we’re looking for is somewhere inside the target list.

                +
                
                 ghci> "cat" `isInfixOf` "im a cat burglar"
                 True
                 ghci> "Cat" `isInfixOf` "im a cat burglar"
                 False
                 ghci> "cats" `isInfixOf` "im a cat burglar"
                 False
                -
                -

                isPrefixOf and isSuffixOf search for a sublist at the beginning and at the end of a list, respectively.

                -
                +
                +

                isPrefixOf and isSuffixOf search for a sublist at the beginning and at the end of a list, respectively.

                +
                
                 ghci> "hey" `isPrefixOf` "hey there!"
                 True
                 ghci> "hey" `isPrefixOf` "oh hey there!"
                @@ -251,211 +251,211 @@ 

                Data.List

                True ghci> "there!" `isSuffixOf` "oh hey there" False -
                -

                elem and notElem check if an element is or isn’t inside a list.

                -

                partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don’t.

                -
                +
                +

                elem and notElem check if an element is or isn’t inside a list.

                +

                partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don’t.

                +
                
                 ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
                 ("BOBMORGAN","sidneyeddy")
                 ghci> partition (>3) [1,3,5,6,3,2,1,0,3,7]
                 ([5,6,7],[1,3,3,2,1,0,3])
                -
                -

                It’s important to understand how this is different from span and break:

                -
                +
                +

                It’s important to understand how this is different from span and break:

                +
                
                 ghci> span (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
                 ("BOB","sidneyMORGANeddy")
                -
                -

                While span and break are done once they encounter the first element that doesn’t and does satisfy the predicate, partition goes through the whole list and splits it up according to the predicate.

                -

                find takes a list and a predicate and returns the first element that satisfies the predicate. But it returns that element wrapped in a Maybe value. We’ll be covering algebraic data types more in depth in the next chapter but for now, this is what you need to know: a Maybe value can either be Just something or Nothing. Much like a list can be either an empty list or a list with some elements, a Maybe value can be either no elements or a single element. And like the type of a list of, say, integers is [Int], the type of maybe having an integer is Maybe Int. Anyway, let’s take our find function for a spin.

                -
                +
                +

                While span and break are done once they encounter the first element that doesn’t and does satisfy the predicate, partition goes through the whole list and splits it up according to the predicate.

                +

                find takes a list and a predicate and returns the first element that satisfies the predicate. But it returns that element wrapped in a Maybe value. We’ll be covering algebraic data types more in depth in the next chapter but for now, this is what you need to know: a Maybe value can either be Just something or Nothing. Much like a list can be either an empty list or a list with some elements, a Maybe value can be either no elements or a single element. And like the type of a list of, say, integers is [Int], the type of maybe having an integer is Maybe Int. Anyway, let’s take our find function for a spin.

                +
                
                 ghci> find (>4) [1,2,3,4,5,6]
                 Just 5
                 ghci> find (>9) [1,2,3,4,5,6]
                 Nothing
                 ghci> :t find
                 find :: (a -> Bool) -> [a] -> Maybe a
                -
                -

                Notice the type of find. Its result is Maybe a. That’s kind of like having the type of [a], only a value of the type Maybe can contain either no elements or one element, whereas a list can contain no elements, one element or several elements.

                -

                Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we’d be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we’d get back a Nothing. But if there was a valid answer in that list, we’d get, say, Just (1001.4,2008,9,4).

                -

                elemIndex is kind of like elem, only it doesn’t return a boolean value. It maybe returns the index of the element we’re looking for. If that element isn’t in our list, it returns a Nothing.

                -
                +
                +

                Notice the type of find. Its result is Maybe a. That’s kind of like having the type of [a], only a value of the type Maybe can contain either no elements or one element, whereas a list can contain no elements, one element or several elements.

                +

                Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we’d be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we’d get back a Nothing. But if there was a valid answer in that list, we’d get, say, Just (1001.4,2008,9,4).

                +

                elemIndex is kind of like elem, only it doesn’t return a boolean value. It maybe returns the index of the element we’re looking for. If that element isn’t in our list, it returns a Nothing.

                +
                
                 ghci> :t elemIndex
                 elemIndex :: (Eq a) => a -> [a] -> Maybe Int
                 ghci> 4 `elemIndex` [1,2,3,4,5,6]
                 Just 3
                 ghci> 10 `elemIndex` [1,2,3,4,5,6]
                 Nothing
                -
                -

                elemIndices is like elemIndex, only it returns a list of indices, in case the element we’re looking for crops up in our list several times. Because we’re using a list to represent the indices, we don’t need a Maybe type, because failure can be represented as the empty list, which is very much synonymous to Nothing.

                -
                -ghci> ' ' `elemIndices` "Where are the spaces?"
                +
                +

                elemIndices is like elemIndex, only it returns a list of indices, in case the element we’re looking for crops up in our list several times. Because we’re using a list to represent the indices, we don’t need a Maybe type, because failure can be represented as the empty list, which is very much synonymous to Nothing.

                +
                
                +ghci> ' ' `elemIndices` "Where are the spaces?"
                 [5,9,13]
                -
                -

                findIndex is like find, but it maybe returns the index of the first element that satisfies the predicate. findIndices returns the indices of all elements that satisfy the predicate in the form of a list.

                -
                +
                +

                findIndex is like find, but it maybe returns the index of the first element that satisfies the predicate. findIndices returns the indices of all elements that satisfy the predicate in the form of a list.

                +
                
                 ghci> findIndex (==4) [5,3,2,1,6,4]
                 Just 5
                 ghci> findIndex (==7) [5,3,2,1,6,4]
                 Nothing
                -ghci> findIndices (`elem` ['A'..'Z']) "Where Are The Caps?"
                +ghci> findIndices (`elem` ['A'..'Z']) "Where Are The Caps?"
                 [0,6,10,14]
                -
                -

                We already covered zip and zipWith. We noted that they zip together two lists, either in a tuple or with a binary function (meaning such a function that takes two parameters). But what if we want to zip together three lists? Or zip three lists with a function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. While this may look like a hack, it works out pretty fine, because there aren’t many times when you want to zip 8 lists together. There’s also a very clever way for zipping infinite numbers of lists, but we’re not advanced enough to cover that just yet.

                -
                +
                +

                We already covered zip and zipWith. We noted that they zip together two lists, either in a tuple or with a binary function (meaning such a function that takes two parameters). But what if we want to zip together three lists? Or zip three lists with a function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. While this may look like a hack, it works out pretty fine, because there aren’t many times when you want to zip 8 lists together. There’s also a very clever way for zipping infinite numbers of lists, but we’re not advanced enough to cover that just yet.

                +
                
                 ghci> zipWith3 (\x y z -> x + y + z) [1,2,3] [4,5,2,2] [2,2,3]
                 [7,9,8]
                 ghci> zip4 [2,3,3] [2,2,2] [5,5,3] [2,2,2]
                 [(2,2,5,2),(3,2,5,2),(3,2,3,2)]
                -
                +

                Just like with normal zipping, lists that are longer than the shortest list that’s being zipped are cut down to size.

                -

                lines is a useful function when dealing with files or input from somewhere. It takes a string and returns every line of that string as separate element of a list.

                -
                +

                lines is a useful function when dealing with files or input from somewhere. It takes a string and returns every line of that string as separate element of a list.

                +
                
                 ghci> lines "first line\nsecond line\nthird line"
                 ["first line","second line","third line"]
                -
                -

                '\n' is the character for a unix newline. Backslashes have special meaning in Haskell strings and characters.

                -

                unlines is the inverse function of lines. It takes a list of strings and joins them together using a '\n'.

                -
                +
                +

                '\n' is the character for a unix newline. Backslashes have special meaning in Haskell strings and characters.

                +

                unlines is the inverse function of lines. It takes a list of strings and joins them together using a '\n'.

                +
                
                 ghci> unlines ["first line", "second line", "third line"]
                 "first line\nsecond line\nthird line\n"
                -
                -

                words and unwords are for splitting a line of text into words or joining a list of words into a text. Very useful.

                -
                +
                +

                words and unwords are for splitting a line of text into words or joining a list of words into a text. Very useful.

                +
                
                 ghci> words "hey these are the words in this sentence"
                 ["hey","these","are","the","words","in","this","sentence"]
                 ghci> words "hey these           are    the words in this\nsentence"
                 ["hey","these","are","the","words","in","this","sentence"]
                 ghci> unwords ["hey","there","mate"]
                 "hey there mate"
                -
                -

                We’ve already mentioned nub. It takes a list and weeds out the duplicate elements, returning a list whose every element is a unique snowflake! The function does have a kind of strange name. It turns out that “nub” means a small lump or essential part of something. In my opinion, they should use real words for function names instead of old-people words.

                -
                +
                +

                We’ve already mentioned nub. It takes a list and weeds out the duplicate elements, returning a list whose every element is a unique snowflake! The function does have a kind of strange name. It turns out that “nub” means a small lump or essential part of something. In my opinion, they should use real words for function names instead of old-people words.

                +
                
                 ghci> nub [1,2,3,4,3,2,1,2,3,4,3,2,1]
                 [1,2,3,4]
                 ghci> nub "Lots of words and stuff"
                 "Lots fwrdanu"
                -
                -

                delete takes an element and a list and deletes the first occurrence of that element in the list.

                -
                +
                +

                delete takes an element and a list and deletes the first occurrence of that element in the list.

                +
                
                 ghci> delete 'h' "hey there ghang!"
                 "ey there ghang!"
                 ghci> delete 'h' . delete 'h' $ "hey there ghang!"
                 "ey tere ghang!"
                 ghci> delete 'h' . delete 'h' . delete 'h' $ "hey there ghang!"
                 "ey tere gang!"
                -
                -

                \\ is the list difference function. It acts like a set difference, basically. For every element in the right-hand list, it removes a matching element in the left one.

                -
                +
                +

                \\ is the list difference function. It acts like a set difference, basically. For every element in the right-hand list, it removes a matching element in the left one.

                +
                
                 ghci> [1..10] \\ [2,5,9]
                 [1,3,4,6,7,8,10]
                 ghci> "Im a big baby" \\ "big"
                 "Im a  baby"
                -
                -

                Doing [1..10] \\ [2,5,9] is like doing delete 2 . delete 5 . delete 9 $ [1..10].

                -

                union also acts like a function on sets. It returns the union of two lists. It pretty much goes over every element in the second list and appends it to the first one if it isn’t already in yet. Watch out though, duplicates are removed from the second list!

                -
                -ghci> "hey man" `union` "man what's up"
                -"hey manwt'sup"
                +
                +

                Doing [1..10] \\ [2,5,9] is like doing delete 2 . delete 5 . delete 9 $ [1..10].

                +

                union also acts like a function on sets. It returns the union of two lists. It pretty much goes over every element in the second list and appends it to the first one if it isn’t already in yet. Watch out though, duplicates are removed from the second list!

                +
                
                +ghci> "hey man" `union` "man what's up"
                +"hey manwt'sup"
                 ghci> [1..7] `union` [5..10]
                 [1,2,3,4,5,6,7,8,9,10]
                -
                -

                intersect works like set intersection. It returns only the elements that are found in both lists.

                -
                +
                +

                intersect works like set intersection. It returns only the elements that are found in both lists.

                +
                
                 ghci> [1..7] `intersect` [5..10]
                 [5,6,7]
                -
                -

                insert takes an element and a list of elements that can be sorted and inserts it into the last position where it’s still less than or equal to the next element. In other words, insert will start at the beginning of the list and then keep going until it finds an element that’s equal to or greater than the element that we’re inserting and it will insert it just before the element.

                -
                +
                +

                insert takes an element and a list of elements that can be sorted and inserts it into the last position where it’s still less than or equal to the next element. In other words, insert will start at the beginning of the list and then keep going until it finds an element that’s equal to or greater than the element that we’re inserting and it will insert it just before the element.

                +
                
                 ghci> insert 4 [3,5,1,2,8,2]
                 [3,4,5,1,2,8,2]
                 ghci> insert 4 [1,3,4,4,1]
                 [1,3,4,4,4,1]
                -
                -

                The 4 is inserted right after the 3 and before the 5 in the first example and in between the 3 and 4 in the second example.

                -

                If we use insert to insert into a sorted list, the resulting list will be kept sorted.

                -
                +
                +

                The 4 is inserted right after the 3 and before the 5 in the first example and in between the 3 and 4 in the second example.

                +

                If we use insert to insert into a sorted list, the resulting list will be kept sorted.

                +
                
                 ghci> insert 4 [1,2,3,5,6,7]
                 [1,2,3,4,5,6,7]
                 ghci> insert 'g' $ ['a'..'f'] ++ ['h'..'z']
                 "abcdefghijklmnopqrstuvwxyz"
                 ghci> insert 3 [1,2,4,3,2,1]
                 [1,2,3,4,3,2,1]
                -
                -

                What length, take, drop, splitAt, !! and replicate have in common is that they take an Int as one of their parameters (or return an Int), even though they could be more generic and usable if they just took any type that’s part of the Integral or Num typeclasses (depending on the functions). They do that for historical reasons. However, fixing that would probably break a lot of existing code. That’s why Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, length has a type signature of length :: [a] -> Int. If we try to get the average of a list of numbers by doing let xs = [1..6] in sum xs / length xs, we get a type error, because you can’t use / with an Int. genericLength, on the other hand, has a type signature of genericLength :: (Num a) => [b] -> a. Because a Num can act like a floating point number, getting the average by doing let xs = [1..6] in sum xs / genericLength xs works out just fine.

                -

                The nub, delete, union, intersect and group functions all have their more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is that the first set of functions use == to test for equality, whereas the By ones also take an equality function and then compare them by using that equality function. group is the same as groupBy (==).

                -

                For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That’s where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

                -
                +
                +

                What length, take, drop, splitAt, !! and replicate have in common is that they take an Int as one of their parameters (or return an Int), even though they could be more generic and usable if they just took any type that’s part of the Integral or Num typeclasses (depending on the functions). They do that for historical reasons. However, fixing that would probably break a lot of existing code. That’s why Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, length has a type signature of length :: [a] -> Int. If we try to get the average of a list of numbers by doing let xs = [1..6] in sum xs / length xs, we get a type error, because you can’t use / with an Int. genericLength, on the other hand, has a type signature of genericLength :: (Num a) => [b] -> a. Because a Num can act like a floating point number, getting the average by doing let xs = [1..6] in sum xs / genericLength xs works out just fine.

                +

                The nub, delete, union, intersect and group functions all have their more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is that the first set of functions use == to test for equality, whereas the By ones also take an equality function and then compare them by using that equality function. group is the same as groupBy (==).

                +

                For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That’s where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

                +
                
                 ghci> let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
                 ghci> groupBy (\x y -> (x > 0) == (y > 0)) values
                 [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
                -
                -

                From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they’re both negative or if they’re both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

                -
                +
                +

                From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they’re both negative or if they’re both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

                +
                
                 on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
                 f `on` g = \x y -> f (g x) (g y)
                -
                -

                So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do:

                -
                +
                +

                So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do:

                +
                
                 ghci> groupBy ((==) `on` (> 0)) values
                 [[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
                -
                +

                Very readable indeed! You can read it out loud: Group this by equality on whether the elements are greater than zero.

                -

                Similarly, the sort, insert, maximum and minimum also have their more general equivalents. Functions like groupBy take a function that determines when two elements are equal. sortBy, insertBy, maximumBy and minimumBy take a function that determine if one element is greater, smaller or equal to the other. The type signature of sortBy is sortBy :: (a -> a -> Ordering) -> [a] -> [a]. If you remember from before, the Ordering type can have a value of LT, EQ or GT. sort is the equivalent of sortBy compare, because compare just takes two elements whose type is in the Ord typeclass and returns their ordering relationship.

                -

                Lists can be compared, but when they are, they are compared lexicographically. What if we have a list of lists and we want to sort it not based on the inner lists’ contents but on their lengths? Well, as you’ve probably guessed, we’ll use the sortBy function.

                -
                +

                Similarly, the sort, insert, maximum and minimum also have their more general equivalents. Functions like groupBy take a function that determines when two elements are equal. sortBy, insertBy, maximumBy and minimumBy take a function that determine if one element is greater, smaller or equal to the other. The type signature of sortBy is sortBy :: (a -> a -> Ordering) -> [a] -> [a]. If you remember from before, the Ordering type can have a value of LT, EQ or GT. sort is the equivalent of sortBy compare, because compare just takes two elements whose type is in the Ord typeclass and returns their ordering relationship.

                +

                Lists can be compared, but when they are, they are compared lexicographically. What if we have a list of lists and we want to sort it not based on the inner lists’ contents but on their lengths? Well, as you’ve probably guessed, we’ll use the sortBy function.

                +
                
                 ghci> let xs = [[5,4,5,4,4],[1,2,3],[3,5,4,3],[],[2],[2,2]]
                 ghci> sortBy (compare `on` length) xs
                 [[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]
                -
                -

                Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

                +
                +

                Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

                Data.Char

                lego char -

                The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

                -

                Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here’s what they are:

                -

                isControl checks whether a character is a control character.

                -

                isSpace checks whether a character is a white-space characters. That includes spaces, tab characters, newlines, etc.

                -

                isLower checks whether a character is lower-cased.

                -

                isUpper checks whether a character is upper-cased.

                -

                isAlpha checks whether a character is a letter.

                -

                isAlphaNum checks whether a character is a letter or a number.

                -

                isPrint checks whether a character is printable. Control characters, for instance, are not printable.

                -

                isDigit checks whether a character is a digit.

                -

                isOctDigit checks whether a character is an octal digit.

                -

                isHexDigit checks whether a character is a hex digit.

                -

                isLetter checks whether a character is a letter.

                -

                isMark checks for Unicode mark characters. Those are characters that combine with preceding letters to form letters with accents. Use this if you are French.

                -

                isNumber checks whether a character is numeric.

                -

                isPunctuation checks whether a character is punctuation.

                -

                isSymbol checks whether a character is a fancy mathematical or currency symbol.

                -

                isSeparator checks for Unicode spaces and separators.

                -

                isAscii checks whether a character falls into the first 128 characters of the Unicode character set.

                -

                isLatin1 checks whether a character falls into the first 256 characters of Unicode.

                -

                isAsciiUpper checks whether a character is ASCII and upper-case.

                -

                isAsciiLower checks whether a character is ASCII and lower-case.

                -

                All these predicates have a type signature of Char -> Bool. Most of the time you’ll use this to filter out strings or something like that. For instance, let’s say we’re making a program that takes a username and the username can only be comprised of alphanumeric characters. We can use the Data.List function all in combination with the Data.Char predicates to determine if the username is alright.

                -
                +

                The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

                +

                Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here’s what they are:

                +

                isControl checks whether a character is a control character.

                +

                isSpace checks whether a character is a white-space characters. That includes spaces, tab characters, newlines, etc.

                +

                isLower checks whether a character is lower-cased.

                +

                isUpper checks whether a character is upper-cased.

                +

                isAlpha checks whether a character is a letter.

                +

                isAlphaNum checks whether a character is a letter or a number.

                +

                isPrint checks whether a character is printable. Control characters, for instance, are not printable.

                +

                isDigit checks whether a character is a digit.

                +

                isOctDigit checks whether a character is an octal digit.

                +

                isHexDigit checks whether a character is a hex digit.

                +

                isLetter checks whether a character is a letter.

                +

                isMark checks for Unicode mark characters. Those are characters that combine with preceding letters to form letters with accents. Use this if you are French.

                +

                isNumber checks whether a character is numeric.

                +

                isPunctuation checks whether a character is punctuation.

                +

                isSymbol checks whether a character is a fancy mathematical or currency symbol.

                +

                isSeparator checks for Unicode spaces and separators.

                +

                isAscii checks whether a character falls into the first 128 characters of the Unicode character set.

                +

                isLatin1 checks whether a character falls into the first 256 characters of Unicode.

                +

                isAsciiUpper checks whether a character is ASCII and upper-case.

                +

                isAsciiLower checks whether a character is ASCII and lower-case.

                +

                All these predicates have a type signature of Char -> Bool. Most of the time you’ll use this to filter out strings or something like that. For instance, let’s say we’re making a program that takes a username and the username can only be comprised of alphanumeric characters. We can use the Data.List function all in combination with the Data.Char predicates to determine if the username is alright.

                +
                
                 ghci> all isAlphaNum "bobby283"
                 True
                 ghci> all isAlphaNum "eddy the fish!"
                 False
                -
                -

                Kewl. In case you don’t remember, all takes a predicate and a list and returns True only if that predicate holds for every element in the list.

                -

                We can also use isSpace to simulate the Data.List function words.

                -
                +
                +

                Kewl. In case you don’t remember, all takes a predicate and a list and returns True only if that predicate holds for every element in the list.

                +

                We can also use isSpace to simulate the Data.List function words.

                +
                
                 ghci> words "hey folks its me"
                 ["hey","folks","its","me"]
                 ghci> groupBy ((==) `on` isSpace) "hey folks its me"
                 ["hey"," ","folks"," ","its"," ","me"]
                 ghci>
                -
                -

                Hmmm, well, it kind of does what words does but we’re left with elements of only spaces. Hmm, whatever shall we do? I know, let’s filter that sucker.

                -
                +
                +

                Hmmm, well, it kind of does what words does but we’re left with elements of only spaces. Hmm, whatever shall we do? I know, let’s filter that sucker.

                +
                
                 ghci> filter (not . any isSpace) . groupBy ((==) `on` isSpace) $ "hey folks its me"
                 ["hey","folks","its","me"]
                -
                +

                Ah.

                -

                The Data.Char also exports a datatype that’s kind of like Ordering. The Ordering type can have a value of LT, EQ or GT. It’s a sort of enumeration. It describes a few possible results that can arise from comparing two elements. The GeneralCategory type is also an enumeration. It presents us with a few possible categories that a character can fall into. The main function for getting the general category of a character is generalCategory. It has a type of generalCategory :: Char -> GeneralCategory. There are about 31 categories so we won’t list them all here, but let’s play around with the function.

                -
                +

                The Data.Char also exports a datatype that’s kind of like Ordering. The Ordering type can have a value of LT, EQ or GT. It’s a sort of enumeration. It describes a few possible results that can arise from comparing two elements. The GeneralCategory type is also an enumeration. It presents us with a few possible categories that a character can fall into. The main function for getting the general category of a character is generalCategory. It has a type of generalCategory :: Char -> GeneralCategory. There are about 31 categories so we won’t list them all here, but let’s play around with the function.

                +
                
                 ghci> generalCategory ' '
                 Space
                 ghci> generalCategory 'A'
                @@ -468,44 +468,44 @@ 

                Data.Char

                DecimalNumber ghci> map generalCategory " \t\nA9?|" [Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol] -
                -

                Since the GeneralCategory type is part of the Eq typeclass, we can also test for stuff like generalCategory c == Space.

                -

                toUpper converts a character to upper-case. Spaces, numbers, and the like remain unchanged.

                -

                toLower converts a character to lower-case.

                -

                toTitle converts a character to title-case. For most characters, title-case is the same as upper-case.

                -

                digitToInt converts a character to an Int. To succeed, the character must be in the ranges '0'..'9', 'a'..'f' or 'A'..'F'.

                -
                +
                +

                Since the GeneralCategory type is part of the Eq typeclass, we can also test for stuff like generalCategory c == Space.

                +

                toUpper converts a character to upper-case. Spaces, numbers, and the like remain unchanged.

                +

                toLower converts a character to lower-case.

                +

                toTitle converts a character to title-case. For most characters, title-case is the same as upper-case.

                +

                digitToInt converts a character to an Int. To succeed, the character must be in the ranges '0'..'9', 'a'..'f' or 'A'..'F'.

                +
                
                 ghci> map digitToInt "34538"
                 [3,4,5,3,8]
                 ghci> map digitToInt "FF85AB"
                 [15,15,8,5,10,11]
                -
                -

                intToDigit is the inverse function of digitToInt. It takes an Int in the range of 0..15 and converts it to a lower-case character.

                -
                +
                +

                intToDigit is the inverse function of digitToInt. It takes an Int in the range of 0..15 and converts it to a lower-case character.

                +
                
                 ghci> intToDigit 15
                 'f'
                 ghci> intToDigit 5
                 '5'
                -
                -

                The ord and chr functions convert characters to their corresponding numbers and vice versa:

                -
                +
                +

                The ord and chr functions convert characters to their corresponding numbers and vice versa:

                +
                
                 ghci> ord 'a'
                 97
                 ghci> chr 97
                 'a'
                 ghci> map ord "abcdefgh"
                 [97,98,99,100,101,102,103,104]
                -
                -

                The difference between the ord values of two characters is equal to how far apart they are in the Unicode table.

                The Caesar cipher is a primitive method of encoding messages by shifting each character in them by a fixed number of positions in the alphabet. We can easily create a sort of Caesar cipher of our own, only we won’t constrict ourselves to the alphabet.

                -
                +
                +

                The difference between the ord values of two characters is equal to how far apart they are in the Unicode table.

                The Caesar cipher is a primitive method of encoding messages by shifting each character in them by a fixed number of positions in the alphabet. We can easily create a sort of Caesar cipher of our own, only we won’t constrict ourselves to the alphabet.

                +
                
                 encode :: Int -> String -> String
                 encode shift msg =
                     let ords = map ord msg
                         shifted = map (+ shift) ords
                     in  map chr shifted
                -
                -

                Here, we first convert the string to a list of numbers. Then we add the shift amount to each number before converting the list of numbers back to characters. If you’re a composition cowboy, you could write the body of this function as map (chr . (+ shift) . ord) msg. Let’s try encoding a few messages.

                -
                +
                +

                Here, we first convert the string to a list of numbers. Then we add the shift amount to each number before converting the list of numbers back to characters. If you’re a composition cowboy, you could write the body of this function as map (chr . (+ shift) . ord) msg. Let’s try encoding a few messages.

                +
                
                 ghci> encode 3 "Heeeeey"
                 "Khhhhh|"
                 ghci> encode 4 "Heeeeey"
                @@ -514,24 +514,24 @@ 

                Data.Char

                "bcde" ghci> encode 5 "Marry Christmas! Ho ho ho!" "Rfww~%Hmwnxyrfx&%Mt%mt%mt&" -
                +

                That’s encoded alright. Decoding a message is basically just shifting it back by the number of places it was shifted by in the first place.

                -
                +
                
                 decode :: Int -> String -> String
                 decode shift msg = encode (negate shift) msg
                -
                -
                +
                +
                
                 ghci> encode 3 "Im a little teapot"
                 "Lp#d#olwwoh#whdsrw"
                 ghci> decode 3 "Lp#d#olwwoh#whdsrw"
                 "Im a little teapot"
                 ghci> decode 5 . encode 5 $ "This is a sentence"
                 "This is a sentence"
                -
                +

                Data.Map

                Association lists (also called dictionaries) are lists that are used to store key-value pairs where ordering doesn’t matter. For instance, we might use an association list to store phone numbers, where phone numbers would be the values and people’s names would be the keys. We don’t care in which order they’re stored, we just want to get the right phone number for the right person.

                The most obvious way to represent association lists in Haskell would be by having a list of pairs. The first component in the pair would be the key, the second component the value. Here’s an example of an association list with phone numbers:

                -
                +
                
                 phoneBook =
                     [("amelia","555-2938")
                     ,("freya","452-2928")
                @@ -540,64 +540,64 @@ 

                Data.Map

                ,("roald","939-8282") ,("tenzing","853-2492") ] -
                +

                Despite this seemingly odd indentation, this is just a list of pairs of strings. The most common task when dealing with association lists is looking up some value by key. Let’s make a function that looks up some value given a key.

                -
                +
                
                 findKey :: (Eq k) => k -> [(k,v)] -> v
                 findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs
                -
                -

                Pretty simple. The function that takes a key and a list, filters the list so that only matching keys remain, gets the first key-value that matches and returns the value. But what happens if the key we’re looking for isn’t in the association list? Hmm. Here, if a key isn’t in the association list, we’ll end up trying to get the head of an empty list, which throws a runtime error. However, we should avoid making our programs so easy to crash, so let’s use the Maybe data type. If we don’t find the key, we’ll return a Nothing. If we find it, we’ll return Just something, where something is the value corresponding to that key.

                -
                +
                +

                Pretty simple. The function that takes a key and a list, filters the list so that only matching keys remain, gets the first key-value that matches and returns the value. But what happens if the key we’re looking for isn’t in the association list? Hmm. Here, if a key isn’t in the association list, we’ll end up trying to get the head of an empty list, which throws a runtime error. However, we should avoid making our programs so easy to crash, so let’s use the Maybe data type. If we don’t find the key, we’ll return a Nothing. If we find it, we’ll return Just something, where something is the value corresponding to that key.

                +
                
                 findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
                 findKey key [] = Nothing
                 findKey key ((k,v):xs) = if key == k
                                             then Just v
                                             else findKey key xs
                -
                +

                Look at the type declaration. It takes a key that can be equated, an association list and then it maybe produces a value. Sounds about right.

                This is a textbook recursive function that operates on a list. Edge case, splitting a list into a head and a tail, recursive calls, they’re all there. This is the classic fold pattern, so let’s see how this would be implemented as a fold.

                -
                +
                
                 findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
                 findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
                -
                -

                Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.

                -
                +
                +

                Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.

                +
                
                 ghci> findKey "tenzing" phoneBook
                 Just "853-2492"
                 ghci> findKey "amelia" phoneBook
                 Just "555-2938"
                 ghci> findKey "christopher" phoneBook
                 Nothing
                -
                +
                legomap -

                Works like a charm! If we have the friend’s phone number, we Just get the number, otherwise we get Nothing.

                -

                We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they’re internally implemented with trees) and also it provides a lot of utility functions. From now on, we’ll say we’re working with maps instead of association lists.

                -

                Because Data.Map exports functions that clash with the Prelude and Data.List ones, we’ll do a qualified import.

                -
                +

                Works like a charm! If we have the friend’s phone number, we Just get the number, otherwise we get Nothing.

                +

                We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they’re internally implemented with trees) and also it provides a lot of utility functions. From now on, we’ll say we’re working with maps instead of association lists.

                +

                Because Data.Map exports functions that clash with the Prelude and Data.List ones, we’ll do a qualified import.

                +
                
                 import qualified Data.Map as Map
                -
                +

                Put this import statement into a script and then load the script via GHCI.

                -

                Let’s go ahead and see what Data.Map has in store for us! Here’s the basic rundown of its functions.

                -

                The fromList function takes an association list (in the form of a list) and returns a map with the same associations.

                -
                +

                Let’s go ahead and see what Data.Map has in store for us! Here’s the basic rundown of its functions.

                +

                The fromList function takes an association list (in the form of a list) and returns a map with the same associations.

                +
                
                 ghci> Map.fromList [("amelia","555-2938"),("freya","452-2928"),("neil","205-2928")]
                 fromList [("amelia","555-2938"),("freya","452-2928"),("neil","205-2928")]
                 ghci> Map.fromList [(1,2),(3,4),(3,2),(5,5)]
                 fromList [(1,2),(3,2),(5,5)]
                -
                -

                If there are duplicate keys in the original association list, the duplicates are just discarded. This is the type signature of fromList

                -
                +
                +

                If there are duplicate keys in the original association list, the duplicates are just discarded. This is the type signature of fromList

                +
                
                 Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v
                -
                -

                It says that it takes a list of pairs of type k and v and returns a map that maps from keys of type k to type v. Notice that when we were doing association lists with normal lists, the keys only had to be equatable (their type belonging to the Eq typeclass) but now they have to be orderable. That’s an essential constraint in the Data.Map module. It needs the keys to be orderable so it can arrange them in a tree.

                -

                You should always use Data.Map for key-value associations unless you have keys that aren’t part of the Ord typeclass.

                -

                empty represents an empty map. It takes no arguments, it just returns an empty map.

                -
                +
                +

                It says that it takes a list of pairs of type k and v and returns a map that maps from keys of type k to type v. Notice that when we were doing association lists with normal lists, the keys only had to be equatable (their type belonging to the Eq typeclass) but now they have to be orderable. That’s an essential constraint in the Data.Map module. It needs the keys to be orderable so it can arrange them in a tree.

                +

                You should always use Data.Map for key-value associations unless you have keys that aren’t part of the Ord typeclass.

                +

                empty represents an empty map. It takes no arguments, it just returns an empty map.

                +
                
                 ghci> Map.empty
                 fromList []
                -
                -

                insert takes a key, a value and a map and returns a new map that’s just like the old one, only with the key and value inserted.

                -
                +
                +

                insert takes a key, a value and a map and returns a new map that’s just like the old one, only with the key and value inserted.

                +
                
                 ghci> Map.empty
                 fromList []
                 ghci> Map.insert 3 100 Map.empty
                @@ -606,57 +606,57 @@ 

                Data.Map

                fromList [(3,100),(4,200),(5,600)] ghci> Map.insert 5 600 . Map.insert 4 200 . Map.insert 3 100 $ Map.empty fromList [(3,100),(4,200),(5,600)] -
                -

                We can implement our own fromList by using the empty map, insert and a fold. Watch:

                -
                +
                +

                We can implement our own fromList by using the empty map, insert and a fold. Watch:

                +
                
                 fromList' :: (Ord k) => [(k,v)] -> Map.Map k v
                 fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
                -
                +

                It’s a pretty straightforward fold. We start of with an empty map and we fold it up from the right, inserting the key value pairs into the accumulator as we go along.

                -

                null checks if a map is empty.

                -
                +

                null checks if a map is empty.

                +
                
                 ghci> Map.null Map.empty
                 True
                 ghci> Map.null $ Map.fromList [(2,3),(5,5)]
                 False
                -
                -

                size reports the size of a map.

                -
                +
                +

                size reports the size of a map.

                +
                
                 ghci> Map.size Map.empty
                 0
                 ghci> Map.size $ Map.fromList [(2,4),(3,3),(4,2),(5,4),(6,4)]
                 5
                -
                -

                singleton takes a key and a value and creates a map that has exactly one mapping.

                -
                +
                +

                singleton takes a key and a value and creates a map that has exactly one mapping.

                +
                
                 ghci> Map.singleton 3 9
                 fromList [(3,9)]
                 ghci> Map.insert 5 9 $ Map.singleton 3 9
                 fromList [(3,9),(5,9)]
                -
                -

                lookup works like the Data.List lookup, only it operates on maps. It returns Just something if it finds something for the key and Nothing if it doesn’t.

                -

                member is a predicate that takes a key and a map and reports whether the key is in the map or not.

                -
                +
                +

                lookup works like the Data.List lookup, only it operates on maps. It returns Just something if it finds something for the key and Nothing if it doesn’t.

                +

                member is a predicate that takes a key and a map and reports whether the key is in the map or not.

                +
                
                 ghci> Map.member 3 $ Map.fromList [(3,6),(4,3),(6,9)]
                 True
                 ghci> Map.member 3 $ Map.fromList [(2,5),(4,5)]
                 False
                -
                -

                map and filter work much like their list equivalents.

                -
                +
                +

                map and filter work much like their list equivalents.

                +
                
                 ghci> Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]
                 fromList [(1,100),(2,400),(3,900)]
                 ghci> Map.filter isUpper $ Map.fromList [(1,'a'),(2,'A'),(3,'b'),(4,'B')]
                 fromList [(2,'A'),(4,'B')]
                -
                -

                toList is the inverse of fromList.

                -
                +
                +

                toList is the inverse of fromList.

                +
                
                 ghci> Map.toList . Map.insert 9 2 $ Map.singleton 4 3
                 [(4,3),(9,2)]
                -
                -

                keys and elems return lists of keys and values respectively. keys is the equivalent of map fst . Map.toList and elems is the equivalent of map snd . Map.toList.

                -

                fromListWith is a cool little function. It acts like fromList, only it doesn’t discard duplicate keys but it uses a function supplied to it to decide what to do with them. Let’s say that a friend can have several numbers and we have an association list set up like this.

                -
                +
                +

                keys and elems return lists of keys and values respectively. keys is the equivalent of map fst . Map.toList and elems is the equivalent of map snd . Map.toList.

                +

                fromListWith is a cool little function. It acts like fromList, only it doesn’t discard duplicate keys but it uses a function supplied to it to decide what to do with them. Let’s say that a friend can have several numbers and we have an association list set up like this.

                +
                
                 phoneBook =
                     [("amelia","555-2938")
                     ,("amelia","342-2492")
                @@ -669,88 +669,88 @@ 

                Data.Map

                ,("tenzing","853-2492") ,("tenzing","555-2111") ] -
                -

                Now if we just use fromList to put that into a map, we’ll lose a few numbers! So here’s what we’ll do:

                -
                +
                +

                Now if we just use fromList to put that into a map, we’ll lose a few numbers! So here’s what we’ll do:

                +
                
                 phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String
                 phoneBookToMap xs = Map.fromListWith (\number1 number2 -> number1 ++ ", " ++ number2) xs
                -
                -
                +
                +
                
                 ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook
                 "827-9162, 943-2929, 493-2928"
                 ghci> Map.lookup "roald" $ phoneBookToMap phoneBook
                 "939-8282"
                 ghci> Map.lookup "amelia" $ phoneBookToMap phoneBook
                 "342-2492, 555-2938"
                -
                -

                If a duplicate key is found, the function we pass is used to combine the values of those keys into some other value. We could also first make all the values in the association list singleton lists and then we can use ++ to combine the numbers.

                -
                +
                +

                If a duplicate key is found, the function we pass is used to combine the values of those keys into some other value. We could also first make all the values in the association list singleton lists and then we can use ++ to combine the numbers.

                +
                
                 phoneBookToMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
                 phoneBookToMap xs = Map.fromListWith (++) $ map (\(k,v) -> (k,[v])) xs
                -
                -
                +
                +
                
                 ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook
                 ["827-9162","943-2929","493-2928"]
                -
                +

                Pretty neat! Another use case is if we’re making a map from an association list of numbers and when a duplicate key is found, we want the biggest value for the key to be kept.

                -
                +
                
                 ghci> Map.fromListWith max [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
                 fromList [(2,100),(3,29),(4,22)]
                -
                +

                Or we could choose to add together values on the same keys.

                -
                +
                
                 ghci> Map.fromListWith (+) [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
                 fromList [(2,108),(3,62),(4,37)]
                -
                -

                insertWith is to insert what fromListWith is to fromList. It inserts a key-value pair into a map, but if that map already contains the key, it uses the function passed to it to determine what to do.

                -
                +
                +

                insertWith is to insert what fromListWith is to fromList. It inserts a key-value pair into a map, but if that map already contains the key, it uses the function passed to it to determine what to do.

                +
                
                 ghci> Map.insertWith (+) 3 100 $ Map.fromList [(3,4),(5,103),(6,339)]
                 fromList [(3,104),(5,103),(6,339)]
                -
                -

                These were just a few functions from Data.Map. You can see a complete list of functions in the documentation.

                +
                +

                These were just a few functions from Data.Map. You can see a complete list of functions in the documentation.

                Data.Set

                legosets -

                The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

                -

                Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

                +

                The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

                +

                Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

                Put this import statement in a script:

                -
                +
                
                 import qualified Data.Set as Set
                -
                +

                And then load the script via GHCI.

                Let’s say we have two pieces of text. We want to find out which characters were used in both of them.

                -
                +
                
                 text1 = "I just had an anime dream. Anime... Reality... Are they so different?"
                 text2 = "The old man left his garbage can out and now his trash is all over my lawn!"
                -
                +

                -The fromList function works much like you would expect. It takes a list and converts it into a set.

                -
                +The fromList function works much like you would expect. It takes a list and converts it into a set.

                +
                
                 ghci> let set1 = Set.fromList text1
                 ghci> let set2 = Set.fromList text2
                 ghci> set1
                 fromList " .?AIRadefhijlmnorstuy"
                 ghci> set2
                 fromList " !Tabcdefghilmnorstuvwy"
                -
                -

                As you can see, the items are ordered and each element is unique. Now let’s use the intersection function to see which elements they both share.

                -
                +
                +

                As you can see, the items are ordered and each element is unique. Now let’s use the intersection function to see which elements they both share.

                +
                
                 ghci> Set.intersection set1 set2
                 fromList " adefhilmnorstuy"
                -
                -

                We can use the difference function to see which letters are in the first set but aren’t in the second one and vice versa.

                -
                +
                +

                We can use the difference function to see which letters are in the first set but aren’t in the second one and vice versa.

                +
                
                 ghci> Set.difference set1 set2
                 fromList ".?AIRj"
                 ghci> Set.difference set2 set1
                 fromList "!Tbcgvw"
                -
                -

                Or we can see all the unique letters used in both sentences by using union.

                -
                +
                +

                Or we can see all the unique letters used in both sentences by using union.

                +
                
                 ghci> Set.union set1 set2
                 fromList " !.?AIRTabcdefghijlmnorstuvwy"
                -
                -

                The null, size, member, empty, singleton, insert and delete functions all work like you’d expect them to.

                -
                +
                +

                The null, size, member, empty, singleton, insert and delete functions all work like you’d expect them to.

                +
                
                 ghci> Set.null Set.empty
                 True
                 ghci> Set.null $ Set.fromList [3,4,5,5,4,3]
                @@ -765,9 +765,9 @@ 

                Data.Set

                fromList [5,6,7,8,9,10] ghci> Set.delete 4 $ Set.fromList [3,4,5,4,3,4,5] fromList [3,5] -
                +

                We can also check for subsets or proper subset. Set A is a subset of set B if B contains all the elements that A does. Set A is a proper subset of set B if B contains all the elements that A does but has more elements.

                -
                +
                
                 ghci> Set.fromList [2,3,4] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]
                 True
                 ghci> Set.fromList [1,2,3,4,5] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]
                @@ -776,30 +776,30 @@ 

                Data.Set

                False ghci> Set.fromList [2,3,4,8] `Set.isSubsetOf` Set.fromList [1,2,3,4,5] False -
                -

                We can also map over sets and filter them.

                -
                +
                +

                We can also map over sets and filter them.

                +
                
                 ghci> Set.filter odd $ Set.fromList [3,4,5,6,7,2,3,4]
                 fromList [3,5,7]
                 ghci> Set.map (+1) $ Set.fromList [3,4,5,6,7,2,3,4]
                 fromList [3,4,5,6,7,8]
                -
                -

                Sets are often used to weed a list of duplicates from a list by first making it into a set with fromList and then converting it back to a list with toList. The Data.List function nub already does that, but weeding out duplicates for large lists is much faster if you cram them into a set and then convert them back to a list than using nub. But using nub only requires the type of the list’s elements to be part of the Eq typeclass, whereas if you want to cram elements into a set, the type of the list has to be in Ord.

                -
                +
                +

                Sets are often used to weed a list of duplicates from a list by first making it into a set with fromList and then converting it back to a list with toList. The Data.List function nub already does that, but weeding out duplicates for large lists is much faster if you cram them into a set and then convert them back to a list than using nub. But using nub only requires the type of the list’s elements to be part of the Eq typeclass, whereas if you want to cram elements into a set, the type of the list has to be in Ord.

                +
                
                 ghci> let setNub xs = Set.toList $ Set.fromList xs
                 ghci> setNub "HEY WHATS CRACKALACKIN"
                 " ACEHIKLNRSTWY"
                 ghci> nub "HEY WHATS CRACKALACKIN"
                 "HEY WATSCRKLIN"
                -
                -

                setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

                +
                +

                setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

                Making our own modules

                making modules

                We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it’s good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

                -

                Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

                +

                Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

                We say that a module exports functions. What that means is that when I import a module, I can use the functions that it exports. It can define functions that its functions call internally, but we can only see and use the ones that it exports.

                -

                At the beginning of a module, we specify the module name. If we have a file called Geometry.hs, then we should name our module Geometry. Then, we specify the functions that it exports and after that, we can start writing the functions. So we’ll start with this.

                -
                +

                At the beginning of a module, we specify the module name. If we have a file called Geometry.hs, then we should name our module Geometry. Then, we specify the functions that it exports and after that, we can start writing the functions. So we’ll start with this.

                +
                
                 module Geometry
                 ( sphereVolume
                 , sphereArea
                @@ -808,9 +808,9 @@ 

                Making our own modules

                , cuboidArea , cuboidVolume ) where -
                +

                As you can see, we’ll be doing areas and volumes for spheres, cubes and cuboids. Let’s go ahead and define our functions then:

                -
                +
                
                 module Geometry
                 ( sphereVolume
                 , sphereArea
                @@ -840,18 +840,18 @@ 

                Making our own modules

                rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b -
                -

                Pretty standard geometry right here. There are a few things to take note of though. Because a cube is only a special case of a cuboid, we defined its area and volume by treating it as a cuboid whose sides are all of the same length. We also defined a helper function called rectangleArea, which calculates a rectangle’s area based on the lenghts of its sides. It’s rather trivial because it’s just multiplication. Notice that we used it in our functions in the module (namely cuboidArea and cuboidVolume) but we didn’t export it! Because we want our module to just present functions for dealing with three-dimensional objects, we used rectangleArea but we didn’t export it.

                -

                When making a module, we usually export only those functions that act as a sort of interface to our module so that the implementation is hidden. If someone is using our Geometry module, they don’t have to concern themselves with functions that we don’t export. We can decide to change those functions completely or delete them in a newer version (we could delete rectangleArea and just use * instead) and no one will mind because we weren’t exporting them in the first place.

                +
                +

                Pretty standard geometry right here. There are a few things to take note of though. Because a cube is only a special case of a cuboid, we defined its area and volume by treating it as a cuboid whose sides are all of the same length. We also defined a helper function called rectangleArea, which calculates a rectangle’s area based on the lenghts of its sides. It’s rather trivial because it’s just multiplication. Notice that we used it in our functions in the module (namely cuboidArea and cuboidVolume) but we didn’t export it! Because we want our module to just present functions for dealing with three-dimensional objects, we used rectangleArea but we didn’t export it.

                +

                When making a module, we usually export only those functions that act as a sort of interface to our module so that the implementation is hidden. If someone is using our Geometry module, they don’t have to concern themselves with functions that we don’t export. We can decide to change those functions completely or delete them in a newer version (we could delete rectangleArea and just use * instead) and no one will mind because we weren’t exporting them in the first place.

                To use our module, we just do:

                -
                +
                
                 import Geometry
                -
                -

                Geometry.hs has to be in the same folder that the program that’s importing it is in, though.

                -

                Modules can also have a hierarchical structure. Each module can have a number of submodules and they can have submodules of their own. Let’s section these functions off so that Geometry is a module that has three submodules, one for each type of object.

                -

                First, we’ll make a folder called Geometry. Mind the capital G. In it, we’ll place three files: Sphere.hs, Cuboid.hs, and Cube.hs. Here’s what the files will contain:

                -

                Sphere.hs

                -
                +
                +

                Geometry.hs has to be in the same folder that the program that’s importing it is in, though.

                +

                Modules can also have a hierarchical structure. Each module can have a number of submodules and they can have submodules of their own. Let’s section these functions off so that Geometry is a module that has three submodules, one for each type of object.

                +

                First, we’ll make a folder called Geometry. Mind the capital G. In it, we’ll place three files: Sphere.hs, Cuboid.hs, and Cube.hs. Here’s what the files will contain:

                +

                Sphere.hs

                +
                
                 module Geometry.Sphere
                 ( volume
                 , area
                @@ -862,9 +862,9 @@ 

                Making our own modules

                area :: Float -> Float area radius = 4 * pi * (radius ^ 2) -
                -

                Cuboid.hs

                -
                +
                +

                Cuboid.hs

                +
                
                 module Geometry.Cuboid
                 ( volume
                 , area
                @@ -878,9 +878,9 @@ 

                Making our own modules

                rectangleArea :: Float -> Float -> Float rectangleArea a b = a * b -
                -

                Cube.hs

                -
                +
                +

                Cube.hs

                +
                
                 module Geometry.Cube
                 ( volume
                 , area
                @@ -893,19 +893,19 @@ 

                Making our own modules

                area :: Float -> Float area side = Cuboid.area side side side -
                -

                Alright! So first is Geometry.Sphere. Notice how we placed it in a folder called Geometry and then defined the module name as Geometry.Sphere. We did the same for the cuboid. Also notice how in all three submodules, we defined functions with the same names. We can do this because they’re separate modules. We want to use functions from Geometry.Cuboid in Geometry.Cube but we can’t just straight up do import Geometry.Cuboid because it exports functions with the same names as Geometry.Cube. That’s why we do a qualified import and all is well.

                -

                So now if we’re in a file that’s on the same level as the Geometry folder, we can do, say:

                -
                +
                +

                Alright! So first is Geometry.Sphere. Notice how we placed it in a folder called Geometry and then defined the module name as Geometry.Sphere. We did the same for the cuboid. Also notice how in all three submodules, we defined functions with the same names. We can do this because they’re separate modules. We want to use functions from Geometry.Cuboid in Geometry.Cube but we can’t just straight up do import Geometry.Cuboid because it exports functions with the same names as Geometry.Cube. That’s why we do a qualified import and all is well.

                +

                So now if we’re in a file that’s on the same level as the Geometry folder, we can do, say:

                +
                
                 import Geometry.Sphere
                -
                -

                And then we can call area and volume and they’ll give us the area and volume for a sphere. And if we want to juggle two or more of these modules, we have to do qualified imports because they export functions with the same names. So we just do something like:

                -
                +
                +

                And then we can call area and volume and they’ll give us the area and volume for a sphere. And if we want to juggle two or more of these modules, we have to do qualified imports because they export functions with the same names. So we just do something like:

                +
                
                 import qualified Geometry.Sphere as Sphere
                 import qualified Geometry.Cuboid as Cuboid
                 import qualified Geometry.Cube as Cube
                -
                -

                And then we can call Sphere.area, Sphere.volume, Cuboid.area, etc. and each will calculate the area or volume for their corresponding object.

                +
                +

                And then we can call Sphere.area, Sphere.volume, Cuboid.area, etc. and each will calculate the area or volume for their corresponding object.

                The next time you find yourself writing a file that’s really big and has a lot of functions, try to see which functions serve some common purpose and then see if you can put them in their own module. You’ll be able to just import your module the next time you’re writing a program that requires some of the same functionality.

                  diff --git a/docs/recursion.html b/docs/recursion.html index 873e2e0..0882101 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -38,9 +38,9 @@

                  Hello recursion!

                  If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

                  Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

                  Maximum awesome

                  -

                  The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

                  +

                  The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

                  Now let’s see how we’d define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it’s the maximum of the tail. That’s it! Now let’s implement that in Haskell.

                  -
                  +
                  
                   maximum' :: (Ord a) => [a] -> a
                   maximum' [] = error "maximum of empty list"
                   maximum' [x] = x
                  @@ -48,91 +48,91 @@ 

                  Maximum awesome

                  | x > maxTail = x | otherwise = maxTail where maxTail = maximum' xs -
                  +

                  As you can see, pattern matching goes great with recursion! Most imperative languages don’t have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what’s the maximum of an empty list? I don’t know. The second pattern also lays out an edge condition. It says that if it’s the singleton list, just give back the only element.

                  -

                  Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

                  -

                  Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

                  -

                  An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here’s how we could rewrite maximum' by using max:

                  -
                  +

                  Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

                  +

                  Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

                  +

                  An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here’s how we could rewrite maximum' by using max:

                  +
                  
                   maximum' :: (Ord a) => [a] -> a
                   maximum' [] = error "maximum of empty list"
                   maximum' [x] = x
                   maximum' (x:xs) = max x (maximum' xs)
                  -
                  +

                  How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

                  max

                  A few more recursive functions

                  -

                  Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

                  -
                  +

                  Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

                  +
                  
                   replicate' :: (Num i, Ord i) => i -> a -> [a]
                   replicate' n x
                       | n <= 0    = []
                       | otherwise = x:replicate' (n-1) x
                  -
                  -

                  We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

                  -

                  Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.

                  -

                  Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

                  -
                  +
                  +

                  We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

                  +

                  Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.

                  +

                  Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

                  +
                  
                   take' :: (Num i, Ord i) => i -> [a] -> [a]
                   take' n _
                       | n <= 0   = []
                   take' _ []     = []
                   take' n (x:xs) = x : take' (n-1) xs
                  -
                  +
                  painter -

                  The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

                  -

                  reverse simply reverses a list. Think about the edge condition. What is it? Come on … it’s the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

                  -
                  +

                  The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

                  +

                  reverse simply reverses a list. Think about the edge condition. What is it? Come on … it’s the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

                  +
                  
                   reverse' :: [a] -> [a]
                   reverse' [] = []
                   reverse' (x:xs) = reverse' xs ++ [x]
                  -
                  +

                  There we go!

                  -

                  Because Haskell supports infinite lists, our recursion doesn’t really have to have an edge condition. But if it doesn’t have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. repeat takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.

                  -
                  +

                  Because Haskell supports infinite lists, our recursion doesn’t really have to have an edge condition. But if it doesn’t have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. repeat takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.

                  +
                  
                   repeat' :: a -> [a]
                   repeat' x = x:repeat' x
                  -
                  -

                  Calling repeat 3 will give us a list that starts with 3 and then has an infinite amount of 3’s as a tail. So calling repeat 3 would evaluate like 3:repeat 3, which is 3:(3:repeat 3), which is 3:(3:(3:repeat 3)), etc. repeat 3 will never finish evaluating, whereas take 5 (repeat 3) will give us a list of five 3’s. So essentially it’s like doing replicate 5 3.

                  -

                  zip takes two lists and zips them together. zip [1,2,3] [2,3] returns [(1,2),(2,3)], because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there’s our edge condition. However, zip takes two lists as parameters, so there are actually two edge conditions.

                  -
                  +
                  +

                  Calling repeat 3 will give us a list that starts with 3 and then has an infinite amount of 3’s as a tail. So calling repeat 3 would evaluate like 3:repeat 3, which is 3:(3:repeat 3), which is 3:(3:(3:repeat 3)), etc. repeat 3 will never finish evaluating, whereas take 5 (repeat 3) will give us a list of five 3’s. So essentially it’s like doing replicate 5 3.

                  +

                  zip takes two lists and zips them together. zip [1,2,3] [2,3] returns [(1,2),(2,3)], because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there’s our edge condition. However, zip takes two lists as parameters, so there are actually two edge conditions.

                  +
                  
                   zip' :: [a] -> [b] -> [(a,b)]
                   zip' _ [] = []
                   zip' [] _ = []
                   zip' (x:xs) (y:ys) = (x,y):zip' xs ys
                  -
                  -

                  First two patterns say that if the first list or second list is empty, we get an empty list. The third one says that two lists zipped are equal to pairing up their heads and then tacking on the zipped tails. Zipping [1,2,3] and ['a','b'] will eventually try to zip [3] with []. The edge condition patterns kick in and so the result is (1,'a'):(2,'b'):[], which is exactly the same as [(1,'a'),(2,'b')].

                  -

                  Let’s implement one more standard library function — elem. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn’t have the droids we’re looking for.

                  -
                  +
                  +

                  First two patterns say that if the first list or second list is empty, we get an empty list. The third one says that two lists zipped are equal to pairing up their heads and then tacking on the zipped tails. Zipping [1,2,3] and ['a','b'] will eventually try to zip [3] with []. The edge condition patterns kick in and so the result is (1,'a'):(2,'b'):[], which is exactly the same as [(1,'a'),(2,'b')].

                  +

                  Let’s implement one more standard library function — elem. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn’t have the droids we’re looking for.

                  +
                  
                   elem' :: (Eq a) => a -> [a] -> Bool
                   elem' a [] = False
                   elem' a (x:xs)
                       | a == x    = True
                       | otherwise = a `elem'` xs
                  -
                  -

                  Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

                  +
                  +

                  Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

                  Quick, sort!

                  -

                  We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

                  +

                  We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

                  quickman -

                  So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

                  -
                  +

                  So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

                  +
                  
                   quicksort :: (Ord a) => [a] -> [a]
                   quicksort [] = []
                   quicksort (x:xs) =
                       let smallerSorted = quicksort [a | a <- xs, a <= x]
                           biggerSorted = quicksort [a | a <- xs, a > x]
                       in  smallerSorted ++ [x] ++ biggerSorted
                  -
                  +

                  Let’s give it a small test run to see if it appears to behave correctly.

                  -
                  +
                  
                   ghci> quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]
                   [1,2,2,3,3,4,4,5,6,7,8,9,10]
                   ghci> quicksort "the quick brown fox jumps over the lazy dog"
                   "        abcdeeefghhijklmnoooopqrrsttuuvwxyz"
                  -
                  -

                  Booyah! That’s what I’m talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

                  +
                  +

                  Booyah! That’s what I’m talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

                  quicksort -

                  An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They’re in green here. We chose the head because it’s easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

                  +

                  An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They’re in green here. We chose the head because it’s easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

                  Thinking recursively

                  We did quite a bit of recursion so far and as you’ve probably noticed, there’s a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn’t matter if it’s a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera …

                  brain diff --git a/docs/sh/Scripts/shCore.js b/docs/sh/Scripts/shCore.js index e9ec53a..58caeff 100644 --- a/docs/sh/Scripts/shCore.js +++ b/docs/sh/Scripts/shCore.js @@ -142,7 +142,7 @@ if((matches=regex.exec(list[i]))!=null) return matches[1];return defaultValue;} function FindTagsByName(list,name,tagName) {var tags=document.getElementsByTagName(tagName);for(var i=0;iStarting Out

      Ready, set, go!

      egg -Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we’re going to load functions. The first thing we’re going to do is run ghc’s interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this. +Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we’re going to load functions. The first thing we’re going to do is run ghc’s interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this.

      -
      +
      
       GHCi, version 9.2.4: https://www.haskell.org/ghc/  :? for help
       ghci>
      -
      +

      Congratulations, you’re in GHCI!

      Here’s some simple arithmetic.

      -
      +
      
       ghci> 2 + 15
       17
       ghci> 49 * 100
      @@ -56,24 +56,24 @@ 

      Ready, set, go!

      420 ghci> 5 / 2 2.5 -ghci>
      +ghci>

      This is pretty self-explanatory. We can also use several operators on one line and all the usual precedence rules are obeyed. We can use parentheses to make the precedence explicit or to change it.

      -
      +
      
       ghci> (50 * 100) - 4999
       1
       ghci> 50 * 100 - 4999
       1
       ghci> 50 * (100 - 4999)
      --244950
      +-244950

      -Pretty cool, huh? Yeah, I know it’s not but bear with me. A little pitfall to watch out for here is negating numbers. If we want to have a negative number, it’s always best to surround it with parentheses. Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine. +Pretty cool, huh? Yeah, I know it’s not but bear with me. A little pitfall to watch out for here is negating numbers. If we want to have a negative number, it’s always best to surround it with parentheses. Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine.

      -Boolean algebra is also pretty straightforward. As you probably know, && means a boolean and, || means a boolean or. not negates a True or a False. +Boolean algebra is also pretty straightforward. As you probably know, && means a boolean and, || means a boolean or. not negates a True or a False.

      -
      +
      
       ghci> True && False
       False
       ghci> True && True
      @@ -83,11 +83,11 @@ 

      Ready, set, go!

      ghci> not False True ghci> not (True && True) -False
      +False

      Testing for equality is done like so.

      -
      +
      
       ghci> 5 == 5
       True
       ghci> 1 == 0
      @@ -97,113 +97,113 @@ 

      Ready, set, go!

      ghci> 5 /= 4 True ghci> "hello" == "hello" -True
      +True

      -What about doing 5 + "llama" or 5 == True? Well, if we try the first snippet, we get a big scary error message! +What about doing 5 + "llama" or 5 == True? Well, if we try the first snippet, we get a big scary error message!

      -
      +
      
           • No instance for (Num String) arising from a use of ‘+’
           • In the expression: 5 + "llama"
             In an equation for ‘it’: it = 5 + "llama"
      -
      +

      -Yikes! What GHCI is telling us here is that "llama" is not a number and so it doesn’t know how to add it to 5. Even if it wasn’t "llama" but "four" or "4", Haskell still wouldn’t consider it to be a number. + expects its left and right side to be numbers. If we tried to do True == 5, GHCI would tell us that the types don’t match. Whereas + works only on things that are considered numbers, == works on any two things that can be compared. But the catch is that they both have to be the same type of thing. You can’t compare apples and oranges. We’ll take a closer look at types a bit later. Note: you can do 5 + 4.0 because 5 is sneaky and can act like an integer or a floating-point number. 4.0 can’t act like an integer, so 5 is the one that has to adapt. +Yikes! What GHCI is telling us here is that "llama" is not a number and so it doesn’t know how to add it to 5. Even if it wasn’t "llama" but "four" or "4", Haskell still wouldn’t consider it to be a number. + expects its left and right side to be numbers. If we tried to do True == 5, GHCI would tell us that the types don’t match. Whereas + works only on things that are considered numbers, == works on any two things that can be compared. But the catch is that they both have to be the same type of thing. You can’t compare apples and oranges. We’ll take a closer look at types a bit later. Note: you can do 5 + 4.0 because 5 is sneaky and can act like an integer or a floating-point number. 4.0 can’t act like an integer, so 5 is the one that has to adapt.

      -You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them. +You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them.

      phoen Functions are usually prefix, so from now on we won’t explicitly state that a function is of the prefix form, we’ll just assume it. In most imperative languages, functions are called by writing the function name and then writing its parameters in parentheses, usually separated by commas. In Haskell, functions are called by writing the function name, a space and then the parameters, separated by spaces. For a start, we’ll try calling one of the most boring functions in Haskell.

      -
      +
      
       ghci> succ 8
      -9 
      +9

      -The succ function takes anything that has a defined successor and returns that successor. As you can see, we just separate the function name from the parameter with a space. Calling a function with several parameters is also simple. The functions min and max take two things that can be put in an order (like integers!). min returns the one that’s lesser and max returns the one that’s greater. See for yourself: +The succ function takes anything that has a defined successor and returns that successor. As you can see, we just separate the function name from the parameter with a space. Calling a function with several parameters is also simple. The functions min and max take two things that can be put in an order (like integers!). min returns the one that’s lesser and max returns the one that’s greater. See for yourself:

      -
      +
      
       ghci> min 9 10
       9
       ghci> max 100 101
      -101 
      +101

      Function application (calling a function by putting a space after it and then typing out the parameters) has the highest precedence of them all. What that means for us is that these two statements are equivalent.

      -
      +
      
       ghci> succ 9 + max 5 4 + 1
       16
       ghci> (succ 9) + (max 5 4) + 1
       16
      -
      +

      -However, if we wanted to get the successor of the product of numbers 9 and 10, we couldn’t write succ 9 * 10 because that would get the successor of 9, which would then be multiplied by 10. So 100. We’d have to write succ (9 * 10) to get 91. +However, if we wanted to get the successor of the product of numbers 9 and 10, we couldn’t write succ 9 * 10 because that would get the successor of 9, which would then be multiplied by 10. So 100. We’d have to write succ (9 * 10) to get 91.

      -

      If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks. For instance, the div function takes two integers and does integral division between them. Doing div 92 10 results in a 9. But when we call it like that, there may be some confusion as to which number is doing the division and which one is being divided. So we can call it as an infix function by doing 92 `div` 10 and suddenly it’s much clearer.

      -

      Lots of people who come from imperative languages tend to stick to the notion that parentheses should denote function application. For example, in C, you use parentheses to call functions like foo(), bar(1) or baz(3, "haha"). Like we said, spaces are used for function application in Haskell. So those functions in Haskell would be foo, bar 1 and baz 3 "haha". So if you see something like bar (bar 3), it doesn’t mean that bar is called with bar and 3 as parameters. It means that we first call the function bar with 3 as the parameter to get some number and then we call bar again with that number. In C, that would be something like bar(bar(3)).

      +

      If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks. For instance, the div function takes two integers and does integral division between them. Doing div 92 10 results in a 9. But when we call it like that, there may be some confusion as to which number is doing the division and which one is being divided. So we can call it as an infix function by doing 92 `div` 10 and suddenly it’s much clearer.

      +

      Lots of people who come from imperative languages tend to stick to the notion that parentheses should denote function application. For example, in C, you use parentheses to call functions like foo(), bar(1) or baz(3, "haha"). Like we said, spaces are used for function application in Haskell. So those functions in Haskell would be foo, bar 1 and baz 3 "haha". So if you see something like bar (bar 3), it doesn’t mean that bar is called with bar and 3 as parameters. It means that we first call the function bar with 3 as the parameter to get some number and then we call bar again with that number. In C, that would be something like bar(bar(3)).

      Baby’s first functions

      In the previous section we got a basic feel for calling functions. Now let’s try making our own! Open up your favorite text editor and punch in this function that takes a number and multiplies it by two.

      -
      -doubleMe x = x + x
      +
      
      +doubleMe x = x + x

      -Functions are defined in a similar way that they are called. The function name is followed by parameters separated by spaces. But when defining functions, there’s a = and after that we define what the function does. Save this as baby.hs or something. Now navigate to where it’s saved and run ghci from there. Once inside GHCI, do :l baby. Now that our script is loaded, we can play with the function that we defined. +Functions are defined in a similar way that they are called. The function name is followed by parameters separated by spaces. But when defining functions, there’s a = and after that we define what the function does. Save this as baby.hs or something. Now navigate to where it’s saved and run ghci from there. Once inside GHCI, do :l baby. Now that our script is loaded, we can play with the function that we defined.

      -
      +
      
       ghci> :l baby
       [1 of 1] Compiling Main             ( baby.hs, interpreted )
       Ok, one module loaded.
       ghci> doubleMe 9
       18
       ghci> doubleMe 8.3
      -16.6 
      +16.6

      -Because + works on integers as well as on floating-point numbers (anything that can be considered a number, really), our function also works on any number. Let’s make a function that takes two numbers and multiplies each by two and then adds them together. +Because + works on integers as well as on floating-point numbers (anything that can be considered a number, really), our function also works on any number. Let’s make a function that takes two numbers and multiplies each by two and then adds them together.

      -
      -doubleUs x y = x*2 + y*2 
      +
      
      +doubleUs x y = x*2 + y*2 

      -Simple. We could have also defined it as doubleUs x y = x + x + y + y. Testing it out produces pretty predictable results (remember to append this function to the baby.hs file, save it and then do :l baby inside GHCI). +Simple. We could have also defined it as doubleUs x y = x + x + y + y. Testing it out produces pretty predictable results (remember to append this function to the baby.hs file, save it and then do :l baby inside GHCI).

      -
      +
      
       ghci> doubleUs 4 9
       26
       ghci> doubleUs 2.3 34.2
       73.0
       ghci> doubleUs 28 88 + doubleMe 123
       478
      -
      +

      -As expected, you can call your own functions from other functions that you made. With that in mind, we could redefine doubleUs like this: +As expected, you can call your own functions from other functions that you made. With that in mind, we could redefine doubleUs like this:

      -
      -doubleUs x y = doubleMe x + doubleMe y 
      +
      
      +doubleUs x y = doubleMe x + doubleMe y 

      -This is a very simple example of a common pattern you will see throughout Haskell. Making basic functions that are obviously correct and then combining them into more complex functions. This way you also avoid repetition. What if some mathematicians figured out that 2 is actually 3 and you had to change your program? You could just redefine doubleMe to be x + x + x and since doubleUs calls doubleMe, it would automatically work in this strange new world where 2 is 3. +This is a very simple example of a common pattern you will see throughout Haskell. Making basic functions that are obviously correct and then combining them into more complex functions. This way you also avoid repetition. What if some mathematicians figured out that 2 is actually 3 and you had to change your program? You could just redefine doubleMe to be x + x + x and since doubleUs calls doubleMe, it would automatically work in this strange new world where 2 is 3.

      -

      Functions in Haskell don’t have to be in any particular order, so it doesn’t matter if you define doubleMe first and then doubleUs or if you do it the other way around.

      +

      Functions in Haskell don’t have to be in any particular order, so it doesn’t matter if you define doubleMe first and then doubleUs or if you do it the other way around.

      Now we’re going to make a function that multiplies a number by 2 but only if that number is smaller than or equal to 100 because numbers bigger than 100 are big enough as it is!

      -
      +
      
       doubleSmallNumber x = if x > 100
                               then x
      -                        else x*2 
      + else x*2
      this is you

      -Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this. +Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this.

      -
      +
      
       doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
      -
      +

      -Had we omitted the parentheses, it would have added one only if x wasn’t greater than 100. Note the ' at the end of the function name. That apostrophe doesn’t have any special meaning in Haskell’s syntax. It’s a valid character to use in a function name. We usually use ' to either denote a strict version of a function (one that isn’t lazy) or a slightly modified version of a function or a variable. Because ' is a valid character in functions, we can make a function like this. +Had we omitted the parentheses, it would have added one only if x wasn’t greater than 100. Note the ' at the end of the function name. That apostrophe doesn’t have any special meaning in Haskell’s syntax. It’s a valid character to use in a function name. We usually use ' to either denote a strict version of a function (one that isn’t lazy) or a slightly modified version of a function or a variable. Because ' is a valid character in functions, we can make a function like this.

      -
      -conanO'Brien = "It's a-me, Conan O'Brien!" 
      +
      
      +conanO'Brien = "It's a-me, Conan O'Brien!" 

      -There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably. +There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably.

      An intro to lists

      @@ -213,50 +213,50 @@

      An intro to lists

      In Haskell, lists are a homogenous data structure. They store several elements of the same type. That means that we can have a list of integers or a list of characters but we can’t have a list that has a few integers and then a few characters. And now, a list!

      -
      +
      
       ghci> lostNumbers = [4,8,15,16,23,42]
       ghci> lostNumbers
       [4,8,15,16,23,42]
      -
      +

      -As you can see, lists are denoted by square brackets and the values in the lists are separated by commas. If we tried a list like [1,2,'a',3,'b','c',4], Haskell would complain that characters (which are, by the way, denoted as a character between single quotes) are not numbers. Speaking of characters, strings are just lists of characters. "hello" is just syntactic sugar for ['h','e','l','l','o']. Because strings are lists, we can use list functions on them, which is really handy. +As you can see, lists are denoted by square brackets and the values in the lists are separated by commas. If we tried a list like [1,2,'a',3,'b','c',4], Haskell would complain that characters (which are, by the way, denoted as a character between single quotes) are not numbers. Speaking of characters, strings are just lists of characters. "hello" is just syntactic sugar for ['h','e','l','l','o']. Because strings are lists, we can use list functions on them, which is really handy.

      -

      A common task is putting two lists together. This is done by using the ++ operator.

      -
      +

      A common task is putting two lists together. This is done by using the ++ operator.

      +
      
       ghci> [1,2,3,4] ++ [9,10,11,12]
       [1,2,3,4,9,10,11,12]
       ghci> "hello" ++ " " ++ "world"
       "hello world"
       ghci> ['w','o'] ++ ['o','t']
       "woot"
      -
      +

      -Watch out when repeatedly using the ++ operator on long strings. When you put together two lists (even if you append a singleton list to a list, for instance: [1,2,3] ++ [4]), internally, Haskell has to walk through the whole list on the left side of ++. That’s not a problem when dealing with lists that aren’t too big. But putting something at the end of a list that’s fifty million entries long is going to take a while. However, putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous. +Watch out when repeatedly using the ++ operator on long strings. When you put together two lists (even if you append a singleton list to a list, for instance: [1,2,3] ++ [4]), internally, Haskell has to walk through the whole list on the left side of ++. That’s not a problem when dealing with lists that aren’t too big. But putting something at the end of a list that’s fifty million entries long is going to take a while. However, putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous.

      -
      +
      
       ghci> 'A':" SMALL CAT"
       "A SMALL CAT"
       ghci> 5:[1,2,3,4,5]
       [5,1,2,3,4,5]
      -
      +

      -Notice how : takes a number and a list of numbers or a character and a list of characters, whereas ++ takes two lists. Even if you’re adding an element to the end of a list with ++, you have to surround it with square brackets so it becomes a list.

      +Notice how : takes a number and a list of numbers or a character and a list of characters, whereas ++ takes two lists. Even if you’re adding an element to the end of a list with ++, you have to surround it with square brackets so it becomes a list.

      -[1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on. +[1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on.

      -

      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      -

      If you want to get an element out of a list by index, use !!. The indices start at 0.

      -
      +

      Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

      +

      If you want to get an element out of a list by index, use !!. The indices start at 0.

      +
      
       ghci> "Steve Buscemi" !! 6
       'B'
       ghci> [9.4,33.2,96.2,11.2,23.25] !! 1
       33.2
      -
      +

      But if you try to get the sixth element from a list that only has four elements, you’ll get an error so be careful!

      Lists can also contain lists. They can also contain lists that contain lists that contain lists …

      -
      +
      
       ghci> b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
       ghci> b
       [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
      @@ -265,10 +265,10 @@ 

      An intro to lists

      ghci> [6,6,6]:b [[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]] ghci> b !! 2 -[1,2,2,3,4]
      +[1,2,2,3,4]

      The lists within a list can be of different lengths but they can’t be of different types. Just like you can’t have a list that has some characters and some numbers, you can’t have a list that has some lists of characters and some lists of numbers.

      -

      Lists can be compared if the stuff they contain can be compared. When using <, <=, > and >= to compare lists, they are compared in lexicographical order. First the heads are compared. If they are equal then the second elements are compared, etc.

      -
      +

      Lists can be compared if the stuff they contain can be compared. When using <, <=, > and >= to compare lists, they are compared in lexicographical order. First the heads are compared. If they are equal then the second elements are compared, etc.

      +
      
       ghci> [3,2,1] > [2,1,0]
       True
       ghci> [3,2,1] > [2,10,100]
      @@ -279,50 +279,50 @@ 

      An intro to lists

      True ghci> [3,4,2] == [3,4,2] True -
      +

      What else can you do with lists? Here are some basic functions that operate on lists.

      -

      head takes a list and returns its head. The head of a list is basically its first element. +

      head takes a list and returns its head. The head of a list is basically its first element.

      -
      +
      
       ghci> head [5,4,3,2,1]
      -5 
      -

      tail takes a list and returns its tail. In other words, it chops off a list’s head.

      -
      +5 
      +

      tail takes a list and returns its tail. In other words, it chops off a list’s head.

      +
      
       ghci> tail [5,4,3,2,1]
      -[4,3,2,1] 
      -

      last takes a list and returns its last element.

      -
      +[4,3,2,1] 
      +

      last takes a list and returns its last element.

      +
      
       ghci> last [5,4,3,2,1]
      -1 
      -

      init takes a list and returns everything except its last element.

      -
      +1 
      +

      init takes a list and returns everything except its last element.

      +
      
       ghci> init [5,4,3,2,1]
      -[5,4,3,2] 
      +[5,4,3,2]

      If we think of a list as a monster, here’s what’s what.

      list monster

      But what happens if we try to get the head of an empty list?

      -
      +
      
       ghci> head []
      -*** Exception: Prelude.head: empty list
      -

      Oh my! It all blows up in our face! If there’s no monster, it doesn’t have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it’s always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list.

      -

      length takes a list and returns its length, obviously.

      -
      +*** Exception: Prelude.head: empty list
      +

      Oh my! It all blows up in our face! If there’s no monster, it doesn’t have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it’s always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list.

      +

      length takes a list and returns its length, obviously.

      +
      
       ghci> length [5,4,3,2,1]
      -5
      -

      null checks if a list is empty. If it is, it returns True, otherwise it returns False. Use this function instead of xs == [] (if you have a list called xs).

      -
      +5
      +

      null checks if a list is empty. If it is, it returns True, otherwise it returns False. Use this function instead of xs == [] (if you have a list called xs).

      +
      
       ghci> null [1,2,3]
       False
       ghci> null []
      -True
      -

      reverse reverses a list.

      -
      +True
      +

      reverse reverses a list.

      +
      
       ghci> reverse [5,4,3,2,1]
      -[1,2,3,4,5]
      -

      take takes a number and a list. It extracts that many elements from the beginning of the list. Watch.

      -
      +[1,2,3,4,5]
      +

      take takes a number and a list. It extracts that many elements from the beginning of the list. Watch.

      +
      
       ghci> take 3 [5,4,3,2,1]
       [5,4,3]
       ghci> take 1 [3,9,3]
      @@ -330,157 +330,157 @@ 

      An intro to lists

      ghci> take 5 [1,2] [1,2] ghci> take 0 [6,6,6] -[]
      +[]

      See how if we try to take more elements than there are in the list, it just returns the list. If we try to take 0 elements, we get an empty list.

      -

      drop works in a similar way, only it drops the number of elements from the beginning of a list.

      -
      +

      drop works in a similar way, only it drops the number of elements from the beginning of a list.

      +
      
       ghci> drop 3 [8,4,2,1,5,6]
       [1,5,6]
       ghci> drop 0 [1,2,3,4]
       [1,2,3,4]
       ghci> drop 100 [1,2,3,4]
      -[] 
      -

      maximum takes a list of stuff that can be put in some kind of order and returns the biggest element.

      minimum returns the smallest.

      -
      +[] 
      +

      maximum takes a list of stuff that can be put in some kind of order and returns the biggest element.

      minimum returns the smallest.

      +
      
       ghci> minimum [8,4,2,1,5,6]
       1
       ghci> maximum [1,9,2,3,4]
      -9 
      -

      sum takes a list of numbers and returns their sum.

      product takes a list of numbers and returns their product.

      -
      +9 
      +

      sum takes a list of numbers and returns their sum.

      product takes a list of numbers and returns their product.

      +
      
       ghci> sum [5,2,1,6,3,2,5,7]
       31
       ghci> product [6,2,1,2]
       24
       ghci> product [1,2,5,6,7,9,2,0]
      -0 
      -

      elem takes a thing and a list of things and tells us if that thing is an element of the list. It’s usually called as an infix function because it’s easier to read that way.

      -
      +0 
      +

      elem takes a thing and a list of things and tells us if that thing is an element of the list. It’s usually called as an infix function because it’s easier to read that way.

      +
      
       ghci> 4 `elem` [3,4,5,6]
       True
       ghci> 10 `elem` [3,4,5,6]
       False
      -
      +

      Those were a few basic functions that operate on lists. We’ll take a look at more list functions later.

      Texas ranges

      draw What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that’s not a solution for gentlemen who demand excellence from their programming languages. Instead, we’ll use ranges. Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers can be enumerated. One, two, three, four, etc. Characters can also be enumerated. The alphabet is an enumeration of characters from A to Z. Names can’t be enumerated. What comes after “John”? I don’t know.

      -

      To make a list containing all the natural numbers from 1 to 20, you just write [1..20]. That is the equivalent of writing [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and there’s no difference between writing one or the other except that writing out long enumeration sequences manually is stupid.

      -
      +

      To make a list containing all the natural numbers from 1 to 20, you just write [1..20]. That is the equivalent of writing [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and there’s no difference between writing one or the other except that writing out long enumeration sequences manually is stupid.

      +
      
       ghci> [1..20]
       [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
       ghci> ['a'..'z']
       "abcdefghijklmnopqrstuvwxyz"
       ghci> ['K'..'Z']
      -"KLMNOPQRSTUVWXYZ" 
      +"KLMNOPQRSTUVWXYZ"

      Ranges are cool because you can also specify a step. What if we want all even numbers between 1 and 20? Or every third number between 1 and 20?

      -
      +
      
       ghci> [2,4..20]
       [2,4,6,8,10,12,14,16,18,20]
       ghci> [3,6..20]
      -[3,6,9,12,15,18] 
      -

      It’s simply a matter of separating the first two elements with a comma and then specifying what the upper limit is. While pretty smart, ranges with steps aren’t as smart as some people expect them to be. You can’t do [1,2,4,8,16..100] and expect to get all the powers of 2. Firstly because you can only specify one step. And secondly because some sequences that aren’t arithmetic are ambiguous if given only by a few of their first terms. +[3,6,9,12,15,18]

      +

      It’s simply a matter of separating the first two elements with a comma and then specifying what the upper limit is. While pretty smart, ranges with steps aren’t as smart as some people expect them to be. You can’t do [1,2,4,8,16..100] and expect to get all the powers of 2. Firstly because you can only specify one step. And secondly because some sequences that aren’t arithmetic are ambiguous if given only by a few of their first terms.

      -

      To make a list with all the numbers from 20 to 1, you can’t just do [20..1], you have to do [20,19..1].

      +

      To make a list with all the numbers from 20 to 1, you can’t just do [20..1], you have to do [20,19..1].

      Watch out when using floating point numbers in ranges! Because they are not completely precise (by definition), their use in ranges can yield some pretty funky results.

      -
      +
      
       ghci> [0.1, 0.3 .. 1]
       [0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
      -
      +

      My advice is not to use them in list ranges.

      -You can also use ranges to make infinite lists by just not specifying an upper limit. Later we’ll go into more detail on infinite lists. For now, let’s examine how you would get the first 24 multiples of 13. Sure, you could do [13,26..24*13]. But there’s a better way: take 24 [13,26..]. Because Haskell is lazy, it won’t try to evaluate the infinite list immediately because it would never finish. It’ll wait to see what you want to get out of that infinite lists. And here it sees you just want the first 24 elements and it gladly obliges. +You can also use ranges to make infinite lists by just not specifying an upper limit. Later we’ll go into more detail on infinite lists. For now, let’s examine how you would get the first 24 multiples of 13. Sure, you could do [13,26..24*13]. But there’s a better way: take 24 [13,26..]. Because Haskell is lazy, it won’t try to evaluate the infinite list immediately because it would never finish. It’ll wait to see what you want to get out of that infinite lists. And here it sees you just want the first 24 elements and it gladly obliges.

      A handful of functions that produce infinite lists:

      -

      cycle takes a list and cycles it into an infinite list. If you just try to display the result, it will go on forever so you have to slice it off somewhere.

      -
      +

      cycle takes a list and cycles it into an infinite list. If you just try to display the result, it will go on forever so you have to slice it off somewhere.

      +
      
       ghci> take 10 (cycle [1,2,3])
       [1,2,3,1,2,3,1,2,3,1]
       ghci> take 12 (cycle "LOL ")
      -"LOL LOL LOL " 
      -

      repeat takes an element and produces an infinite list of just that element. It’s like cycling a list with only one element.

      -
      +"LOL LOL LOL " 
      +

      repeat takes an element and produces an infinite list of just that element. It’s like cycling a list with only one element.

      +
      
       ghci> take 10 (repeat 5)
       [5,5,5,5,5,5,5,5,5,5]
      -
      -

      Although it’s simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

      +
      +

      Although it’s simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

      I’m a list comprehension

      frog -If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate. +If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate.

      -

      If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn’t want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We’ll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here’s that comprehension in action.

      -
      +

      If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn’t want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We’ll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here’s that comprehension in action.

      +
      
       ghci> [x*2 | x <- [1..10]]
       [2,4,6,8,10,12,14,16,18,20]
      -
      +

      As you can see, we get the desired results. Now let’s add a condition (or a predicate) to that comprehension. Predicates go after the binding parts and are separated from them by a comma. Let’s say we want only the elements which, doubled, are greater than or equal to 12.

      -
      +
      
       ghci> [x*2 | x <- [1..10], x*2 >= 12]
       [12,14,16,18,20]
      -
      +

      Cool, it works. How about if we wanted all numbers from 50 to 100 whose remainder when divided with the number 7 is 3? Easy.

      -
      +
      
       ghci> [ x | x <- [50..100], x `mod` 7 == 3]
      -[52,59,66,73,80,87,94] 
      -

      Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let’s say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that’s less than 10 with "BOOM!". If a number isn’t odd, we throw it out of our list. For convenience, we’ll put that comprehension inside a function so we can easily reuse it.

      -
      -boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] 
      -

      The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True.

      -
      +[52,59,66,73,80,87,94] 
      +

      Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let’s say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that’s less than 10 with "BOOM!". If a number isn’t odd, we throw it out of our list. For convenience, we’ll put that comprehension inside a function so we can easily reuse it.

      +
      
      +boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] 
      +

      The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True.

      +
      
       ghci> boomBangs [7..13]
      -["BOOM!","BOOM!","BANG!","BANG!"] 
      -

      We can include several predicates. If we wanted all numbers from 10 to 20 that are not 13, 15 or 19, we’d do:

      +["BOOM!","BOOM!","BANG!","BANG!"] 
      +

      We can include several predicates. If we wanted all numbers from 10 to 20 that are not 13, 15 or 19, we’d do:

      
       ghci> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19]
      -[10,11,12,14,16,17,18,20]
      -

      Not only can we have multiple predicates in list comprehensions (an element must satisfy all the predicates to be included in the resulting list), we can also draw from several lists. When drawing from several lists, comprehensions produce all combinations of the given lists and then join them by the output function we supply. A list produced by a comprehension that draws from two lists of length 4 will have a length of 16, provided we don’t filter them. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible combinations between numbers in those lists, here’s what we’d do.

      -
      +[10,11,12,14,16,17,18,20]
      +

      Not only can we have multiple predicates in list comprehensions (an element must satisfy all the predicates to be included in the resulting list), we can also draw from several lists. When drawing from several lists, comprehensions produce all combinations of the given lists and then join them by the output function we supply. A list produced by a comprehension that draws from two lists of length 4 will have a length of 16, provided we don’t filter them. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible combinations between numbers in those lists, here’s what we’d do.

      +
      
       ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
      -[16,20,22,40,50,55,80,100,110] 
      +[16,20,22,40,50,55,80,100,110]

      As expected, the length of the new list is 9. What if we wanted all possible products that are more than 50?

      -
      +
      
       ghci> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50]
      -[55,80,100,110] 
      +[55,80,100,110]

      How about a list comprehension that combines a list of adjectives and a list of nouns … for epic hilarity.

      -
      +
      
       ghci> nouns = ["hobo","frog","pope"]
       ghci> adjectives = ["lazy","grouchy","scheming"]
       ghci> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns]
       ["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog",
      -"grouchy pope","scheming hobo","scheming frog","scheming pope"] 
      -

      I know! Let’s write our own version of length! We’ll call it length'.

      -
      -length' xs = sum [1 | _ <- xs] 
      -

      _ means that we don’t care what we’ll draw from the list anyway so instead of writing a variable name that we’ll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.

      +"grouchy pope","scheming hobo","scheming frog","scheming pope"]
      +

      I know! Let’s write our own version of length! We’ll call it length'.

      +
      
      +length' xs = sum [1 | _ <- xs] 
      +

      _ means that we don’t care what we’ll draw from the list anyway so instead of writing a variable name that we’ll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.

      Just a friendly reminder: because strings are lists, we can use list comprehensions to process and produce strings. Here’s a function that takes a string and removes everything except uppercase letters from it.

      -
      -removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] 
      +
      
      +removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] 

      Testing it out:

      -
      +
      
       ghci> removeNonUppercase "Hahaha! Ahahaha!"
       "HA"
       ghci> removeNonUppercase "IdontLIKEFROGS"
      -"ILIKEFROGS" 
      -

      The predicate here does all the work. It says that the character will be included in the new list only if it’s an element of the list ['A'..'Z']. Nested list comprehensions are also possible if you’re operating on lists that contain lists. A list contains several lists of numbers. Let’s remove all odd numbers without flattening the list.

      -
      +"ILIKEFROGS" 
      +

      The predicate here does all the work. It says that the character will be included in the new list only if it’s an element of the list ['A'..'Z']. Nested list comprehensions are also possible if you’re operating on lists that contain lists. A list contains several lists of numbers. Let’s remove all odd numbers without flattening the list.

      +
      
       ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
       ghci> [ [ x | x <- xs, even x ] | xs <- xxs]
       [[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
      -
      +

      You can write list comprehensions across several lines. So if you’re not in GHCI, it’s better to split longer list comprehensions across multiple lines, especially if they’re nested.

      Tuples

      tuples

      In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That’s its type and it doesn’t matter if it has only one number in it or an infinite amount of numbers. Tuples, however, are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.

      Another key difference is that they don’t have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

      -

      Think about how we’d represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it’s still a list of lists with numbers, but it kind of doesn’t make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can’t have a couple of pairs in it and then a triple (a tuple of size three), so let’s use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we’d get this error:

      -
      +

      Think about how we’d represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it’s still a list of lists with numbers, but it kind of doesn’t make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can’t have a couple of pairs in it and then a triple (a tuple of size three), so let’s use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we’d get this error:

      +
      
           • Couldn't match expected type ‘(a, b)’
                         with actual type ‘(a0, b0, c0)’
           • In the expression: (8, 11, 5)
      @@ -488,53 +488,53 @@ 

      Tuples

      In an equation for ‘it’: it = [(1, 2), (8, 11, 5), (4, 5)] • Relevant bindings include it :: [(a, b)] (bound at <interactive>:1:1) -
      -

      It’s telling us that we tried to use a pair and a triple in the same list, which is not supposed to happen. You also couldn’t make a list like [(1,2),("One",2)] because the first element of the list is a pair of numbers and the second element is a pair consisting of a string and a number. Tuples can also be used to represent a wide variety of data. For instance, if we wanted to represent someone’s name and age in Haskell, we could use a triple: ("Christopher", "Walken", 55). As seen in this example, tuples can also contain lists.

      +
      +

      It’s telling us that we tried to use a pair and a triple in the same list, which is not supposed to happen. You also couldn’t make a list like [(1,2),("One",2)] because the first element of the list is a pair of numbers and the second element is a pair consisting of a string and a number. Tuples can also be used to represent a wide variety of data. For instance, if we wanted to represent someone’s name and age in Haskell, we could use a triple: ("Christopher", "Walken", 55). As seen in this example, tuples can also contain lists.

      Use tuples when you know in advance how many components some piece of data should have. Tuples are much more rigid because each different size of tuple is its own type, so you can’t write a general function to append an element to a tuple — you’d have to write a function for appending to a pair, one function for appending to a triple, one function for appending to a 4-tuple, etc.

      While there are singleton lists, there’s no such thing as a singleton tuple. It doesn’t really make much sense when you think about it. A singleton tuple would just be the value it contains and as such would have no benefit to us.

      Like lists, tuples can be compared with each other if their components can be compared. Only you can’t compare two tuples of different sizes, whereas you can compare two lists of different sizes. Two useful functions that operate on pairs:

      -

      fst takes a pair and returns its first component.

      -
      +

      fst takes a pair and returns its first component.

      +
      
       ghci> fst (8,11)
       8
       ghci> fst ("Wow", False)
      -"Wow"
      -

      snd takes a pair and returns its second component. Surprise!

      -
      +"Wow"
      +

      snd takes a pair and returns its second component. Surprise!

      +
      
       ghci> snd (8,11)
       11
       ghci> snd ("Wow", False)
      -False
      +False

      Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.

      -

      A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.

      -
      +

      A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.

      +
      
       ghci> zip [1,2,3,4,5] [5,5,5,5,5]
       [(1,5),(2,5),(3,5),(4,5),(5,5)]
       ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"]
       [(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
      -
      -

      It pairs up the elements and produces a new list. The first element goes with the first, the second with the second, etc. Notice that because pairs can have different types in them, zip can take two lists that contain different types and zip them up. What happens if the lengths of the lists don’t match?

      -
      +
      +

      It pairs up the elements and produces a new list. The first element goes with the first, the second with the second, etc. Notice that because pairs can have different types in them, zip can take two lists that contain different types and zip them up. What happens if the lengths of the lists don’t match?

      +
      
       ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["im","a","turtle"]
       [(5,"im"),(3,"a"),(2,"turtle")]
      -
      +

      The longer list simply gets cut off to match the length of the shorter one. Because Haskell is lazy, we can zip finite lists with infinite lists:

      -
      +
      
       ghci> zip [1..] ["apple", "orange", "cherry", "mango"]
       [(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
      -
      +
      look at meee

      Here’s a problem that combines tuples and list comprehensions: which right triangle that has integers for all sides and all sides equal to or smaller than 10 has a perimeter of 24? First, let’s try generating all triangles with sides equal to or smaller than 10:

      -
      ghci> triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ] 
      -

      We’re just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out triangles in GHCI, you’ll get a list of all possible triangles with sides under or equal to 10. Next, we’ll add a condition that they all have to be right triangles. We’ll also modify this function by taking into consideration that side b isn’t larger than the hypothenuse and that side a isn’t larger than side b.

      -
      -ghci> rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2] 
      +
      ghci> triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ] 
      +

      We’re just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out triangles in GHCI, you’ll get a list of all possible triangles with sides under or equal to 10. Next, we’ll add a condition that they all have to be right triangles. We’ll also modify this function by taking into consideration that side b isn’t larger than the hypothenuse and that side a isn’t larger than side b.

      +
      
      +ghci> rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2] 

      We’re almost done. Now, we just modify the function by saying that we want the ones where the perimeter is 24.

      -
      +
      
       ghci> rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
       ghci> rightTriangles'
       [(6,8,10)]
      -
      +

      And there’s our answer! This is a common pattern in functional programming. You take a starting set of solutions and then you apply transformations to those solutions and filter them until you get the right ones.

        diff --git a/docs/style.css b/docs/style.css index fe2e5e6..5e3c34e 100644 --- a/docs/style.css +++ b/docs/style.css @@ -58,7 +58,7 @@ img.center { margin:10px auto 25px auto; display:block; } -pre.code { +pre > code { color:white; /*background-color:#522812;*/ background-color:black; @@ -105,7 +105,7 @@ 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; diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index 4c8a56a..dbbd4da 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -36,12 +36,12 @@

        Pattern matching

        four!

        This chapter will cover some of Haskell’s cool syntactic constructs and we’ll start with pattern matching. Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns.

        When defining functions, you can define separate function bodies for different patterns. This leads to really neat code that’s simple and readable. You can pattern match on any data type — numbers, characters, lists, tuples, etc. Let’s make a really trivial function that checks if the number we supplied to it is a seven or not.

        -
        +
        
         lucky :: (Integral a) => a -> String
         lucky 7 = "LUCKY NUMBER SEVEN!"
        -lucky x = "Sorry, you're out of luck, pal!" 
        -

        When you call lucky, the patterns will be checked from top to bottom and when it conforms to a pattern, the corresponding function body will be used. The only way a number can conform to the first pattern here is if it is 7. If it’s not, it falls through to the second pattern, which matches anything and binds it to x. This function could have also been implemented by using an if statement. But what if we wanted a function that says the numbers from 1 to 5 and says "Not between 1 and 5" for any other number? Without pattern matching, we’d have to make a pretty convoluted if then else tree. However, with it:

        -
        +lucky x = "Sorry, you're out of luck, pal!" 
        +

        When you call lucky, the patterns will be checked from top to bottom and when it conforms to a pattern, the corresponding function body will be used. The only way a number can conform to the first pattern here is if it is 7. If it’s not, it falls through to the second pattern, which matches anything and binds it to x. This function could have also been implemented by using an if statement. But what if we wanted a function that says the numbers from 1 to 5 and says "Not between 1 and 5" for any other number? Without pattern matching, we’d have to make a pretty convoluted if then else tree. However, with it:

        +
        
         sayMe :: (Integral a) => a -> String
         sayMe 1 = "One!"
         sayMe 2 = "Two!"
        @@ -49,50 +49,50 @@ 

        Pattern matching

        sayMe 4 = "Four!" sayMe 5 = "Five!" sayMe x = "Not between 1 and 5" -
        -

        Note that if we moved the last pattern (the catch-all one) to the top, it would always say "Not between 1 and 5", because it would catch all the numbers and they wouldn’t have a chance to fall through and be checked for any other patterns.

        -

        Remember the factorial function we implemented previously? We defined the factorial of a number n as product [1..n]. We can also define a factorial function recursively, the way it is usually defined in mathematics. We start by saying that the factorial of 0 is 1. Then we state that the factorial of any positive integer is that integer multiplied by the factorial of its predecessor. Here’s how that looks like translated in Haskell terms.

        -
        +
        +

        Note that if we moved the last pattern (the catch-all one) to the top, it would always say "Not between 1 and 5", because it would catch all the numbers and they wouldn’t have a chance to fall through and be checked for any other patterns.

        +

        Remember the factorial function we implemented previously? We defined the factorial of a number n as product [1..n]. We can also define a factorial function recursively, the way it is usually defined in mathematics. We start by saying that the factorial of 0 is 1. Then we state that the factorial of any positive integer is that integer multiplied by the factorial of its predecessor. Here’s how that looks like translated in Haskell terms.

        +
        
         factorial :: (Integral a) => a -> a
         factorial 0 = 1
         factorial n = n * factorial (n - 1)
        -
        +

        -This is the first time we’ve defined a function recursively. Recursion is important in Haskell and we’ll take a closer look at it later. But in a nutshell, this is what happens if we try to get the factorial of, say, 3. It tries to compute 3 * factorial 2. The factorial of 2 is 2 * factorial 1, so for now we have 3 * (2 * factorial 1). factorial 1 is 1 * factorial 0, so we have 3 * (2 * (1 * factorial 0)). Now here comes the trick — we’ve defined the factorial of 0 to be just 1 and because it encounters that pattern before the catch-all one, it just returns 1. So the final result is equivalent to 3 * (2 * (1 * 1)). Had we written the second pattern on top of the first one, it would catch all numbers, including 0 and our calculation would never terminate. That’s why order is important when specifying patterns and it’s always best to specify the most specific ones first and then the more general ones later.

        +This is the first time we’ve defined a function recursively. Recursion is important in Haskell and we’ll take a closer look at it later. But in a nutshell, this is what happens if we try to get the factorial of, say, 3. It tries to compute 3 * factorial 2. The factorial of 2 is 2 * factorial 1, so for now we have 3 * (2 * factorial 1). factorial 1 is 1 * factorial 0, so we have 3 * (2 * (1 * factorial 0)). Now here comes the trick — we’ve defined the factorial of 0 to be just 1 and because it encounters that pattern before the catch-all one, it just returns 1. So the final result is equivalent to 3 * (2 * (1 * 1)). Had we written the second pattern on top of the first one, it would catch all numbers, including 0 and our calculation would never terminate. That’s why order is important when specifying patterns and it’s always best to specify the most specific ones first and then the more general ones later.

        Pattern matching can also fail. If we define a function like this:

        -
        +
        
         charName :: Char -> String
         charName 'a' = "Albert"
         charName 'b' = "Broseph"
         charName 'c' = "Cecil"
        -
        +

        and then try to call it with an input that we didn’t expect, this is what happens:

        -
        +
        
         ghci> charName 'a'
         "Albert"
         ghci> charName 'b'
         "Broseph"
         ghci> charName 'h'
         "*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in function charName
        -
        +

        It complains that we have non-exhaustive patterns, and rightfully so. When making patterns, we should always include a catch-all pattern so that our program doesn’t crash if we get some unexpected input.

        Pattern matching can also be used on tuples. What if we wanted to make a function that takes two vectors in a 2D space (that are in the form of pairs) and adds them together? To add together two vectors, we add their x components separately and then their y components separately. Here’s how we would have done it if we didn’t know about pattern matching:

        -
        +
        
         addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
         addVectors a b = (fst a + fst b, snd a + snd b)
        -
        +

        Well, that works, but there’s a better way to do it. Let’s modify the function so that it uses pattern matching.

        -
        +
        
         addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
         addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
        -
        -

        There we go! Much better. Note that this is already a catch-all pattern. The type of addVectors (in both cases) is addVectors :: (Num a) => (a, a) -> (a, a) - > (a, a), so we are guaranteed to get two pairs as parameters.

        -

        fst and snd extract the components of pairs. But what about triples? Well, there are no provided functions that do that but we can make our own.

        -
        +
        +

        There we go! Much better. Note that this is already a catch-all pattern. The type of addVectors (in both cases) is addVectors :: (Num a) => (a, a) -> (a, a) - > (a, a), so we are guaranteed to get two pairs as parameters.

        +

        fst and snd extract the components of pairs. But what about triples? Well, there are no provided functions that do that but we can make our own.

        +
        
         first :: (a, b, c) -> a
         first (x, _, _) = x
         
        @@ -101,146 +101,146 @@ 

        Pattern matching

        third :: (a, b, c) -> c third (_, _, z) = z -
        -

        The _ means the same thing as it does in list comprehensions. It means that we really don’t care what that part is, so we just write a _.

        +
        +

        The _ means the same thing as it does in list comprehensions. It means that we really don’t care what that part is, so we just write a _.

        Which reminds me, you can also pattern match in list comprehensions. Check this out:

        -
        +
        
         ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]
         ghci> [a+b | (a,b) <- xs]
        -[4,7,6,8,11,4] 
        +[4,7,6,8,11,4]

        Should a pattern match fail, it will just move on to the next element.

        -

        Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list. But since [1,2,3] is just syntactic sugar for 1:2:3:[], you can also use the former pattern. A pattern like x:xs will bind the head of the list to x and the rest of it to xs, even if there’s only one element so xs ends up being an empty list.

        -

        Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.

        -

        If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.

        -

        Now that we know how to pattern match against list, let’s make our own implementation of the head function.

        -
        +

        Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list. But since [1,2,3] is just syntactic sugar for 1:2:3:[], you can also use the former pattern. A pattern like x:xs will bind the head of the list to x and the rest of it to xs, even if there’s only one element so xs ends up being an empty list.

        +

        Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.

        +

        If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.

        +

        Now that we know how to pattern match against list, let’s make our own implementation of the head function.

        +
        
         head' :: [a] -> a
         head' [] = error "Can't call head on an empty list, dummy!"
         head' (x:_) = x
        -
        +

        Checking if it works:

        -
        +
        
         ghci> head' [4,5,6]
         4
         ghci> head' "Hello"
         'H'
        -
        +

        -Nice! Notice that if you want to bind to several variables (even if one of them is just _ and doesn’t actually bind at all), we have to surround them in parentheses. Also notice the error function that we used. It takes a string and generates a runtime error, using that string as information about what kind of error occurred. It causes the program to crash, so it’s not good to use it too much. But calling head on an empty list doesn’t make sense. +Nice! Notice that if you want to bind to several variables (even if one of them is just _ and doesn’t actually bind at all), we have to surround them in parentheses. Also notice the error function that we used. It takes a string and generates a runtime error, using that string as information about what kind of error occurred. It causes the program to crash, so it’s not good to use it too much. But calling head on an empty list doesn’t make sense.

        Let’s make a trivial function that tells us some of the first elements of the list in (in)convenient English form.

        -
        +
        
         tell :: (Show a) => [a] -> String
         tell [] = "The list is empty"
         tell (x:[]) = "The list has one element: " ++ show x
         tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y
         tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
        -
        -

        This function is safe because it takes care of the empty list, a singleton list, a list with two elements and a list with more than two elements. Note that (x:[]) and (x:y:[]) could be rewritten as [x] and [x,y] (because its syntactic sugar, we don’t need the parentheses). We can’t rewrite (x:y:_) with square brackets because it matches any list of length 2 or more.

        -

        We already implemented our own length function using list comprehension. Now we’ll do it by using pattern matching and a little recursion:

        -
        +
        +

        This function is safe because it takes care of the empty list, a singleton list, a list with two elements and a list with more than two elements. Note that (x:[]) and (x:y:[]) could be rewritten as [x] and [x,y] (because its syntactic sugar, we don’t need the parentheses). We can’t rewrite (x:y:_) with square brackets because it matches any list of length 2 or more.

        +

        We already implemented our own length function using list comprehension. Now we’ll do it by using pattern matching and a little recursion:

        +
        
         length' :: (Num b) => [a] -> b
         length' [] = 0
        -length' (_:xs) = 1 + length' xs
        -

        This is similar to the factorial function we wrote earlier. First we defined the result of a known input — the empty list. This is also known as the edge condition. Then in the second pattern we take the list apart by splitting it into a head and a tail. We say that the length is equal to 1 plus the length of the tail. We use _ to match the head because we don’t actually care what it is. Also note that we’ve taken care of all possible patterns of a list. The first pattern matches an empty list and the second one matches anything that isn’t an empty list.

        -

        Let’s see what happens if we call length' on "ham". First, it will check if it’s an empty list. Because it isn’t, it falls through to the second pattern. It matches on the second pattern and there it says that the length is 1 + length' "am", because we broke it into a head and a tail and discarded the head. O-kay. The length' of "am" is, similarly, 1 + length' "m". So right now we have 1 + (1 + length' "m"). length' "m" is 1 + length' "" (could also be written as 1 + length' []). And we’ve defined length' [] to be 0. So in the end we have 1 + (1 + (1 + 0)).

        -

        Let’s implement sum. We know that the sum of an empty list is 0. We write that down as a pattern. And we also know that the sum of a list is the head plus the sum of the rest of the list. So if we write that down, we get:

        -
        +length' (_:xs) = 1 + length' xs
        +

        This is similar to the factorial function we wrote earlier. First we defined the result of a known input — the empty list. This is also known as the edge condition. Then in the second pattern we take the list apart by splitting it into a head and a tail. We say that the length is equal to 1 plus the length of the tail. We use _ to match the head because we don’t actually care what it is. Also note that we’ve taken care of all possible patterns of a list. The first pattern matches an empty list and the second one matches anything that isn’t an empty list.

        +

        Let’s see what happens if we call length' on "ham". First, it will check if it’s an empty list. Because it isn’t, it falls through to the second pattern. It matches on the second pattern and there it says that the length is 1 + length' "am", because we broke it into a head and a tail and discarded the head. O-kay. The length' of "am" is, similarly, 1 + length' "m". So right now we have 1 + (1 + length' "m"). length' "m" is 1 + length' "" (could also be written as 1 + length' []). And we’ve defined length' [] to be 0. So in the end we have 1 + (1 + (1 + 0)).

        +

        Let’s implement sum. We know that the sum of an empty list is 0. We write that down as a pattern. And we also know that the sum of a list is the head plus the sum of the rest of the list. So if we write that down, we get:

        +
        
         sum' :: (Num a) => [a] -> a
         sum' [] = 0
         sum' (x:xs) = x + sum' xs
        -
        -

        There’s also a thing called as patterns. Those are a handy way of breaking something up according to a pattern and binding it to names whilst still keeping a reference to the whole thing. You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again. Here’s a quick and dirty example:

        -
        +
        +

        There’s also a thing called as patterns. Those are a handy way of breaking something up according to a pattern and binding it to names whilst still keeping a reference to the whole thing. You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again. Here’s a quick and dirty example:

        +
        
         capital :: String -> String
         capital "" = "Empty string, whoops!"
         capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
        -
        -
        +
        +
        
         ghci> capital "Dracula"
         "The first letter of Dracula is D"
        -
        +

        Normally we use as patterns to avoid repeating ourselves when matching against a bigger pattern when we have to use the whole thing again in the function body.

        -

        One more thing — you can’t use ++ in pattern matches. If you tried to pattern match against (xs ++ ys), what would be in the first and what would be in the second list? It doesn’t make much sense. It would make sense to match stuff against (xs ++ [x,y,z]) or just (xs ++ [x]), but because of the nature of lists, you can’t do that.

        +

        One more thing — you can’t use ++ in pattern matches. If you tried to pattern match against (xs ++ ys), what would be in the first and what would be in the second list? It doesn’t make much sense. It would make sense to match stuff against (xs ++ [x,y,z]) or just (xs ++ [x]), but because of the nature of lists, you can’t do that.

        Guards, guards!

        guards

        Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds a lot like an if statement and it’s very similar. The thing is that guards are a lot more readable when you have several conditions and they play really nicely with patterns.

        Instead of explaining their syntax, let’s just dive in and make a function using guards. We’re going to make a simple function that responds differently depending on the density given. Density (or specific mass) is a substance’s mass per unit of volume (here, grams per liter). If a substance has a density of less than 1.2, it will float in air, as 1.2g/L is the density of air. If it has more than 1000g/L (the density of water), it will sink in water. Between are things (like people, usually) that will neither float away nor sink in water. So here’s the function (we won’t be calculating density right now, this function just gets a density and responds)

        -
        +
        
         densityTell :: (RealFloat a) => a -> String
         densityTell density
             | density < 1.2 = "Wow! You're going for a ride in the sky!"
             | density <= 1000.0 = "Have fun swimming, but watch out for sharks!"
             | otherwise   = "If it's sink or swim, you're going to sink."
        -
        -

        Guards are indicated by pipes that follow a function’s name and its parameters. Usually, they’re indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that’s smaller than or equal to 1.2. Because it isn’t, it falls through to the next guard. The check is carried out with the second guard and because 24.3 is less than 1000.0, the second string is returned.

        +
        +

        Guards are indicated by pipes that follow a function’s name and its parameters. Usually, they’re indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that’s smaller than or equal to 1.2. Because it isn’t, it falls through to the next guard. The check is carried out with the second guard and because 24.3 is less than 1000.0, the second string is returned.

        This is very reminiscent of a big if else tree in imperative languages, only this is far better and more readable. While big if else trees are usually frowned upon, sometimes a problem is defined in such a discrete way that you can’t get around them. Guards are a very nice alternative for this.

        -

        Many times, the last guard is otherwise. otherwise is defined simply as otherwise = True and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to False (and we haven’t provided an otherwise catch-all guard), evaluation falls through to the next pattern. That’s how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.

        +

        Many times, the last guard is otherwise. otherwise is defined simply as otherwise = True and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to False (and we haven’t provided an otherwise catch-all guard), evaluation falls through to the next pattern. That’s how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.

        Of course we can use guards with functions that take as many parameters as we want. Instead of having the user calculate the density of the substance on their own before calling the function, let’s modify this function so that it takes a mass (in grams) and volume (in liters).

        -
        +
        
         densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
             | mass / volume <= 1000.0 = "Have fun swimming, but watch out for sharks!"
             | otherwise   = "If it's sink or swim, you're going to sink."
        -
        +

        Let’s see if cat food will float …

        -
        +
        
         ghci> densityTell 400 1
         "Have fun swimming, but watch out for sharks!"
        -
        +

        Looks like it will! At least until it dissolves into the pool… Yuck!

        -

        Note that there’s no = right after the function name and its parameters, before the first guard. Many newbies get syntax errors because they sometimes put it there.

        -

        Another very simple example: let’s implement our own max function. If you remember, it takes two things that can be compared and returns the larger of them.

        -
        +

        Note that there’s no = right after the function name and its parameters, before the first guard. Many newbies get syntax errors because they sometimes put it there.

        +

        Another very simple example: let’s implement our own max function. If you remember, it takes two things that can be compared and returns the larger of them.

        +
        
         max' :: (Ord a) => a -> a -> a
         max' a b
             | a > b     = a
             | otherwise = b
        -
        -

        Guards can also be written inline, although I’d advise against that because it’s less readable, even for very short functions. But to demonstrate, we could write max' like this:

        -
        +
        +

        Guards can also be written inline, although I’d advise against that because it’s less readable, even for very short functions. But to demonstrate, we could write max' like this:

        +
        
         max' :: (Ord a) => a -> a -> a
         max' a b | a > b = a | otherwise = b
        -
        +
        -

        Ugh! Not very readable at all! Moving on: let’s implement our own compare by using guards.

        -
        +

        Ugh! Not very readable at all! Moving on: let’s implement our own compare by using guards.

        +
        
         myCompare :: (Ord a) => a -> a -> Ordering
         a `myCompare` b
             | a > b     = GT
             | a == b    = EQ
             | otherwise = LT
        -
        -
        +
        +
        
         ghci> 3 `myCompare` 2
         GT
        -
        +

        Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.

        Where!?

        In the previous section, we defined a density calculator function and responder like this:

        -
        +
        
         densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
             | mass / volume <= 1000.0 = "Have fun swimming, but watch out for sharks!"
             | otherwise   = "If it's sink or swim, you're going to sink."
        -
        +

        Notice that we repeat ourselves here two times. We repeat ourselves two times. Repeating yourself (two times) while programming is about as desirable as getting kicked inna head. Since we repeat the same expression twice, it would be ideal if we could calculate it once, bind it to a name and then use that name instead of the expression. Well, we can modify our function like this:

        -
        +
        
         densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | density < 1.2 = "Wow! You're going for a ride in the sky!"
             | density <= 1000.0 = "Have fun swimming, but watch out for sharks!"
             | otherwise   = "If it's sink or swim, you're going to sink."
             where density = mass / volume
        -
        -

        We put the keyword where after the guards (usually it’s best to indent it as much as the pipes are indented) and then we define several names or functions. These names are visible across the guards and give us the advantage of not having to repeat ourselves. If we decide that we want to calculate density a bit differently, we only have to change it once. It also improves readability by giving names to things and can make our programs faster since stuff like our density variable here is calculated only once. We could go a bit overboard and present our function like this:

        -
        +
        +

        We put the keyword where after the guards (usually it’s best to indent it as much as the pipes are indented) and then we define several names or functions. These names are visible across the guards and give us the advantage of not having to repeat ourselves. If we decide that we want to calculate density a bit differently, we only have to change it once. It also improves readability by giving names to things and can make our programs faster since stuff like our density variable here is calculated only once. We could go a bit overboard and present our function like this:

        +
        
         densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | density < air = "Wow! You're going for a ride in the sky!"
        @@ -249,84 +249,84 @@ 

        Where!?

        where density = mass / volume air = 1.2 water = 1000.0 -
        +

        The names we define in the where section of a function are only visible to that function, so we don’t have to worry about them polluting the namespace of other functions. Notice that all the names are aligned at a single column. If we don’t align them nice and proper, Haskell gets confused because then it doesn’t know they’re all part of the same block.

        where bindings aren’t shared across function bodies of different patterns. If you want several patterns of one function to access some shared name, you have to define it globally.

        You can also use where bindings to pattern match! We could have rewritten the where section of our previous function as:

        -
        +
        
             ...
             where density = mass / volume
                   (air, water) = (1.2, 1000.0)
        -
        +

        Let’s make another fairly trivial function where we get a first and a last name and give someone back their initials.

        -
        +
        
         initials :: String -> String -> String
         initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
             where (f:_) = firstname
                   (l:_) = lastname
        -
        +

        We could have done this pattern matching directly in the function’s parameters (it would have been shorter and clearer actually) but this just goes to show that it’s possible to do it in where bindings as well.

        Just like we’ve defined constants in where blocks, you can also define functions. Staying true to our solids programming theme, let’s make a function that takes a list of mass-volume pairs and returns a list of densities.

        -
        +
        
         calcDensities :: (RealFloat a) => [(a, a)] -> [a]
         calcDensities xs = [density m v | (m, v) <- xs]
             where density mass volume = mass / volume
        -
        -

        And that’s all there is to it! The reason we had to introduce density as a function in this example is because we can’t just calculate one density from the function’s parameters. We have to examine the list passed to the function and there’s a different density for every pair in there.

        +
        +

        And that’s all there is to it! The reason we had to introduce density as a function in this example is because we can’t just calculate one density from the function’s parameters. We have to examine the list passed to the function and there’s a different density for every pair in there.

        where bindings can also be nested. It’s a common idiom to make a function and define some helper function in its where clause and then to give those functions helper functions as well, each with its own where clause.

        Let it be

        Very similar to where bindings are let bindings. Where bindings are a syntactic construct that let you bind to variables at the end of a function and the whole function can see them, including all the guards. Let bindings let you bind to variables anywhere and are expressions themselves, but are very local, so they don’t span across guards. Just like any construct in Haskell that is used to bind values to names, let bindings can be used for pattern matching. Let’s see them in action! This is how we could define a function that gives us a cylinder’s surface area based on its height and radius:

        -
        +
        
         cylinder :: (RealFloat a) => a -> a -> a
         cylinder r h =
             let sideArea = 2 * pi * r * h
                 topArea = pi * r ^2
             in  sideArea + 2 * topArea
        -
        +
        let it be -

        The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

        +

        The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

        The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs. Remember when we did the if statement and it was explained that an if else statement is an expression and you can cram it in almost anywhere?

        -
        +
        
         ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
         ["Woo", "Bar"]
         ghci> 4 * (if 10 > 5 then 10 else 0) + 2
         42
        -
        +

        You can also do that with let bindings.

        -
        +
        
         ghci> 4 * (let a = 9 in a + 1) + 2
         42
        -
        +

        They can also be used to introduce functions in a local scope:

        -
        +
        
         ghci> [let square x = x * x in (square 5, square 3, square 2)]
         [(25,9,4)]
        -
        +

        If we want to bind to several variables inline, we obviously can’t align them at columns. That’s why we can separate them with semicolons.

        -
        +
        
         ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)
         (6000000,"Hey there!")
        -
        +

        You don’t have to put a semicolon after the last binding but you can if you want. Like we said before, you can pattern match with let bindings. They’re very useful for quickly dismantling a tuple into components and binding them to names and such.

        -
        +
        
         ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100
         600
        -
        +

        You can also put let bindings inside list comprehensions. Let’s rewrite our previous example of calculating lists of mass-volume pairs to use a let inside a list comprehension instead of defining an auxiliary function with a where.

        -
        +
        
         calcDensities :: (RealFloat a) => [(a, a)] -> [a]
         calcDensities xs = [density | (m, v) <- xs, let density = m / v]
        -
        -

        We include a let inside a list comprehension much like we would a predicate, only it doesn’t filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the densities that will float in air:

        -
        +
        +

        We include a let inside a list comprehension much like we would a predicate, only it doesn’t filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the densities that will float in air:

        +
        
         calcDensities :: (RealFloat a) => [(a, a)] -> [a]
         calcDensities xs = [density | (m, v) <- xs, let density = m / v, density < 1.2]
        -
        -

        We can’t use the density name in the (m, v) <- xs part because it’s defined prior to the let binding.

        +
        +

        We can’t use the density name in the (m, v) <- xs part because it’s defined prior to the let binding.

        We omitted the in part of the let binding when we used them in list comprehensions because the visibility of the names is already predefined there. However, we could use a let in binding in a predicate and the names defined would only be visible to that predicate. The in part can also be omitted when defining functions and constants directly in GHCi. If we do that, then the names will be visible throughout the entire interactive session.

        -
        +
        
         ghci> let zoot x y z = x * y + z
         ghci> zoot 3 9 2
         29
        @@ -334,45 +334,45 @@ 

        Let it be

        14 ghci> boot <interactive>:1:0: Not in scope: `boot' -
        +

        If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

        Case expressions

        case

        Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for specific values of that variable and then maybe including a catch-all block of code in case the variable has some value for which we didn’t set up a case.

        Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

        -
        +
        
         head' :: [a] -> a
         head' [] = error "No head for empty lists!"
         head' (x:_) = x
        -
        -
        +
        +
        
         head' :: [a] -> a
         head' xs = case xs of [] -> error "No head for empty lists!"
                               (x:_) -> x
        -
        +

        As you can see, the syntax for case expressions is pretty simple:

        -
        +
        
         case expression of pattern -> result
                            pattern -> result
                            pattern -> result
                            ...
        -
        -

        expression is matched against the patterns. The pattern matching action is the same as expected: the first pattern that matches the expression is used. If it falls through the whole case expression and no suitable pattern is found, a runtime error occurs.

        +
        +

        expression is matched against the patterns. The pattern matching action is the same as expected: the first pattern that matches the expression is used. If it falls through the whole case expression and no suitable pattern is found, a runtime error occurs.

        Whereas pattern matching on function parameters can only be done when defining functions, case expressions can be used pretty much anywhere. For instance:

        -
        +
        
         describeList :: [a] -> String
         describeList xs = "The list is " ++ case xs of [] -> "empty."
                                                        [x] -> "a singleton list."
                                                        xs -> "a longer list."
        -
        +

        They are useful for pattern matching against something in the middle of an expression. Because pattern matching in function definitions is syntactic sugar for case expressions, we could have also defined this like so:

        -
        +
        
         describeList :: [a] -> String
         describeList xs = "The list is " ++ what xs
             where what [] = "empty."
                   what [x] = "a singleton list."
                   what xs = "a longer list."
        -
        +
          diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 8e57826..8a15d57 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -36,9 +36,9 @@

          Believe the type

          moo

          Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with some number, it won’t even compile. That’s good because it’s better to catch such errors at compile time instead of having your program crash. Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.

          Unlike Java or Pascal, Haskell has type inference. If we write a number, we don’t have to tell Haskell it’s a number. It can infer that on its own, so we don’t have to explicitly write out the types of our functions and expressions to get things done. We covered some of the basics of Haskell with only a very superficial glance at types. However, understanding the type system is a very important part of learning Haskell.

          -

          A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

          -

          Now we’ll use GHCI to examine the types of some expressions. We’ll do that by using the :t command which, followed by any valid expression, tells us its type. Let’s give it a whirl.

          -
          +

          A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

          +

          Now we’ll use GHCI to examine the types of some expressions. We’ll do that by using the :t command which, followed by any valid expression, tells us its type. Let’s give it a whirl.

          +
          
           ghci> :t 'a'
           'a' :: Char
           ghci> :t True
          @@ -49,104 +49,104 @@ 

          Believe the type

          (True, 'a') :: (Bool, Char) ghci> :t 4 == 5 4 == 5 :: Bool -
          +

          bomb -Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. 'a', as it would seem, has a type of Char. It’s not hard to conclude that it stands for character. True is of a Bool type. That makes sense. But what’s this? Examining the type of "HELLO!" yields a [Char]. The square brackets denote a list. So we read that as it being a list of characters. Unlike lists, each tuple length has its own type. So the expression of (True, 'a') has a type of (Bool, Char), whereas an expression such as ('a','b','c') would have the type of (Char, Char, Char). 4 == 5 will always return False, so its type is Bool. +Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. 'a', as it would seem, has a type of Char. It’s not hard to conclude that it stands for character. True is of a Bool type. That makes sense. But what’s this? Examining the type of "HELLO!" yields a [Char]. The square brackets denote a list. So we read that as it being a list of characters. Unlike lists, each tuple length has its own type. So the expression of (True, 'a') has a type of (Bool, Char), whereas an expression such as ('a','b','c') would have the type of (Char, Char, Char). 4 == 5 will always return False, so its type is Bool.

          Functions also have types. When writing our own functions, we can choose to give them an explicit type declaration. This is generally considered to be good practice except when writing very short functions. From here on, we’ll give all the functions that we make type declarations. Remember the list comprehension we made previously that filters a string so that only caps remain? Here’s how it looks like with a type declaration.

          -
          +
          
           removeNonUppercase :: [Char] -> [Char]
           removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
          -
          +

          -removeNonUppercase has a type of [Char] -> [Char], meaning that it maps from a string to a string. That’s because it takes one string as a parameter and returns another as a result. The [Char] type is synonymous with String so it’s clearer if we write removeNonUppercase :: String -> String. We didn’t have to give this function a type declaration because the compiler can infer by itself that it’s a function from a string to a string but we did anyway. But how do we write out the type of a function that takes several parameters? Here’s a simple function that takes three integers and adds them together: +removeNonUppercase has a type of [Char] -> [Char], meaning that it maps from a string to a string. That’s because it takes one string as a parameter and returns another as a result. The [Char] type is synonymous with String so it’s clearer if we write removeNonUppercase :: String -> String. We didn’t have to give this function a type declaration because the compiler can infer by itself that it’s a function from a string to a string but we did anyway. But how do we write out the type of a function that takes several parameters? Here’s a simple function that takes three integers and adds them together:

          -
          +
          
           addThree :: Int -> Int -> Int -> Int
           addThree x y z = x + y + z
          -
          -

          The parameters are separated with -> and there’s no special distinction between the parameters and the return type. The return type is the last item in the declaration and the parameters are the first three. Later on we’ll see why they’re all just separated with -> instead of having some more explicit distinction between the return types and the parameters like Int, Int, Int -> Int or something. +

          +

          The parameters are separated with -> and there’s no special distinction between the parameters and the return type. The return type is the last item in the declaration and the parameters are the first three. Later on we’ll see why they’re all just separated with -> instead of having some more explicit distinction between the return types and the parameters like Int, Int, Int -> Int or something.

          -

          If you want to give your function a type declaration but are unsure as to what it should be, you can always just write the function without it and then check it with :t. Functions are expressions too, so :t works on them without a problem.

          +

          If you want to give your function a type declaration but are unsure as to what it should be, you can always just write the function without it and then check it with :t. Functions are expressions too, so :t works on them without a problem.

          Here’s an overview of some common types.

          -Int stands for integer. It’s used for whole numbers. 7 can be an Int but 7.2 cannot. Int is bounded, which means that it has a minimum and a maximum value. Usually on 32-bit machines the maximum possible Int is 2147483647 and the minimum is -2147483648. +Int stands for integer. It’s used for whole numbers. 7 can be an Int but 7.2 cannot. Int is bounded, which means that it has a minimum and a maximum value. Usually on 32-bit machines the maximum possible Int is 2147483647 and the minimum is -2147483648.

          -

          Integer stands for, er … also integer. The main difference is that it’s not bounded so it can be used to represent really really big numbers. I mean like really big. Int, however, is more efficient. +

          Integer stands for, er … also integer. The main difference is that it’s not bounded so it can be used to represent really really big numbers. I mean like really big. Int, however, is more efficient.

          -
          +
          
           factorial :: Integer -> Integer
           factorial n = product [1..n]
          -
          -
          +
          +
          
           ghci> factorial 50
           30414093201713378043612608166064768844377641568960512000000000000
          -
          -

          Float is a real floating point with single precision.

          -
          +
          +

          Float is a real floating point with single precision.

          +
          
           circumference :: Float -> Float
           circumference r = 2 * pi * r
          -
          -
          +
          +
          
           ghci> circumference 4.0
           25.132742
          -
          -

          Double is a real floating point with double the precision!

          -
          +
          +

          Double is a real floating point with double the precision!

          +
          
           circumference' :: Double -> Double
           circumference' r = 2 * pi * r
          -
          -
          +
          +
          
           ghci> circumference' 4.0
           25.132741228718345
          -
          +

          -Bool is a boolean type. It can have only two values: True and False. +Bool is a boolean type. It can have only two values: True and False.

          -Char represents a character. It’s denoted by single quotes. A list of characters is a string. +Char represents a character. It’s denoted by single quotes. A list of characters is a string.

          -

          Tuples are types but they are dependent on their length as well as the types of their components, so there is theoretically an infinite number of tuple types, which is too many to cover in this tutorial. Note that the empty tuple () is also a type which can only have a single value: ()

          +

          Tuples are types but they are dependent on their length as well as the types of their components, so there is theoretically an infinite number of tuple types, which is too many to cover in this tutorial. Note that the empty tuple () is also a type which can only have a single value: ()

          Type variables

          -What do you think is the type of the head function? Because head takes a list of any type and returns the first element, so what could it be? Let’s check! +What do you think is the type of the head function? Because head takes a list of any type and returns the first element, so what could it be? Let’s check!

          -
          +
          
           ghci> :t head
           head :: [a] -> a
          -
          +

          box -Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means that a can be of any type. This is much like generics in other languages, only in Haskell it’s much more powerful because it allows us to easily write very general functions if they don’t use any specific behavior of the types in them. Functions that have type variables are called polymorphic functions. The type declaration of head states that it takes a list of any type and returns one element of that type. +Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means that a can be of any type. This is much like generics in other languages, only in Haskell it’s much more powerful because it allows us to easily write very general functions if they don’t use any specific behavior of the types in them. Functions that have type variables are called polymorphic functions. The type declaration of head states that it takes a list of any type and returns one element of that type.

          Although type variables can have names longer than one character, we usually give them names of a, b, c, d …

          -

          Remember fst? It returns the first component of a pair. Let’s examine its type.

          -
          +

          Remember fst? It returns the first component of a pair. Let’s examine its type.

          +
          
           ghci> :t fst
           fst :: (a, b) -> a
          -
          +

          -We see that fst takes a tuple which contains two types and returns an element which is of the same type as the pair’s first component. That’s why we can use fst on a pair that contains any two types. Note that just because a and b are different type variables, they don’t have to be different types. It just states that the first component’s type and the return value’s type are the same. +We see that fst takes a tuple which contains two types and returns an element which is of the same type as the pair’s first component. That’s why we can use fst on a pair that contains any two types. Note that just because a and b are different type variables, they don’t have to be different types. It just states that the first component’s type and the return value’s type are the same.

          Typeclasses 101

          class

          A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages. Well, they’re not. You can think of them kind of as Java interfaces, only better.

          -

          What’s the type signature of the == function?

          -
          +

          What’s the type signature of the == function?

          +
          
           ghci> :t (==)
           (==) :: (Eq a) => a -> a -> Bool
          -
          -

          Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.

          -

          Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

          -

          The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.

          -

          The elem function has a type of (Eq a) => a -> [a] -> Bool because it uses == over a list to check whether some value we’re looking for is in it.

          +
          +

          Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.

          +

          Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

          +

          The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.

          +

          The elem function has a type of (Eq a) => a -> [a] -> Bool because it uses == over a list to check whether some value we’re looking for is in it.

          Some basic typeclasses:

          -

          Eq is used for types that support equality testing. The functions its members implement are == and /=. So if there’s an Eq class constraint for a type variable in a function, it uses == or /= somewhere inside its definition. All the types we mentioned previously except for functions are part of Eq, so they can be tested for equality.

          -
          +

          Eq is used for types that support equality testing. The functions its members implement are == and /=. So if there’s an Eq class constraint for a type variable in a function, it uses == or /= somewhere inside its definition. All the types we mentioned previously except for functions are part of Eq, so they can be tested for equality.

          +
          
           ghci> 5 == 5
           True
           ghci> 5 /= 5
          @@ -157,17 +157,17 @@ 

          Typeclasses 101

          True ghci> 3.432 == 3.432 True -
          +

          -Ord is for types that have an ordering.

          -
          +Ord is for types that have an ordering.

          +
          
           ghci> :t (>)
           (>) :: (Ord a) => a -> a -> Bool
          -
          +

          -All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively.

          -

          To be a member of Ord, a type must first have membership in the prestigious and exclusive Eq club.

          -
          +All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively.

          +

          To be a member of Ord, a type must first have membership in the prestigious and exclusive Eq club.

          +
          
           ghci> "Abrakadabra" < "Zebra"
           True
           ghci> "Abrakadabra" `compare` "Zebra"
          @@ -176,18 +176,18 @@ 

          Typeclasses 101

          True ghci> 5 `compare` 3 GT -
          -

          Members of Show can be presented as strings. All types covered so far except for functions are a part of Show. The most used function that deals with the Show typeclass is show. It takes a value whose type is a member of Show and presents it to us as a string.

          -
          +
          +

          Members of Show can be presented as strings. All types covered so far except for functions are a part of Show. The most used function that deals with the Show typeclass is show. It takes a value whose type is a member of Show and presents it to us as a string.

          +
          
           ghci> show 3
           "3"
           ghci> show 5.334
           "5.334"
           ghci> show True
           "True"
          -
          -

          Read is sort of the opposite typeclass of Show. The read function takes a string and returns a type which is a member of Read.

          -
          +
          +

          Read is sort of the opposite typeclass of Show. The read function takes a string and returns a type which is a member of Read.

          +
          
           ghci> read "True" || False
           True
           ghci> read "8.2" + 3.8
          @@ -196,24 +196,24 @@ 

          Typeclasses 101

          3 ghci> read "[1,2,3,4]" ++ [3] [1,2,3,4,3] -
          -

          So far so good. Again, all types covered so far are in this typeclass. But what happens if we try to do just read "4"?

          -
          +
          +

          So far so good. Again, all types covered so far are in this typeclass. But what happens if we try to do just read "4"?

          +
          
           ghci> read "4"
           <interactive>:1:0:
               Ambiguous type variable `a' in the constraint:
                 `Read a' arising from a use of `read' at <interactive>:1:0-7
               Probable fix: add a type signature that fixes these type variable(s)
          -
          -

          What GHCI is telling us here is that it doesn’t know what we want in return. Notice that in the previous uses of read we did something with the result afterwards. That way, GHCI could infer what kind of result we wanted out of our read. If we used it as a boolean, it knew it had to return a Bool. But now, it knows we want some type that is part of the Read class, it just doesn’t know which one. Let’s take a look at the type signature of read.

          -
          +
          +

          What GHCI is telling us here is that it doesn’t know what we want in return. Notice that in the previous uses of read we did something with the result afterwards. That way, GHCI could infer what kind of result we wanted out of our read. If we used it as a boolean, it knew it had to return a Bool. But now, it knows we want some type that is part of the Read class, it just doesn’t know which one. Let’s take a look at the type signature of read.

          +
          
           ghci> :t read
           read :: (Read a) => String -> a
          -
          +

          -See? It returns a type that’s part of Read but if we don’t try to use it in some way later, it has no way of knowing which type. That’s why we can use explicit type annotations. Type annotations are a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type. Observe: +See? It returns a type that’s part of Read but if we don’t try to use it in some way later, it has no way of knowing which type. That’s why we can use explicit type annotations. Type annotations are a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type. Observe:

          -
          +
          
           ghci> read "5" :: Int
           5
           ghci> read "5" :: Float
          @@ -224,10 +224,10 @@ 

          Typeclasses 101

          [1,2,3,4] ghci> read "(3, 'a')" :: (Int, Char) (3, 'a') -
          -

          Most expressions are such that the compiler can infer what their type is by itself. But sometimes, the compiler doesn’t know whether to return a value of type Int or Float for an expression like read "5". To see what the type is, Haskell would have to actually evaluate read "5". But since Haskell is a statically typed language, it has to know all the types before the code is compiled (or in the case of GHCI, evaluated). So we have to tell Haskell: “Hey, this expression should have this type, in case you don’t know!”.

          -

          Enum members are sequentially ordered types — they can be enumerated. The main advantage of the Enum typeclass is that we can use its types in list ranges. They also have defined successors and predecessors, which you can get with the succ and pred functions. Types in this class: (), Bool, Char, Ordering, Int, Integer, Float and Double.

          -
          +
          +

          Most expressions are such that the compiler can infer what their type is by itself. But sometimes, the compiler doesn’t know whether to return a value of type Int or Float for an expression like read "5". To see what the type is, Haskell would have to actually evaluate read "5". But since Haskell is a statically typed language, it has to know all the types before the code is compiled (or in the case of GHCI, evaluated). So we have to tell Haskell: “Hey, this expression should have this type, in case you don’t know!”.

          +

          Enum members are sequentially ordered types — they can be enumerated. The main advantage of the Enum typeclass is that we can use its types in list ranges. They also have defined successors and predecessors, which you can get with the succ and pred functions. Types in this class: (), Bool, Char, Ordering, Int, Integer, Float and Double.

          +
          
           ghci> ['a'..'e']
           "abcde"
           ghci> [LT .. GT]
          @@ -236,9 +236,9 @@ 

          Typeclasses 101

          [3,4,5] ghci> succ 'B' 'C' -
          -

          Bounded members have an upper and a lower bound.

          -
          +
          +

          Bounded members have an upper and a lower bound.

          +
          
           ghci> minBound :: Int
           -2147483648
           ghci> maxBound :: Char
          @@ -247,20 +247,20 @@ 

          Typeclasses 101

          True ghci> minBound :: Bool False -
          -

          minBound and maxBound are interesting because they have a type of (Bounded a) => a. In a sense they are polymorphic constants.

          -

          All tuples are also part of Bounded if the components are also in it.

          -
          +
          +

          minBound and maxBound are interesting because they have a type of (Bounded a) => a. In a sense they are polymorphic constants.

          +

          All tuples are also part of Bounded if the components are also in it.

          +
          
           ghci> maxBound :: (Bool, Int, Char)
           (True,2147483647,'\1114111')
          -
          -

          Num is a numeric typeclass. Its members have the property of being able to act like numbers. Let’s examine the type of a number.

          -
          +
          +

          Num is a numeric typeclass. Its members have the property of being able to act like numbers. Let’s examine the type of a number.

          +
          
           ghci> :t 20
           20 :: (Num t) => t
          -
          -

          It appears that whole numbers are also polymorphic constants. They can act like any type that’s a member of the Num typeclass.

          -
          +
          +

          It appears that whole numbers are also polymorphic constants. They can act like any type that’s a member of the Num typeclass.

          +
          
           ghci> 20 :: Int
           20
           ghci> 20 :: Integer
          @@ -269,20 +269,20 @@ 

          Typeclasses 101

          20.0 ghci> 20 :: Double 20.0 -
          -

          Those are types that are in the Num typeclass. If we examine the type of *, we’ll see that it accepts all numbers.

          -
          +
          +

          Those are types that are in the Num typeclass. If we examine the type of *, we’ll see that it accepts all numbers.

          +
          
           ghci> :t (*)
           (*) :: (Num a) => a -> a -> a
          -
          +

          -It takes two numbers of the same type and returns a number of that type. That’s why (5 :: Int) * (6 :: Integer) will result in a type error whereas 5 * (6 :: Integer) will work just fine and produce an Integer because 5 can act like an Integer or an Int. +It takes two numbers of the same type and returns a number of that type. That’s why (5 :: Int) * (6 :: Integer) will result in a type error whereas 5 * (6 :: Integer) will work just fine and produce an Integer because 5 can act like an Integer or an Int.

          -

          To join Num, a type must already be friends with Show and Eq.

          -

          Integral is also a numeric typeclass. Num includes all numbers, including real numbers and integral numbers, Integral includes only integral (whole) numbers. In this typeclass are Int and Integer.

          -

          Floating includes only floating point numbers, so Float and Double.

          -

          A very useful function for dealing with numbers is fromIntegral. It has a type declaration of fromIntegral :: (Num b, Integral a) => a -> b. From its type signature we see that it takes an integral number and turns it into a more general number. That’s useful when you want integral and floating point types to work together nicely. For instance, the length function has a type declaration of length :: [a] -> Int instead of having a more general type of (Num b) => length :: [a] -> b. I think that’s there for historical reasons or something, although in my opinion, it’s pretty stupid. Anyway, if we try to get a length of a list and then add it to 3.2, we’ll get an error because we tried to add together an Int and a floating point number. So to get around this, we do fromIntegral (length [1,2,3,4]) + 3.2 and it all works out.

          -

          Notice that fromIntegral has several class constraints in its type signature. That’s completely valid and as you can see, the class constraints are separated by commas inside the parentheses.

          +

          To join Num, a type must already be friends with Show and Eq.

          +

          Integral is also a numeric typeclass. Num includes all numbers, including real numbers and integral numbers, Integral includes only integral (whole) numbers. In this typeclass are Int and Integer.

          +

          Floating includes only floating point numbers, so Float and Double.

          +

          A very useful function for dealing with numbers is fromIntegral. It has a type declaration of fromIntegral :: (Num b, Integral a) => a -> b. From its type signature we see that it takes an integral number and turns it into a more general number. That’s useful when you want integral and floating point types to work together nicely. For instance, the length function has a type declaration of length :: [a] -> Int instead of having a more general type of (Num b) => length :: [a] -> b. I think that’s there for historical reasons or something, although in my opinion, it’s pretty stupid. Anyway, if we try to get a length of a list and then add it to 3.2, we’ll get an error because we tried to add together an Int and a floating point number. So to get around this, we do fromIntegral (length [1,2,3,4]) + 3.2 and it all works out.

          +

          Notice that fromIntegral has several class constraints in its type signature. That’s completely valid and as you can see, the class constraints are separated by commas inside the parentheses.

            diff --git a/docs/zippers.html b/docs/zippers.html index 93fd66d..15c7440 100644 --- a/docs/zippers.html +++ b/docs/zippers.html @@ -75,9 +75,9 @@

            Taking a walk

            let’s pick a seed that we will use to plant ours. Here it is:

            -
            +
            
             data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)
            -
            +

            So our tree is either empty or it’s a node that has an element and two @@ -85,7 +85,7 @@

            Taking a walk

            reader, for free!

            -
            +
            
             freeTree :: Tree Char
             freeTree =
                 Node 'P'
            @@ -109,7 +109,7 @@ 

            Taking a walk

            (Node 'C' Empty Empty) ) ) -
            +

            And here’s this tree represented graphically: @@ -118,39 +118,39 @@

            Taking a walk

            polly says her back hurts

            -Notice that W in the tree there? Say we want to -change it into a P. How would we go about doing that? +Notice that W in the tree there? Say we want to +change it into a P. How would we go about doing that? Well, one way would be to pattern match on our tree until we find the element that’s located by first going right and then left and changing said element. Here’s the code for this:

            -
            +
            
             changeToP :: Tree Char -> Tree Char
             changeToP (Node x l (Node y (Node _ m n) r)) = Node x l (Node y (Node 'P' m n) r)
            -
            +

            Yuck! Not only is this rather ugly, it’s also kind of confusing. What happens here? Well, we pattern match on our tree and name its root element -x (that’s becomes the 'P' in the -root) and its left subtree l. Instead of giving a +x (that’s becomes the 'P' in the +root) and its left subtree l. Instead of giving a name to its right subtree, we further pattern match on it. We continue this pattern matching until we reach the subtree whose root is our -'W'. Once we’ve done this, we rebuild the tree, only the -subtree that contained the 'W' at its root now -has a 'P'. +'W'. Once we’ve done this, we rebuild the tree, only the +subtree that contained the 'W' at its root now +has a 'P'.

            Is there a better way of doing this? How about we make our function take a tree along with a list of directions. The directions will be either -L or R, representing left and +L or R, representing left and right respectively, and we’ll change the element that we arrive at if we follow the supplied directions. Here it is:

            -
            +
            
             data Direction = L | R deriving (Show)
             type Directions = [Direction]
             
            @@ -158,17 +158,17 @@ 

            Taking a walk

            changeToP (L:ds) (Node x l r) = Node x (changeToP ds l) r changeToP (R:ds) (Node x l r) = Node x l (changeToP ds r) changeToP [] (Node _ l r) = Node 'P' l r -
            +

            If the first element in our list of directions is -L, we construct a new tree that’s like the old tree, only -its left subtree has an element changed to 'P'. When -we recursively call changeToP, we give it only the +L, we construct a new tree that’s like the old tree, only +its left subtree has an element changed to 'P'. When +we recursively call changeToP, we give it only the tail of the list of directions, because we already took a left. We do the same -thing in the case of an R. If the list of directions is +thing in the case of an R. If the list of directions is empty, that means that we’re at our destination, so we return a tree that’s like -the one supplied, only it has 'P' as its root +the one supplied, only it has 'P' as its root element.

            @@ -177,31 +177,31 @@

            Taking a walk

            directions and tells us what the element at the destination is:

            -
            +
            
             elemAt :: Directions -> Tree a -> a
             elemAt (L:ds) (Node _ l _) = elemAt ds l
             elemAt (R:ds) (Node _ _ r) = elemAt ds r
             elemAt [] (Node x _ _) = x
            -
            +

            -This function is actually quite similar to changeToP, +This function is actually quite similar to changeToP, only instead of remembering stuff along the way and reconstructing the tree, it ignores everything except its destination. Here we change the -'W' to a 'P' and see if the +'W' to a 'P' and see if the change in our new tree sticks:

            -
            +
            
             ghci> let newTree = changeToP [R,L] freeTree
             ghci> elemAt [R,L] newTree
             'P'
            -
            +

            Nice, this seems to work. In these functions, the list of directions acts as a sort of focus, because it pinpoints one exact subtree from our tree. -A direction list of [R] focuses on the subtree +A direction list of [R] focuses on the subtree that’s right of the root, for example. An empty direction list focuses on the main tree itself.

            @@ -237,80 +237,80 @@

            A trail of breadcrumbs

            To represent our breadcrumbs, we’ll also use a list of -Direction (which is either L -or R), only instead of calling it -Directions, we’ll call it Breadcrumbs +Direction (which is either L +or R), only instead of calling it +Directions, we’ll call it Breadcrumbs , because our directions will now be reversed since we’re leaving them as we go down our tree:

            -
            +
            
             type Breadcrumbs = [Direction]
            -
            +

            Here’s a function that takes a tree and some breadcrumbs and moves to the left -subtree while adding L to the head of the list that +subtree while adding L to the head of the list that represents our breadcrumbs:

            -
            +
            
             goLeft :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
             goLeft (Node _ l _, bs) = (l, L:bs)
            -
            +

            We ignore the element at the root and the right subtree and just return the -left subtree along with the old breadcrumbs with L +left subtree along with the old breadcrumbs with L as the head. Here’s a function to go right:

            -
            +
            
             goRight :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
             goRight (Node _ _ r, bs) = (r, R:bs)
            -
            +

            It works the same way. Let’s use these functions to take our -freeTree and go right and then left: +freeTree and go right and then left:

            -
            +
            
             ghci> goLeft (goRight (freeTree, []))
             (Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
            -
            +
            almostthere

            -Okay, so now we have a tree that has 'W' -in its root and 'C' in the root of its left subtree -and 'R' in the root of its right subtree. The -breadcrumbs are [L,R], because we first went right +Okay, so now we have a tree that has 'W' +in its root and 'C' in the root of its left subtree +and 'R' in the root of its right subtree. The +breadcrumbs are [L,R], because we first went right and then left.

            To make walking along our tree clearer, we can use the --: function that we defined like so: +-: function that we defined like so:

            -
            +
            
             x -: f = f x
            -
            +

            Which allows us to apply functions to values by first writing the value, then -writing a -: and then the function. So instead of -goRight (freeTree, []), we can write -(freeTree, []) -: goRight. Using this, we can rewrite +writing a -: and then the function. So instead of +goRight (freeTree, []), we can write +(freeTree, []) -: goRight. Using this, we can rewrite the above so that it’s more apparent that we’re first going right and then left:

            -
            +
            
             ghci> (freeTree, []) -: goRight -: goLeft
             (Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
            -
            +

            Going back up

            @@ -337,18 +337,18 @@

            Going back up

            Let’s modify our breadcrumbs so that they also contain information about everything that we previously ignored when moving left and right. Instead of -Direction, we’ll make a new data type: +Direction, we’ll make a new data type:

            -
            +
            
             data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) deriving (Show)
            -
            +

            -Now, instead of just L, we have a -LeftCrumb that also contains the element in the node that we +Now, instead of just L, we have a +LeftCrumb that also contains the element in the node that we moved from and the right tree that we didn’t visit. Instead of -R, we have RightCrumb, which +R, we have RightCrumb, which contains the element in the node that we moved from and the left tree that we didn’t visit.

            @@ -365,90 +365,90 @@

            Going back up

            move deeper into a tree, the breadcrumb carries all the information that the node that we moved away from carried except the subtree that we chose to focus on. It also has to note where the hole is. In the case of a -LeftCrumb, we know that we moved left, so the +LeftCrumb, we know that we moved left, so the subtree that’s missing is the left one.

            -Let’s also change our Breadcrumbs type synonym to +Let’s also change our Breadcrumbs type synonym to reflect this:

            -
            +
            
             type Breadcrumbs a = [Crumb a]
            -
            +

            -Next up, we have to modify the goLeft and -goRight functions to store information about the +Next up, we have to modify the goLeft and +goRight functions to store information about the paths that we didn’t take in our breadcrumbs, instead of ignoring that -information like they did before. Here’s goLeft: +information like they did before. Here’s goLeft:

            -
            +
            
             goLeft :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
             goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
            -
            +

            -You can see that it’s very similar to our previous goLeft, -only instead of just adding a L to the head of our -list of breadcrumbs, we add a LeftCrumb to signify -that we went left and we equip our LeftCrumb with the -element in the node that we moved from (that’s the x) +You can see that it’s very similar to our previous goLeft, +only instead of just adding a L to the head of our +list of breadcrumbs, we add a LeftCrumb to signify +that we went left and we equip our LeftCrumb with the +element in the node that we moved from (that’s the x) and the right subtree that we chose not to visit.

            Note that this function assumes that the current tree that’s under focus isn’t -Empty. An empty tree doesn’t have any subtrees, so +Empty. An empty tree doesn’t have any subtrees, so if we try to go left from an empty tree, an error will occur because the pattern -match on Node won’t succeed and there’s no pattern -that takes care of Empty. +match on Node won’t succeed and there’s no pattern +that takes care of Empty.

            -goRight is similar: +goRight is similar:

            -
            +
            
             goRight :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
             goRight (Node x l r, bs) = (r, RightCrumb x l:bs)
            -
            +

            We were previously able to go left and right. What we’ve gotten now is the ability to actually go back up by remembering stuff about the parent nodes and -the paths that we didn’t visit. Here’s the goUp +the paths that we didn’t visit. Here’s the goUp function:

            -
            +
            
             goUp :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
             goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)
             goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
            -
            +
            asstronaut

            -We’re focusing on the tree t and we check what the -latest Crumb is. If it’s a LeftCrumb, -then we construct a new tree where our tree t is the left +We’re focusing on the tree t and we check what the +latest Crumb is. If it’s a LeftCrumb, +then we construct a new tree where our tree t is the left subtree and we use the information about the right subtree that we didn’t -visit and the element to fill out the rest of the Node. +visit and the element to fill out the rest of the Node. Because we moved back so to speak and picked up the last breadcrumb to recreate with it the parent tree, the new list of breadcrumbs doesn’t contain it.

            Note that this function causes an error if we’re already at the top of a tree -and we want to move up. Later on, we’ll use the Maybe +and we want to move up. Later on, we’ll use the Maybe monad to represent possible failure when moving focus.

            -With a pair of Tree a and Breadcrumbs a, +With a pair of Tree a and Breadcrumbs a, we have all the information to rebuild the whole tree and we also have a focus on a subtree. This scheme also enables us to easily move up, left and right. Such a pair that contains a focused part of a data structure and its surroundings is @@ -457,15 +457,15 @@

            Going back up

            synonym as such:

            -
            +
            
             type Zipper a = (Tree a, Breadcrumbs a)
            -
            +

            -I’d prefer naming the type synonym Focus because that +I’d prefer naming the type synonym Focus because that makes it clearer that we’re focusing on a part of a data structure, but the term zipper is more widely used to describe such a setup, so we’ll stick with -Zipper. +Zipper.

            Manipulating trees under focus

            @@ -475,50 +475,50 @@

            Manipulating trees under focus

            modifies the element in the root of the subtree that the zipper is focusing on:

            -
            +
            
             modify :: (a -> a) -> Zipper a -> Zipper a
             modify f (Node x l r, bs) = (Node (f x) l r, bs)
             modify f (Empty, bs) = (Empty, bs)
            -
            +

            If we’re focusing on a node, we modify its root element with the function -f. If we’re focusing on an empty tree, we leave it as +f. If we’re focusing on an empty tree, we leave it as it is. Now we can start off with a tree, move to anywhere we want and modify an element, all while keeping focus on that element so that we can easily move further up or down. An example:

            -
            +
            
             ghci> let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))
            -
            +

            We go left, then right and then modify the root element by replacing it with a -'P'. This reads even better if we use --:: +'P'. This reads even better if we use +-::

            -
            +
            
             ghci> let newFocus = (freeTree,[]) -: goLeft -: goRight -: modify (\_ -> 'P')
            -
            +

            We can then move up if we want and replace an -element with a mysterious 'X': +element with a mysterious 'X':

            -
            +
            
             ghci> let newFocus2 = modify (\_ -> 'X') (goUp newFocus)
            -
            +

            -Or if we wrote it with -:: +Or if we wrote it with -::

            -
            +
            
             ghci> let newFocus2 = newFocus -: goUp -: modify (\_ -> 'X')
            -
            +

            Moving up is easy because the breadcrumbs that we leave form the part of the @@ -535,29 +535,29 @@

            Manipulating trees under focus

            simple:

            -
            +
            
             attach :: Tree a -> Zipper a -> Zipper a
             attach t (_, bs) = (t, bs)
            -
            +

            We take a tree and a zipper and return a new zipper that has its focus replaced with the supplied tree. Not only can we extend trees this way by replacing empty subtrees with new trees, we can also replace whole existing subtrees. Let’s -attach a tree to the far left of our freeTree: +attach a tree to the far left of our freeTree:

            -
            +
            
             ghci> let farLeft = (freeTree,[]) -: goLeft -: goLeft -: goLeft -: goLeft
             ghci> let newFocus = farLeft -: attach (Node 'Z' Empty Empty)
            -
            +

            -newFocus is now focused on the tree that we just +newFocus is now focused on the tree that we just attached and the rest of the tree lies inverted in the breadcrumbs. If we were -to use goUp to walk all the way to the top of the -tree, it would be the same tree as freeTree but with -an additional 'Z' on its far left. +to use goUp to walk all the way to the top of the +tree, it would be the same tree as freeTree but with +an additional 'Z' on its far left.

            I’m going straight to the top, oh yeah, up where the air is fresh and clean!

            @@ -567,20 +567,20 @@

            +
            
             topMost :: Zipper a -> Zipper a
             topMost (t,[]) = (t,[])
             topMost z = topMost (goUp z)
            -
            +

      If our trail of beefed up breadcrumbs is empty, this means that we’re already at the root of our tree, so we just return the current focus. Otherwise, we go up to get the focus of the parent node and then recursively apply -topMost to that. So now we can walk around our tree, going -left and right and up, applying modify and -attach as we go along and then when we’re done with -our modifications, we use topMost to focus on the +topMost to that. So now we can walk around our tree, going +left and right and up, applying modify and +attach as we go along and then when we’re done with +our modifications, we use topMost to focus on the root of our tree and see the changes that we’ve done in proper perspective.

      @@ -596,9 +596,9 @@

      Focusing on lists

      our own lists, we defined our data type like so:

      -
      +
      
       data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
      -
      +
      the best damn thing @@ -608,14 +608,14 @@

      Focusing on lists

      -A list like [1,2,3] can be written as -1:2:3:[]. It consists of the head of the list, which -is 1 and then the list’s tail, which is -2:3:[]. In turn, 2:3:[] -also has a head, which is 2 and a tail, which is -3:[]. With 3:[], the -3 is the head and the tail is the empty list -[]. +A list like [1,2,3] can be written as +1:2:3:[]. It consists of the head of the list, which +is 1 and then the list’s tail, which is +2:3:[]. In turn, 2:3:[] +also has a head, which is 2 and a tail, which is +3:[]. With 3:[], the +3 is the head and the tail is the empty list +[].

      @@ -635,21 +635,21 @@

      Focusing on lists

      went left or right, because there’s only one way to go deeper into a list. Because there’s only one subtree to each node, we don’t have to remember the paths that we didn’t take either. It seems that all we have to remember is the -previous element. If we have a list like [3,4,5] and -we know that the previous element was 2, we can go +previous element. If we have a list like [3,4,5] and +we know that the previous element was 2, we can go back by just putting that element at the head of our list, getting -[2,3,4,5]. +[2,3,4,5].

      Because a single breadcrumb here is just the element, we don’t really have to put it inside a data type, like we did when we made the -Crumb data type for tree zippers: +Crumb data type for tree zippers:

      -
      +
      
       type ListZipper a = ([a],[a])
      -
      +

      The first list represents the list that we’re focusing on and the second list @@ -657,13 +657,13 @@

      Focusing on lists

      into lists:

      -
      +
      
       goForward :: ListZipper a -> ListZipper a
       goForward (x:xs, bs) = (xs, x:bs)
       
       goBack :: ListZipper a -> ListZipper a
       goBack (xs, b:bs) = (b:xs, bs)
      -
      +

      When we’re going forward, we focus on the tail of the current list and leave the @@ -675,7 +675,7 @@

      Focusing on lists

      Here are these two functions in action:

      -
      +
      
       ghci> let xs = [1,2,3,4]
       ghci> goForward (xs,[])
       ([2,3,4],[1])
      @@ -685,7 +685,7 @@ 

      Focusing on lists

      ([4],[3,2,1]) ghci> goBack ([4],[3,2,1]) ([3,4],[2,1]) -
      +

      We see that the breadcrumbs in the case of lists are nothing more but a reversed @@ -727,11 +727,11 @@

      A very simple file system

      type for this and some type synonyms so we know what’s what:

      -
      +
      
       type Name = String
       type Data = String
       data FSItem = File Name Data | Folder Name [FSItem] deriving (Show)
      -
      +

      A file comes with two strings, which represent its name and the data it holds. A @@ -743,7 +743,7 @@

      A very simple file system

      Here’s a folder with some files and sub-folders:

      -
      +
      
       myDisk :: FSItem
       myDisk =
           Folder "root"
      @@ -765,7 +765,7 @@ 

      A very simple file system

      ] ] ] -
      +

      That’s actually what my disk contains right now. @@ -794,12 +794,12 @@

      A zipper for our file system

      -If we’re focusing on the folder "root" and we then -focus on the file "dijon_poupon.doc", what should the +If we’re focusing on the folder "root" and we then +focus on the file "dijon_poupon.doc", what should the breadcrumb that we leave look like? Well, it should contain the name of its parent folder along with the items that come before the file that we’re focusing on and the items that come after it. So all we need is a -Name and two lists of items. By keeping separate lists for +Name and two lists of items. By keeping separate lists for the items that come before the item that we’re focusing and for the items that come after it, we know exactly where to place it once we move back up. So this way, we know where the hole is. @@ -809,17 +809,17 @@

      A zipper for our file system

      Here’s our breadcrumb type for the file system:

      -
      +
      
       data FSCrumb = FSCrumb Name [FSItem] [FSItem] deriving (Show)
      -
      +

      And here’s a type synonym for our zipper:

      -
      +
      
       type FSZipper = (FSItem, [FSCrumb])
      -
      +

      Going back up in the hierarchy is very simple. We just take the latest @@ -827,24 +827,24 @@

      A zipper for our file system

      Like so:

      -
      +
      
       fsUp :: FSZipper -> FSZipper
       fsUp (item, FSCrumb name ls rs:bs) = (Folder name (ls ++ [item] ++ rs), bs)
      -
      +

      Because our breadcrumb knew what the parent folder’s name was, as well as the items that came before our focused item in the folder (that’s -ls) and the ones that came after (that’s -rs), moving up was easy. +ls) and the ones that came after (that’s +rs), moving up was easy.

      How about going deeper into the file system? If we’re in the -"root" and we want to focus on -"dijon_poupon.doc", the breadcrumb that we leave is going to -include the name "root" along with the items that -precede "dijon_poupon.doc" and the ones that come +"root" and we want to focus on +"dijon_poupon.doc", the breadcrumb that we leave is going to +include the name "root" along with the items that +precede "dijon_poupon.doc" and the ones that come after it.

      @@ -853,7 +853,7 @@

      A zipper for our file system

      in the current focused folder:

      -
      +
      
       import Data.List (break)
       
       fsTo :: Name -> FSZipper -> FSZipper
      @@ -864,12 +864,12 @@ 

      A zipper for our file system

      nameIs :: Name -> FSItem -> Bool nameIs name (Folder folderName _) = name == folderName nameIs name (File fileName _) = name == fileName -
      +

      -fsTo takes a Name and a -FSZipper -and returns a new FSZipper that focuses on the file +fsTo takes a Name and a +FSZipper +and returns a new FSZipper that focuses on the file with the given name. That file has to be in the current focused folder. This function doesn’t search all over the place, it just looks at the current folder.

      @@ -877,64 +877,64 @@

      A zipper for our file system

      wow cool great

      -First we use break to break the list of items in a +First we use break to break the list of items in a folder into those that precede the file that we’re searching for and those that -come after it. If you remember, break takes a +come after it. If you remember, break takes a predicate and a list and returns a pair of lists. The first list in the pair -holds items for which the predicate returns False. -Then, once the predicate returns True for an item, it +holds items for which the predicate returns False. +Then, once the predicate returns True for an item, it places that item and the rest of the list in the second item of the pair. We -made an auxilliary function called nameIs that takes -a name and a file system item and returns True if +made an auxilliary function called nameIs that takes +a name and a file system item and returns True if the names match.

      -So now, ls is a list that contains the items that -precede the item that we’re searching for, item is -that very item and rs is the list of items that come +So now, ls is a list that contains the items that +precede the item that we’re searching for, item is +that very item and rs is the list of items that come after it in its folder. Now that we have this, we just present the item that we -got from break as the focus and build a breadcrumb +got from break as the focus and build a breadcrumb that has all the data it needs.

      Note that if the name we’re looking for isn’t in the folder, the pattern -item:rs will try to match on an empty list and we’ll +item:rs will try to match on an empty list and we’ll get an error. Also, if our current focus isn’t a folder at all but a file, we get an error as well and the program crashes.

      Now we can move up and down our file system. Let’s start at the root and walk to -the file "skull_man(scary).bmp": +the file "skull_man(scary).bmp":

      -
      +
      
       ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsTo "skull_man(scary).bmp"
      -
      +

      -newFocus is now a zipper that’s focused on the -"skull_man(scary).bmp" file. Let’s get the first +newFocus is now a zipper that’s focused on the +"skull_man(scary).bmp" file. Let’s get the first component of the zipper (the focus itself) and see if that’s really true:

      -
      +
      
       ghci> fst newFocus
       File "skull_man(scary).bmp" "Yikes!"
      -
      +

      Let’s move up and then focus on its neighboring file -"watermelon_smash.gif": +"watermelon_smash.gif":

      -
      +
      
       ghci> let newFocus2 = newFocus -: fsUp -: fsTo "watermelon_smash.gif"
       ghci> fst newFocus2
       File "watermelon_smash.gif" "smash!!"
      -
      +

      Manipulating our file system

      @@ -943,23 +943,23 @@

      Manipulating our file system

      Here’s a function that renames the currently focused file or folder:

      -
      +
      
       fsRename :: Name -> FSZipper -> FSZipper
       fsRename newName (Folder name items, bs) = (Folder newName items, bs)
       fsRename newName (File name dat, bs) = (File newName dat, bs)
      -
      +

      -Now we can rename our "pics" folder to -"cspi": +Now we can rename our "pics" folder to +"cspi":

      -
      +
      
       ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsRename "cspi" -: fsUp
      -
      +

      -We descended to the "pics" folder, renamed it and +We descended to the "pics" folder, renamed it and then moved back up.

      @@ -967,11 +967,11 @@

      Manipulating our file system

      How about a function that makes a new item in the current folder? Behold:

      -
      +
      
       fsNewFile :: FSItem -> FSZipper -> FSZipper
       fsNewFile item (Folder folderName items, bs) =
           (Folder folderName (item:items), bs)
      -
      +

      Easy as pie. Note that this would crash if we tried to add an item but weren’t @@ -979,19 +979,19 @@

      Manipulating our file system

      -Let’s add a file to our "pics" folder and then move +Let’s add a file to our "pics" folder and then move back up to the root:

      -
      +
      
       ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsNewFile (File "heh.jpg" "lol") -: fsUp
      -
      +

      What’s really cool about all this is that when we modify our file system, it doesn’t actually modify it in place but it returns a whole new file system. That way, we -have access to our old file system (in this case, myDisk) -as well as the new one (the first component of newFocus). +have access to our old file system (in this case, myDisk) +as well as the new one (the first component of newFocus). So by using zippers, we get versioning for free, meaning that we can always refer to older versions of data structures even after we’ve changed them, so to speak. This isn’t unique to zippers, but is a property of Haskell because its @@ -1006,20 +1006,20 @@

      Watch your step

      So far, while walking through our data structures, whether they were binary trees, lists or file systems, we didn’t really care if we took a step too far -and fell off. For instance, our goLeft function takes +and fell off. For instance, our goLeft function takes a zipper of a binary tree and moves the focus to its left subtree:

      -
      +
      
       goLeft :: Zipper a -> Zipper a
       goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
      -
      +
      falling for you

      But what if the tree we’re stepping off from is an empty tree? That is, what if -it’s not a Node, but an Empty? +it’s not a Node, but an Empty? In this case, we’d get a runtime error because the pattern match would fail and we have made no pattern to handle an empty tree, which doesn’t have any subtrees at all. So far, we just assumed that we’d never try to focus on the @@ -1034,22 +1034,22 @@

      Watch your step

      that when using zippers, any step could be our last (cue ominous music). In other words, any move can result in a success, but it can also result in a failure. Does that remind you of something? Of course, monads! More -specifically, the Maybe monad which adds a context of +specifically, the Maybe monad which adds a context of possible failure to normal values.

      -So let’s use the Maybe monad to add a context of +So let’s use the Maybe monad to add a context of possible failure to our movements. We’re going to take the functions that work on our binary tree zipper and we’re going to make them into monadic functions. -First, let’s take care of possible failure in goLeft -and goRight. So far, the failure of functions that +First, let’s take care of possible failure in goLeft +and goRight. So far, the failure of functions that could fail was always reflected in their result, and this time is no -different. So here are goLeft and -goRight with an added possibility of failure: +different. So here are goLeft and +goRight with an added possibility of failure:

      -
      +
      
       goLeft :: Zipper a -> Maybe (Zipper a)
       goLeft (Node x l r, bs) = Just (l, LeftCrumb x r:bs)
       goLeft (Empty, _) = Nothing
      @@ -1057,43 +1057,43 @@ 

      Watch your step

      goRight :: Zipper a -> Maybe (Zipper a) goRight (Node x l r, bs) = Just (r, RightCrumb x l:bs) goRight (Empty, _) = Nothing -
      +

      Cool, now if we try to take a step to the left of an empty tree, we get a -Nothing! +Nothing!

      -
      +
      
       ghci> goLeft (Empty, [])
       Nothing
       ghci> goLeft (Node 'A' Empty Empty, [])
       Just (Empty,[LeftCrumb 'A' Empty])
      -
      +

      Looks good! How about going up? The problem before happened if we tried to go up but we didn’t have any more breadcrumbs, which meant that we were already in the -root of the tree. This is the goUp function that +root of the tree. This is the goUp function that throws an error if we don’t keep within the bounds of our tree:

      -
      +
      
       goUp :: Zipper a -> Zipper a
       goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)
       goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
      -
      +

      Now let’s modify it to fail gracefully:

      -
      +
      
       goUp :: Zipper a -> Maybe (Zipper a)
       goUp (t, LeftCrumb x r:bs) = Just (Node x t r, bs)
       goUp (t, RightCrumb x l:bs) = Just (Node x l t, bs)
       goUp (_, []) = Nothing
      -
      +

      If we have breadcrumbs, everything is okay and we return a successful new focus, @@ -1105,13 +1105,13 @@

      Watch your step

      could chain them like this to walk around:

      -
      +
      
       gchi> let newFocus = (freeTree,[]) -: goLeft -: goRight
      -
      +

      -But now, instead of returning Zipper a, they return -Maybe (Zipper a), so chaining functions like this +But now, instead of returning Zipper a, they return +Maybe (Zipper a), so chaining functions like this won’t work. We had a similar problem when we were dealing with our tightrope walker in the chapter about monads. He also walked one step at a time and @@ -1124,16 +1124,16 @@

      Watch your step

      we’re the ones doing the walking, and we’re traversing a labyrinth of our own devising. Luckily, we can learn from the tightrope walker and just do what he did, which is to exchange normal -function application for using >>=, which takes -a value with a context (in our case, the Maybe (Zipper a), +function application for using >>=, which takes +a value with a context (in our case, the Maybe (Zipper a), which has a context of possible failure) and feeds it into a function while making sure that the context is taken care of. So just like our tightrope walker, we’re -going to trade in all our -: operators for ->>=. Alright, we can chain our functions again! +going to trade in all our -: operators for +>>=. Alright, we can chain our functions again! Watch:

      -
      +
      
       ghci> let coolTree = Node 1 Empty (Node 3 Empty Empty)
       ghci> return (coolTree,[]) >>= goRight
       Just (Node 3 Empty Empty,[RightCrumb 1 Empty])
      @@ -1141,18 +1141,18 @@ 

      Watch your step

      Just (Empty,[RightCrumb 3 Empty,RightCrumb 1 Empty]) ghci> return (coolTree,[]) >>= goRight >>= goRight >>= goRight Nothing -
      +

      -We used return to put a zipper in a -Just and then used >>= to -feed that to our goRight function. First, we made a +We used return to put a zipper in a +Just and then used >>= to +feed that to our goRight function. First, we made a tree that has on its left an empty subtree and on its right a node that has two empty subtrees. When we try to go right once, the result is a success, because the operation makes sense. Going right twice is okay too; we end up with the focus on an empty subtree. But going right three times wouldn’t make sense, because we can’t go to the right of an empty subtree, which is why the -result is a Nothing. +result is a Nothing.

      @@ -1164,7 +1164,7 @@

      Watch your step

      Our file system also has a lot of cases where an operation could fail, such as trying to focus on a file or folder that doesn’t exist. As an exercise, you can equip our file system with functions that fail gracefully by using the -Maybe monad. +Maybe monad.

        From 14e504c4f50138c491de7e0b53c577b7a5e95e04 Mon Sep 17 00:00:00 2001 From: Gregory Cox Date: Sun, 27 Nov 2022 14:29:27 +0900 Subject: [PATCH 27/27] Do all remaining changes to replace existing HTML with markdown-generated HTML --- docs/a-fistful-of-monads.html | 3004 +++++------- docs/chapters.html | 345 +- docs/faq.html | 48 +- docs/for-a-few-monads-more.html | 4233 ++++++----------- docs/functionally-solving-problems.html | 707 ++- ...tors-applicative-functors-and-monoids.html | 3856 ++++++++------- docs/higher-order-functions.html | 1152 +++-- docs/input-and-output.html | 2620 +++++++--- docs/introduction.html | 178 +- .../making-our-own-types-and-typeclasses.html | 2239 ++++++--- docs/modules.html | 1583 +++--- docs/recursion.html | 366 +- docs/starting-out.html | 963 ++-- docs/syntax-in-functions.html | 702 ++- docs/types-and-typeclasses.html | 500 +- docs/zippers.html | 1621 +++---- 16 files changed, 13046 insertions(+), 11071 deletions(-) diff --git a/docs/a-fistful-of-monads.html b/docs/a-fistful-of-monads.html index 204c440..b336165 100644 --- a/docs/a-fistful-of-monads.html +++ b/docs/a-fistful-of-monads.html @@ -31,357 +31,230 @@
      -

      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 +

      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
      +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, +[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 +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 +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")
      +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
      +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 +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
      +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
      +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
      +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)
      +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
      +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 +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. -

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

      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
      @@ -390,304 +263,191 @@ 

      The Monad type class

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

      - - +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 +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
      +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)
      +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)
      +(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
      +(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
      @@ -695,505 +455,310 @@ 

      Walk the line

      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)
      +    | 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)) anymore -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 +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)) anymore +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. +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 +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 +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’s 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 +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’s 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
      +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
      +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 + 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. -

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

      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 +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)))
      +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
      +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
      +      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 + 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 of the 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
      +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 of the 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 tightwalker’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 tightwalker -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
      +    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 tightwalker’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 tightwalker 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 + 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
      +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
      @@ -1201,934 +766,565 @@ 

      do notation

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

      - - + 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
      +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. + 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 voila! 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"]
      +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 voila! 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)]
      +[]
      +

      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
      +    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
      +    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 + 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 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 ()
      +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]
      +[]
      +

      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]
      +[]
      +

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

      - + 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]
      +

      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 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]
      +    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 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)
      +    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]
      +[(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. -

      - - + 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 -the if the laws hold. But don’t worry, they’re not complicated. -

      - + +

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

      - +

      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:

        -
      • return x >>= f is the same damn - thing as f x
      • +
      • return x >>= f is the same damn +thing as f x
      - -

      -If you look at monadic values as values with a context and +

      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))
      +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])
      +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. -

      - +["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: -

      - +

      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:

        -
      • m >>= return is no different than - just m
      • +
      • m >>= return is no different +than just m
      - -

      -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)
      +

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

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

      - +

      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:

        -
      • Doing (m >>= f) >>= g is just like - doing m >>= (\x -> f x >>= g)
      • +
      • Doing (m >>= f) >>= g is +just like doing m >>= (\x -> f x >>= g)
      - -

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

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

      +[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/chapters.html b/docs/chapters.html index e8a969c..faac4bd 100644 --- a/docs/chapters.html +++ b/docs/chapters.html @@ -16,156 +16,201 @@
        -

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

        +

        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 index f56e382..fd9f17f 100644 --- a/docs/faq.html +++ b/docs/faq.html @@ -16,19 +16,43 @@
        -

        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.

        +

        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.

        +

        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 index 9d96c5e..0eb0022 100644 --- a/docs/for-a-few-monads-more.html +++ b/docs/for-a-few-monads-more.html @@ -31,256 +31,165 @@
        -

        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 +

        For a Few Monads More

        + +

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

        - - +mtl, followed by a version number.

        Writer? I hardly know her!

        - -

        -We’ve loaded our gun with the Maybe monad, the list +

        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 +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
        +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 +(True,"Compared gang size to 9.")

        + +

        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 +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
        +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."))
        +(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. -

        - +(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 +

        +

        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]
        +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
        +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
        @@ -288,189 +197,120 @@ 

        Monoids to the rescue

        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
        +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 +("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). -

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

        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. + (Writer (x,v)) >>= f = let (Writer (y, v')) = f x in Writer (y, v `mappend` v')

        + +

        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)
        +"" 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 +(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
        +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])
        @@ -479,121 +319,77 @@ 

        Using do notation with Writer

        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 + 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
        +["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"])
        -
        - + 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
        +

        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
        +    | 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
        @@ -602,125 +398,77 @@ 

        Adding logging to programs

        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' 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)
        +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
        +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
        @@ -730,137 +478,77 @@ 

        Inefficient list construction

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

        - +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
        +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
        +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
        +    (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
        @@ -870,752 +558,471 @@ 

        Difference lists

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

        - +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) ()
        +

        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 + 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
        +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] ()
        +...
        +

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

        - - + 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 youre 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)
        +bang youre 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)
        +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 +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
        +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 + 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
        +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
        +    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. -

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

        - +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]
        +

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

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

        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 + 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
        +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
        +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
        +    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 ()
        +    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 ()
        +            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 ()
        +        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
        +        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
        +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)
        @@ -1623,260 +1030,155 @@ 

        Randomness and the state monad

        a <- randomSt b <- randomSt c <- randomSt - return (a,b,c) -
        - -

        -threeCoins is now a stateful computation and after + 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! -

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

        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
        +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 + 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
        +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)
        +"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)
        +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. -

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

        - +

        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 +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)
        +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
        @@ -1887,710 +1189,424 @@ 

        liftM and friends

        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
        +(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
        +    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. +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 +<*> 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, +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
        +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
        +    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 +[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. -

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

        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))
        +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 +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 +>>=, 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
        +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 on to the top. -

        - -

        -The implementation for join is as follows: -

        - -
        
        -join :: (Monad m) => m (m a) -> m a
        +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 on to 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
        +    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 + 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 >>=. -

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

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

        - +[]
        +

        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 +

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

        - +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 a 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 +i’ve found yellow! +

        When we were solving the problem of implementing +a 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
        +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]
        +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
        +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
        +                                _ -> 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]
        +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] "*"
        +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]
        @@ -2599,490 +1615,313 @@ 

        Making a safe RPN calculator

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

        - +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)
        +

        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)]
        +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 +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 +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
        +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
        -
        - - +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
        +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’use 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, +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’use 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 +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 +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)
        +'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
        +    ]
        +

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

        + (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 index c644f6d..04d6c6d 100644 --- a/docs/functionally-solving-problems.html +++ b/docs/functionally-solving-problems.html @@ -31,55 +31,185 @@
        -

        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 on to 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 on to 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 a 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.

        -

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

        +

        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 on to 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 on to 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 a 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.

        +
        +

        Protip: 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
        +
        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
        +    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
        +    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 on to 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 intial 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.

        + 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 on +to 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 intial 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 * -"
        +
        ghci> solveRPN "10 4 3 + 2 * -"
         -4
         ghci> solveRPN "2 3 +"
         5
        @@ -90,12 +220,19 @@ 

        Reverse Polish notation calculator

        -

        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
        +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
        @@ -106,34 +243,59 @@ 

        Reverse Polish notation calculator

        -

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

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

        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
        @@ -144,65 +306,201 @@ 

        Heathrow to London

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

        +0
        +

        To mentally parse the input file, read it in threes and mentally +split the road system into sections. Each section is comprised 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:

          -
        • Forget Haskell for a minute and think about how we’d solve the problem by hand
        • -
        • Think about how we’re going to represent our data in Haskell
        • -
        • Figure out how to operate on that data in Haskell so that we produce at a solution
        • +
        • Forget Haskell for a minute and think about how we’d solve the +problem by hand
        • +
        • Think about how we’re going to represent our data in Haskell
        • +
        • Figure out how to operate on that data in Haskell so that we produce +at a solution
        -

        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)
        +

        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
        @@ -216,46 +514,107 @@ 

        Heathrow to London

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

        + 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]]
        +
        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
        +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
        @@ -265,12 +624,22 @@ 

        Heathrow to London

        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
        +    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
        @@ -281,15 +650,17 @@ 

        Heathrow to London

        25 10 8 -0 -
        -

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

        -
        
        -$ cat paths.txt | runhaskell heathrow.hs
        +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.

        +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 index 991bc12..46d5b36 100644 --- a/docs/functors-applicative-functors-and-monoids.html +++ b/docs/functors-applicative-functors-and-monoids.html @@ -31,79 +31,239 @@
        -

        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, 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
        +
        +

        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
        +        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 $ "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
        +          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
        +          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))
        -
        +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))
        -
        +
        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)
        +

        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
        @@ -112,27 +272,96 @@ 

        Functors redux

        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)
        +"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.

        + +

        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]
        +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]
        @@ -141,13 +370,30 @@ 

        Functors redux

        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.

        +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)
        +
        ghci> fmap id (Just 3)
         Just 3
         ghci> id (Just 3)
         Just 3
        @@ -158,28 +404,83 @@ 

        Functors redux

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

        + +

        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"
        @@ -188,84 +489,243 @@ 

        Functors redux

        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
        +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")
        +    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")
        +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.

        +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")
        +
        +

        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]
        +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
        +[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 a 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
        +    (<*>) :: 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 a +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.

        + (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
        +
        ghci> Just (+3) <*> Just 9
         Just 12
         ghci> pure (+3) <*> Just 10
         Just 13
        @@ -274,215 +734,502 @@ 

        Applicative functors

        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
        +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
        -
        -whaale -

        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 occurences 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"
        -
        +Nothing
        +whaale +

        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 +occurences 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
        +
        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]
        +    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.

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

        + 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 :: 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
        +    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
        +    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)
        +    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
        +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]
        +        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])
        +[('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]
        +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.

        +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]
        +
        ghci> sequenceA [Just 3, Just 2, Just 1]
         Just [3,2,1]
         ghci> sequenceA [Just 3, Nothing, Just 1]
         Nothing
        @@ -491,30 +1238,64 @@ 

        Applicative functors

        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]
        +[]
        +

        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
        +

        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]]
        +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]]
        @@ -525,770 +1306,547 @@ 

        Applicative functors

        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.

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

          -
        • We start off with sequenceA [[1,2],[3,4]]
        • -
        • That evaluates to (:) <$> [1,2] <*> sequenceA [[3,4]]
        • -
        • Evaluating the inner sequenceA further, we get (:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])
        • -
        • We’ve reached the edge condition, so this is now (:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
        • -
        • Now, we evaluate the (:) <$> [3,4] <*> [[]] part, which will use : with every possible value in the left list (possible values are 3 and 4) with every possible value on the right list (only possible value is []), which results in [3:[], 4:[]], which is [[3],[4]]. So now we have (:) <$> [1,2] <*> [[3],[4]]
        • -
        • Now, : is used with every possible value from the left list (1 and 2) with every possible value in the right list ([3] and [4]), which results in [1:[3], 1:[4], 2:[3], 2:[4]], which is [[1,3],[1,4],[2,3],[2,4]
        • +
        • We start off with sequenceA [[1,2],[3,4]]
        • +
        • That evaluates to +(:) <$> [1,2] <*> sequenceA [[3,4]]
        • +
        • Evaluating the inner sequenceA further, we get +(:) <$> [1,2] <*> ((:) <$> [3,4] <*> sequenceA [])
        • +
        • We’ve reached the edge condition, so this is now +(:) <$> [1,2] <*> ((:) <$> [3,4] <*> [[]])
        • +
        • Now, we evaluate the (:) <$> [3,4] <*> [[]] +part, which will use : with every possible value in the +left list (possible values are 3 and 4) with +every possible value on the right list (only possible value is +[]), which results in [3:[], 4:[]], which is +[[3],[4]]. So now we have +(:) <$> [1,2] <*> [[3],[4]]
        • +
        • Now, : is used with every possible value from the left +list (1 and 2) with every possible value in +the right list ([3] and [4]), which results in +[1:[3], 1:[4], 2:[3], 2:[4]], which is +[[1,3],[1,4],[2,3],[2,4]
        -

        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]
        +

        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:

        +["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:

          -
        • pure id <*> v = v
        • -
        • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
        • -
        • pure f <*> pure x = pure (f x)
        • -
        • u <*> pure y = pure ($ y) <*> u
        • +
        • pure id <*> v = v
        • +
        • pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
        • +
        • pure f <*> pure x = pure (f x)
        • +
        • u <*> pure y = pure ($ y) <*> u
        -

        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.

        +

        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
        +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!"
        +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 "oisters"
        -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
        -
        - +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))
        +
        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)
        -
        - +("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 +

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

        - +data to define your type. Let’s do the same thing here and +apply helloMe to an undefined value:

        +
        ghci> helloMe undefined
        +"hello"
        + +

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

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

        - +[0.5,2.5]
        +

        It seems that both * together with 1 and +++ along with [] share some common +properties:

          -
        • The function takes two parameters.
        • -
        • The parameters and the returned value have the same type.
        • -
        • There exists such a value that doesn’t change other values when used - with the binary function.
        • +
        • The function takes two parameters.
        • +
        • The parameters and the returned value have the same type.
        • +
        • There exists such a value that doesn’t change other values when used +with the binary function.
        - -

        -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)
        +

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

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

          -
        • mempty `mappend` x = x
        • -
        • x `mappend` mempty = x
        • -
        • (x `mappend` y) `mappend` z = x `mappend` (y - `mappend` z)
        • +
        • mempty `mappend` x = x
        • +
        • x `mappend` mempty = x
        • +
        • (x `mappend` y) `mappend` z = x `mappend` (y `mappend` z)
        - -

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

        - +

        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
        +

        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]
        +    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"
        @@ -1301,752 +1859,479 @@ 

        Lists are monoids

        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 adjecent 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"
        +[]
        +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 adjecent +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. -

        - +"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
        +

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

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

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

        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
        +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
        +    GT `mappend` _ = GT
        + +

        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 +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
        +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
        +                    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"
        +                    (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
        +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"
        +    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. -

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

        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 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"
        +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
        +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')
        +    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 +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 the 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 values, Data.Monoid provides a the
        +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 +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
        +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]
        +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)
        +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
        +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
        +                             F.foldMap f r
        + +

        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)
        @@ -2054,80 +2339,47 @@ 

        Using monoids to fold data struct (Node 9 (Node 8 Empty Empty) (Node 10 Empty Empty) - ) -

        - -

        -It has 5 at its root and then its left node is 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
        +            )
        +

        It has 5 at its root and then its left node is 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. -

        +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 index d7df2b7..60dfb46 100644 --- a/docs/higher-order-functions.html +++ b/docs/higher-order-functions.html @@ -31,79 +31,178 @@
        -

        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.

        +

        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
        +

        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.

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

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

        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"
        @@ -112,20 +211,45 @@ 

        Some higher-orderism is in order

        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]
        +[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]
        +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]
        @@ -134,39 +258,61 @@ 

        Some higher-orderism is in order

        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)
        +[[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"
        +    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]
        -
        +[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 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]
        +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!"]
        @@ -175,20 +321,28 @@ 

        Maps and filters

        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]
        +[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]
        +    | 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]
        @@ -199,152 +353,372 @@ 

        Maps and filters

        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]
        +"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
        +    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]
        +    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
        +    | 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
        +[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 listOfFuns = map (*) [0..]
        +    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 listOfFuns = map (*) [0..]
         ghci> (listOfFuns !! 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.

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

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

        +
        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
        -
        +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
        +
        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]
        @@ -360,114 +734,232 @@ 

        Only folds and horses

        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]
        +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 roots of all natural numbers to exceed 1000? To get the squares 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
        +[[],[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 roots of all natural numbers to +exceed 1000? To get the squares 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.

        +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]
        -
        +

        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]
        -
        +

        In mathematics, function composition is defined like this: , 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 right sides. 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
        +
        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 right sides. 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.

        + 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/input-and-output.html b/docs/input-and-output.html index 85406da..95098d2 100644 --- a/docs/input-and-output.html +++ b/docs/input-and-output.html @@ -31,95 +31,236 @@
        -

        Input and Output

        -poor dog -

        We’ve mentioned that Haskell is a purely functional language. Whereas in imperative languages you usually get things done by giving the computer a series of steps to execute, functional programming is more of defining what stuff is. In Haskell, a function can’t change some state, like changing the contents of a variable (when a function changes state, we say that the function has side-effects). The only thing a function can do in Haskell is give us back some result based on the parameters we gave it. If a function is called two times with the same parameters, it has to return the same result. While this may seem a bit limiting when you’re coming from an imperative world, we’ve seen that it’s actually really cool. In an imperative language, you have no guarantee that a simple function that should just crunch some numbers won’t burn down your house, kidnap your dog and scratch your car with a potato while crunching those numbers. For instance, when we were making a binary search tree, we didn’t insert an element into a tree by modifying some tree in place. Our function for inserting into a binary search tree actually returned a new tree, because it can’t change the old one.

        -

        While functions being unable to change state is good because it helps us reason about our programs, there’s one problem with that. If a function can’t change anything in the world, how is it supposed to tell us what it calculated? In order to tell us what it calculated, it has to change the state of an output device (usually the state of the screen), which then emits photons that travel to our brain and change the state of our mind, man.

        -

        Do not despair, all is not lost. It turns out that Haskell actually has a really clever system for dealing with functions that have side-effects that neatly separates the part of our program that is pure and the part of our program that is impure, which does all the dirty work like talking to the keyboard and the screen. With those two parts separated, we can still reason about our pure program and take advantage of all the things that purity offers, like laziness, robustness and modularity while efficiently communicating with the outside world.

        +

        Input and Output

        +poor dog +

        We’ve mentioned that Haskell is a purely functional language. Whereas +in imperative languages you usually get things done by giving the +computer a series of steps to execute, functional programming is more of +defining what stuff is. In Haskell, a function can’t change some state, +like changing the contents of a variable (when a function changes state, +we say that the function has side-effects). The only thing a +function can do in Haskell is give us back some result based on the +parameters we gave it. If a function is called two times with the same +parameters, it has to return the same result. While this may seem a bit +limiting when you’re coming from an imperative world, we’ve seen that +it’s actually really cool. In an imperative language, you have no +guarantee that a simple function that should just crunch some numbers +won’t burn down your house, kidnap your dog and scratch your car with a +potato while crunching those numbers. For instance, when we were making +a binary search tree, we didn’t insert an element into a tree by +modifying some tree in place. Our function for inserting into a binary +search tree actually returned a new tree, because it can’t change the +old one.

        +

        While functions being unable to change state is good because it helps +us reason about our programs, there’s one problem with that. If a +function can’t change anything in the world, how is it supposed to tell +us what it calculated? In order to tell us what it calculated, it has to +change the state of an output device (usually the state of the screen), +which then emits photons that travel to our brain and change the state +of our mind, man.

        +

        Do not despair, all is not lost. It turns out that Haskell actually +has a really clever system for dealing with functions that have +side-effects that neatly separates the part of our program that is pure +and the part of our program that is impure, which does all the dirty +work like talking to the keyboard and the screen. With those two parts +separated, we can still reason about our pure program and take advantage +of all the things that purity offers, like laziness, robustness and +modularity while efficiently communicating with the outside world.

        Hello, world!

        -HELLO! -

        Up until now, we’ve always loaded our functions into GHCI to test them out and play with them. We’ve also explored the standard library functions that way. But now, after eight or so chapters, we’re finally going to write our first real Haskell program! Yay! And sure enough, we’re going to do the good old "hello, world" schtick.

        -

        Hey! For the purposes of this chapter, I’m going to assume you’re using a unix-y environment for learning Haskell. If you’re on Windows, I’d suggest you download Cygwin, which is a Linux-like environment for Windows, A.K.A. just what you need.

        -

        So, for starters, punch in the following in your favorite text editor:

        -
        
        -main = putStrLn "hello, world"
        -
        -

        We just defined a name called main and in it we call a function called putStrLn with the parameter "hello, world". Looks pretty much run of the mill, but it isn’t, as we’ll see in just a few moments. Save that file as helloworld.hs.

        -

        And now, we’re going to do something we’ve never done before. We’re actually going to compile our program! I’m so excited! Open up your terminal and navigate to the directory where helloworld.hs is located and do the following:

        -
        
        -$ ghc --make helloworld
        +HELLO!
        +

        Up until now, we’ve always loaded our functions into GHCI to test +them out and play with them. We’ve also explored the standard library +functions that way. But now, after eight or so chapters, we’re finally +going to write our first real Haskell program! Yay! And sure +enough, we’re going to do the good old "hello, world" +schtick.

        +
        +

        Hey! For the purposes of this chapter, I’m going to +assume you’re using a unix-y environment for learning Haskell. If you’re +on Windows, I’d suggest you download Cygwin, which is a Linux-like +environment for Windows, A.K.A. just what you need.

        +
        +

        So, for starters, punch in the following in your favorite text +editor:

        +
        main = putStrLn "hello, world"
        +

        We just defined a name called main and in it we call a +function called putStrLn with the parameter +"hello, world". Looks pretty much run of the mill, but it +isn’t, as we’ll see in just a few moments. Save that file as +helloworld.hs.

        +

        And now, we’re going to do something we’ve never done before. We’re +actually going to compile our program! I’m so excited! Open up your +terminal and navigate to the directory where helloworld.hs +is located and do the following:

        +
        $ ghc --make helloworld
         [1 of 1] Compiling Main             ( helloworld.hs, helloworld.o )
        -Linking helloworld ...
        -
        -

        Okay! With any luck, you got something like this and now you can run your program by doing ./helloworld.

        -
        
        -$ ./helloworld
        -hello, world
        -
        -

        And there we go, our first compiled program that printed out something to the terminal. How extraordinarily boring!

        -

        Let’s examine what we wrote. First, let’s look at the type of the function putStrLn.

        -
        
        -ghci> :t putStrLn
        +Linking helloworld ...
        +

        Okay! With any luck, you got something like this and now you can run +your program by doing ./helloworld.

        +
        $ ./helloworld
        +hello, world
        +

        And there we go, our first compiled program that printed out +something to the terminal. How extraordinarily boring!

        +

        Let’s examine what we wrote. First, let’s look at the type of the +function putStrLn.

        +
        ghci> :t putStrLn
         putStrLn :: String -> IO ()
         ghci> :t putStrLn "hello, world"
        -putStrLn "hello, world" :: IO ()
        -
        -

        We can read the type of putStrLn like this: putStrLn takes a string and returns an I/O action that has a result type of () (i.e. the empty tuple, also know as unit). An I/O action is something that, when performed, will carry out an action with a side-effect (that’s usually either reading from the input or printing stuff to the screen) and will also contain some kind of return value inside it. Printing a string to the terminal doesn’t really have any kind of meaningful return value, so a dummy value of () is used.

        -

        The empty tuple is a value of () and it also has a type of ().

        -

        So, when will an I/O action be performed? Well, this is where main comes in. An I/O action will be performed when we give it a name of main and then run our program.

        -

        Having your whole program be just one I/O action seems kind of limiting. That’s why we can use do syntax to glue together several I/O actions into one. Take a look at the following example:

        -
        
        -main = do
        +putStrLn "hello, world" :: IO ()
        +

        We can read the type of putStrLn like this: +putStrLn takes a string and returns an I/O +action that has a result type of () (i.e. the +empty tuple, also know as unit). An I/O action is something that, when +performed, will carry out an action with a side-effect (that’s usually +either reading from the input or printing stuff to the screen) and will +also contain some kind of return value inside it. Printing a string to +the terminal doesn’t really have any kind of meaningful return value, so +a dummy value of () is used.

        +
        +

        The empty tuple is a value of () and it also has a type +of ().

        +
        +

        So, when will an I/O action be performed? Well, this is where +main comes in. An I/O action will be performed when we give +it a name of main and then run our program.

        +

        Having your whole program be just one I/O action seems kind of +limiting. That’s why we can use do syntax to glue together +several I/O actions into one. Take a look at the following example:

        +
        main = do
             putStrLn "Hello, what's your name?"
             name <- getLine
        -    putStrLn ("Hey " ++ name ++ ", you rock!")
        -
        -

        Ah, interesting, new syntax! And this reads pretty much like an imperative program. If you compile it and try it out, it will probably behave just like you expect it to. Notice that we said do and then we laid out a series of steps, like we would in an imperative program. Each of these steps is an I/O action. By putting them together with do syntax, we glued them into one I/O action. The action that we got has a type of IO (), because that’s the type of the last I/O action inside.

        -

        Because of that, main always has a type signature of main :: IO something, where something is some concrete type. By convention, we don’t usually specify a type declaration for main.

        -

        An interesting thing that we haven’t met before is the third line, which states name <- getLine. It looks like it reads a line from the input and stores it into a variable called name. Does it really? Well, let’s examine the type of getLine.

        -
        
        -ghci> :t getLine
        -getLine :: IO String
        -
        -luggage -

        Aha, o-kay. getLine is an I/O action that contains a result type of String. That makes sense, because it will wait for the user to input something at the terminal and then that something will be represented as a string. So what’s up with name <- getLine then? You can read that piece of code like this: perform the I/O action getLine and then bind its result value to name. getLine has a type of IO String, so name will have a type of String. You can think of an I/O action as a box with little feet that will go out into the real world and do something there (like write some graffiti on a wall) and maybe bring back some data. Once it’s fetched that data for you, the only way to open the box and get the data inside it is to use the <- construct. And if we’re taking data out of an I/O action, we can only take it out when we’re inside another I/O action. This is how Haskell manages to neatly separate the pure and impure parts of our code. getLine is in a sense impure because its result value is not guaranteed to be the same when performed twice. That’s why it’s sort of tainted with the IO type constructor and we can only get that data out in I/O code. And because I/O code is tainted too, any computation that depends on tainted I/O data will have a tainted result.

        -

        When I say tainted, I don’t mean tainted in such a way that we can never use the result contained in an I/O action ever again in pure code. No, we temporarily un-taint the data inside an I/O action when we bind it to a name. When we do name <- getLine, name is just a normal string, because it represents what’s inside the box. We can have a really complicated function that, say, takes your name (a normal string) as a parameter and tells you your fortune and your whole life’s future based on your name. We can do this:

        -
        
        -main = do
        +    putStrLn ("Hey " ++ name ++ ", you rock!")
        +

        Ah, interesting, new syntax! And this reads pretty much like an +imperative program. If you compile it and try it out, it will probably +behave just like you expect it to. Notice that we said do and +then we laid out a series of steps, like we would in an imperative +program. Each of these steps is an I/O action. By putting them together +with do syntax, we glued them into one I/O action. The action +that we got has a type of IO (), because that’s the type of +the last I/O action inside.

        +

        Because of that, main always has a type signature of +main :: IO something, where +something is some concrete type. By convention, we +don’t usually specify a type declaration for main.

        +

        An interesting thing that we haven’t met before is the third line, +which states name <- getLine. It looks like it reads a +line from the input and stores it into a variable called +name. Does it really? Well, let’s examine the type of +getLine.

        +
        ghci> :t getLine
        +getLine :: IO String
        +luggage +

        Aha, o-kay. getLine is an I/O action that contains a +result type of String. That makes sense, because it will +wait for the user to input something at the terminal and then that +something will be represented as a string. So what’s up with +name <- getLine then? You can read that piece of code +like this: perform the I/O action getLine and then +bind its result value to name. +getLine has a type of IO String, so +name will have a type of String. You can think +of an I/O action as a box with little feet that will go out into the +real world and do something there (like write some graffiti on a wall) +and maybe bring back some data. Once it’s fetched that data for you, the +only way to open the box and get the data inside it is to use the +<- construct. And if we’re taking data out of an I/O +action, we can only take it out when we’re inside another I/O action. +This is how Haskell manages to neatly separate the pure and impure parts +of our code. getLine is in a sense impure because its +result value is not guaranteed to be the same when performed twice. +That’s why it’s sort of tainted with the IO type +constructor and we can only get that data out in I/O code. And because +I/O code is tainted too, any computation that depends on tainted I/O +data will have a tainted result.

        +

        When I say tainted, I don’t mean tainted in such a way that +we can never use the result contained in an I/O action ever again in +pure code. No, we temporarily un-taint the data inside an I/O +action when we bind it to a name. When we do +name <- getLine, name is just a normal +string, because it represents what’s inside the box. We can have a +really complicated function that, say, takes your name (a normal string) +as a parameter and tells you your fortune and your whole life’s future +based on your name. We can do this:

        +
        main = do
             putStrLn "Hello, what's your name?"
             name <- getLine
        -    putStrLn $ "Read this carefully, because this is your future: " ++ tellFortune name
        -
        -

        and tellFortune (or any of the functions it passes name to) doesn’t have to know anything about I/O, it’s just a normal String -> String function!

        + putStrLn $ "Read this carefully, because this is your future: " ++ tellFortune name
        +

        and tellFortune (or any of the functions it passes +name to) doesn’t have to know anything about I/O, it’s just +a normal String -> String function!

        Take a look at this piece of code. Is it valid?

        -
        
        -nameTag = "Hello, my name is " ++ getLine
        -
        -

        If you said no, go eat a cookie. If you said yes, drink a bowl of molten lava. Just kidding, don’t! The reason that this doesn’t work is that ++ requires both its parameters to be lists over the same type. The left parameter has a type of String (or [Char] if you will), whilst getLine has a type of IO String. You can’t concatenate a string and an I/O action. We first have to get the result out of the I/O action to get a value of type String and the only way to do that is to say something like name <- getLine inside some other I/O action. If we want to deal with impure data, we have to do it in an impure environment. So the taint of impurity spreads around much like the undead scourge and it’s in our best interest to keep the I/O parts of our code as small as possible.

        -

        Every I/O action that gets performed has a result encapsulated within it. That’s why our previous example program could also have been written like this:

        -
        
        -main = do
        +
        nameTag = "Hello, my name is " ++ getLine
        +

        If you said no, go eat a cookie. If you said yes, drink a bowl of +molten lava. Just kidding, don’t! The reason that this doesn’t work is +that ++ requires both its parameters to be lists over the +same type. The left parameter has a type of String (or +[Char] if you will), whilst getLine has a type +of IO String. You can’t concatenate a string and an I/O +action. We first have to get the result out of the I/O action to get a +value of type String and the only way to do that is to say +something like name <- getLine inside some other I/O +action. If we want to deal with impure data, we have to do it in an +impure environment. So the taint of impurity spreads around much like +the undead scourge and it’s in our best interest to keep the I/O parts +of our code as small as possible.

        +

        Every I/O action that gets performed has a result encapsulated within +it. That’s why our previous example program could also have been written +like this:

        +
        main = do
             foo <- putStrLn "Hello, what's your name?"
             name <- getLine
        -    putStrLn ("Hey " ++ name ++ ", you rock!")
        -
        -

        However, foo would just have a value of (), so doing that would be kind of moot. Notice that we didn’t bind the last putStrLn to anything. That’s because in a do block, the last action cannot be bound to a name like the first two were. We’ll see exactly why that is so a bit later when we venture off into the world of monads. For now, you can think of it in the way that the do block automatically extracts the value from the last action and binds it to its own result.

        -

        Except for the last line, every line in a do block that doesn’t bind can also be written with a bind. So putStrLn "BLAH" can be written as _ <- putStrLn "BLAH". But that’s useless, so we leave out the <- for I/O actions that don’t contain an important result, like putStrLn something.

        + putStrLn ("Hey " ++ name ++ ", you rock!")
        +

        However, foo would just have a value of (), +so doing that would be kind of moot. Notice that we didn’t bind the last +putStrLn to anything. That’s because in a do +block, the last action cannot be bound to a name like +the first two were. We’ll see exactly why that is so a bit later when we +venture off into the world of monads. For now, you can think of it in +the way that the do block automatically extracts the value from +the last action and binds it to its own result.

        +

        Except for the last line, every line in a do block that +doesn’t bind can also be written with a bind. So +putStrLn "BLAH" can be written as +_ <- putStrLn "BLAH". But that’s useless, so we leave +out the <- for I/O actions that don’t contain an +important result, like putStrLn something.

        Beginners sometimes think that doing

        -
        
        -name = getLine
        -
        -

        will read from the input and then bind the value of that to name. Well, it won’t, all this does is give the getLine I/O action a different name called, well, name. Remember, to get the value out of an I/O action, you have to perform it inside another I/O action by binding it to a name with <-.

        -

        I/O actions will only be performed when they are given a name of main or when they’re inside a bigger I/O action that we composed with a do block. We can also use a do block to glue together a few I/O actions and then we can use that I/O action in another do block and so on. Either way, they’ll be performed only if they eventually fall into main.

        -

        Oh, right, there’s also one more case when I/O actions will be performed. When we type out an I/O action in GHCI and press return, it will be performed.

        -
        
        -ghci> putStrLn "HEEY"
        -HEEY
        -
        -

        Even when we just punch out a number or call a function in GHCI and press return, it will evaluate it (as much as it needs) and then call show on it and then it will print that string to the terminal using putStrLn implicitly.

        -

        Remember let bindings? If you don’t, refresh your memory on them by reading this section. They have to be in the form of let bindings in expression, where bindings are names to be given to expressions and expression is the expression that is to be evaluated that sees them. We also said that in list comprehensions, the in part isn’t needed. Well, you can use them in do blocks pretty much like you use them in list comprehensions. Check this out:

        -
        
        -import Data.Char
        +
        name = getLine
        +

        will read from the input and then bind the value of that to +name. Well, it won’t, all this does is give the +getLine I/O action a different name called, well, +name. Remember, to get the value out of an I/O action, you +have to perform it inside another I/O action by binding it to a name +with <-.

        +

        I/O actions will only be performed when they are given a name of +main or when they’re inside a bigger I/O action that we +composed with a do block. We can also use a do block +to glue together a few I/O actions and then we can use that I/O action +in another do block and so on. Either way, they’ll be performed +only if they eventually fall into main.

        +

        Oh, right, there’s also one more case when I/O actions will be +performed. When we type out an I/O action in GHCI and press return, it +will be performed.

        +
        ghci> putStrLn "HEEY"
        +HEEY
        +

        Even when we just punch out a number or call a function in GHCI and +press return, it will evaluate it (as much as it needs) and then call +show on it and then it will print that string to the +terminal using putStrLn implicitly.

        +

        Remember let bindings? If you don’t, refresh your memory on +them by reading this +section. They have to be in the form of let bindings +in expression, where bindings are +names to be given to expressions and expression is +the expression that is to be evaluated that sees them. We also said that +in list comprehensions, the in part isn’t needed. Well, you can +use them in do blocks pretty much like you use them in list +comprehensions. Check this out:

        +
        import Data.Char
         
         main = do
             putStrLn "What's your first name?"
        @@ -128,13 +269,29 @@ 

        Hello, world!

        lastName <- getLine let bigFirstName = map toUpper firstName bigLastName = map toUpper lastName - putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?" -
        -

        See how the I/O actions in the do block are lined up? Also notice how the let is lined up with the I/O actions and the names of the let are lined up with each other? That’s good practice, because indentation is important in Haskell. Now, we did map toUpper firstName, which turns something like "John" into a much cooler string like "JOHN". We bound that uppercased string to a name and then used it in a string later on that we printed to the terminal.

        -

        You may be wondering when to use <- and when to use let bindings? Well, remember, <- is (for now) for performing I/O actions and binding their results to names. map toUpper firstName, however, isn’t an I/O action. It’s a pure expression in Haskell. So use <- when you want to bind results of I/O actions to names and you can use let bindings to bind pure expressions to names. Had we done something like let firstName = getLine, we would have just called the getLine I/O action a different name and we’d still have to run it through a <- to perform it.

        -

        Now we’re going to make a program that continuously reads a line and prints out the same line with the words reversed. The program’s execution will stop when we input a blank line. This is the program:

        -
        
        -main = do
        +    putStrLn $ "hey " ++ bigFirstName ++ " " ++ bigLastName ++ ", how are you?"
        +

        See how the I/O actions in the do block are lined up? Also +notice how the let is lined up with the I/O actions and the +names of the let are lined up with each other? That’s good +practice, because indentation is important in Haskell. Now, we did +map toUpper firstName, which turns something like +"John" into a much cooler string like "JOHN". +We bound that uppercased string to a name and then used it in a string +later on that we printed to the terminal.

        +

        You may be wondering when to use <- and when to use +let bindings? Well, remember, <- is (for now) +for performing I/O actions and binding their results to names. +map toUpper firstName, however, isn’t an I/O action. It’s a +pure expression in Haskell. So use <- when you want to +bind results of I/O actions to names and you can use let +bindings to bind pure expressions to names. Had we done something like +let firstName = getLine, we would have just called the +getLine I/O action a different name and we’d still have to +run it through a <- to perform it.

        +

        Now we’re going to make a program that continuously reads a line and +prints out the same line with the words reversed. The program’s +execution will stop when we input a blank line. This is the program:

        +
        main = do
             line <- getLine
             if null line
                 then return ()
        @@ -143,162 +300,287 @@ 

        Hello, world!

        main reverseWords :: String -> String -reverseWords = unwords . map reverse . words -
        -

        To get a feel of what it does, you can run it before we go over the code.

        -

        Protip: To run a program you can either compile it and then run the produced executable file by doing ghc --make helloworld and then ./helloworld or you can use the runhaskell command like so: runhaskell helloworld.hs and your program will be executed on the fly.

        -

        First, let’s take a look at the reverseWords function. It’s just a normal function that takes a string like "hey there man" and then calls words with it to produce a list of words like ["hey","there","man"]. Then we map reverse on the list, getting ["yeh","ereht","nam"] and then we put that back into one string by using unwords and the final result is "yeh ereht nam". See how we used function composition here. Without function composition, we’d have to write something like reverseWords st = unwords (map reverse (words st)).

        -

        What about main? First, we get a line from the terminal by performing getLine call that line line. And now, we have a conditional expression. Remember that in Haskell, every if must have a corresponding else because every expression has to have some sort of value. We make the if so that when a condition is true (in our case, the line that we entered is blank), we perform one I/O action and when it isn’t, the I/O action under the else is performed. That’s why in an I/O do block, ifs have to have a form of if condition then I/O action else I/O action.

        -

        Let’s first take a look at what happens under the else clause. Because, we have to have exactly one I/O action after the else, we use a do block to glue together two I/O actions into one. You could also write that part out as:

        -
        
        -        else (do
        +reverseWords = unwords . map reverse . words
        +

        To get a feel of what it does, you can run it before we go over the +code.

        +
        +

        Protip: To run a program you can either compile it +and then run the produced executable file by doing +ghc --make helloworld and then ./helloworld or +you can use the runhaskell command like so: +runhaskell helloworld.hs and your program will be executed +on the fly.

        +
        +

        First, let’s take a look at the reverseWords function. +It’s just a normal function that takes a string like +"hey there man" and then calls words with it +to produce a list of words like ["hey","there","man"]. Then +we map reverse on the list, getting +["yeh","ereht","nam"] and then we put that back into one +string by using unwords and the final result is +"yeh ereht nam". See how we used function composition here. +Without function composition, we’d have to write something like +reverseWords st = unwords (map reverse (words st)).

        +

        What about main? First, we get a line from the terminal +by performing getLine call that line line. And +now, we have a conditional expression. Remember that in Haskell, every +if must have a corresponding else because every +expression has to have some sort of value. We make the if so +that when a condition is true (in our case, the line that we entered is +blank), we perform one I/O action and when it isn’t, the I/O action +under the else is performed. That’s why in an I/O do +block, ifs have to have a form of if condition +then I/O action else I/O action.

        +

        Let’s first take a look at what happens under the else +clause. Because, we have to have exactly one I/O action after the +else, we use a do block to glue together two I/O +actions into one. You could also write that part out as:

        +
        else (do
                     putStrLn $ reverseWords line
        -            main)
        -
        -

        This makes it more explicit that the do block can be viewed as one I/O action, but it’s uglier. Anyway, inside the do block, we call reverseWords on the line that we got from getLine and then print that out to the terminal. After that, we just perform main. It’s called recursively and that’s okay, because main is itself an I/O action. So in a sense, we go back to the start of the program.

        -

        Now what happens when null line holds true? What’s after the then is performed in that case. If we look up, we’ll see that it says then return (). If you’ve done imperative languages like C, Java or Python, you’re probably thinking that you know what this return does and chances are you’ve already skipped this really long paragraph. Well, here’s the thing: the return in Haskell is really nothing like the return in most other languages! It has the same name, which confuses a lot of people, but in reality it’s quite different. In imperative languages, return usually ends the execution of a method or subroutine and makes it report some sort of value to whoever called it. In Haskell (in I/O actions specifically), it makes an I/O action out of a pure value. If you think about the box analogy from before, it takes a value and wraps it up in a box. The resulting I/O action doesn’t actually do anything, it just has that value encapsulated as its result. So in an I/O context, return "haha" will have a type of IO String. What’s the point of just transforming a pure value into an I/O action that doesn’t do anything? Why taint our program with IO more than it has to be? Well, we needed some I/O action to carry out in the case of an empty input line. That’s why we just made a bogus I/O action that doesn’t do anything by writing return ().

        -

        Using return doesn’t cause the I/O do block to end in execution or anything like that. For instance, this program will quite happily carry out all the way to the last line:

        -
        
        -main = do
        +            main)
        +

        This makes it more explicit that the do block can be viewed +as one I/O action, but it’s uglier. Anyway, inside the do +block, we call reverseWords on the line that we got from +getLine and then print that out to the terminal. After +that, we just perform main. It’s called recursively and +that’s okay, because main is itself an I/O action. So in a +sense, we go back to the start of the program.

        +

        Now what happens when null line holds true? What’s after +the then is performed in that case. If we look up, we’ll see +that it says then return (). If you’ve done imperative +languages like C, Java or Python, you’re probably thinking that you know +what this return does and chances are you’ve already +skipped this really long paragraph. Well, here’s the thing: the +return in Haskell is really nothing like the +return in most other languages! It has the same +name, which confuses a lot of people, but in reality it’s quite +different. In imperative languages, return usually ends the +execution of a method or subroutine and makes it report some sort of +value to whoever called it. In Haskell (in I/O actions specifically), it +makes an I/O action out of a pure value. If you think about the box +analogy from before, it takes a value and wraps it up in a box. The +resulting I/O action doesn’t actually do anything, it just has that +value encapsulated as its result. So in an I/O context, +return "haha" will have a type of IO String. +What’s the point of just transforming a pure value into an I/O action +that doesn’t do anything? Why taint our program with IO +more than it has to be? Well, we needed some I/O action to carry out in +the case of an empty input line. That’s why we just made a bogus I/O +action that doesn’t do anything by writing return ().

        +

        Using return doesn’t cause the I/O do block to +end in execution or anything like that. For instance, this program will +quite happily carry out all the way to the last line:

        +
        main = do
             return ()
             return "HAHAHA"
             line <- getLine
             return "BLAH BLAH BLAH"
             return 4
        -    putStrLn line
        -
        -

        All these returns do is that they make I/O actions that don’t really do anything except have an encapsulated result and that result is thrown away because it isn’t bound to a name. We can use return in combination with <- to bind stuff to names.

        -
        
        -main = do
        +    putStrLn line
        +

        All these returns do is that they make I/O actions that +don’t really do anything except have an encapsulated result and that +result is thrown away because it isn’t bound to a name. We can use +return in combination with <- to bind stuff +to names.

        +
        main = do
             a <- return "hell"
             b <- return "yeah!"
        -    putStrLn $ a ++ " " ++ b
        -
        -

        So you see, return is sort of the opposite to <-. While return takes a value and wraps it up in a box, <- takes a box (and performs it) and takes the value out of it, binding it to a name. But doing this is kind of redundant, especially since you can use let bindings in do blocks to bind to names, like so:

        -
        
        -main = do
        +    putStrLn $ a ++ " " ++ b
        +

        So you see, return is sort of the opposite to +<-. While return takes a value and wraps it +up in a box, <- takes a box (and performs it) and takes +the value out of it, binding it to a name. But doing this is kind of +redundant, especially since you can use let bindings in +do blocks to bind to names, like so:

        +
        main = do
             let a = "hell"
                 b = "yeah"
        -    putStrLn $ a ++ " " ++ b
        -
        -

        When dealing with I/O do blocks, we mostly use return either because we need to create an I/O action that doesn’t do anything or because we don’t want the I/O action that’s made up from a do block to have the result value of its last action, but we want it to have a different result value, so we use return to make an I/O action that always has our desired result contained and we put it at the end.

        -

        A do block can also have just one I/O action. In that case, it’s the same as just writing the I/O action. Some people would prefer writing then do return () in this case because the else also has a do.

        -

        Before we move on to files, let’s take a look at some functions that are useful when dealing with I/O.

        -

        putStr is much like putStrLn in that it takes a string as a parameter and returns an I/O action that will print that string to the terminal, only putStr doesn’t jump into a new line after printing out the string while putStrLn does.

        -
        
        -main = do   putStr "Hey, "
        +    putStrLn $ a ++ " " ++ b
        +

        When dealing with I/O do blocks, we mostly use +return either because we need to create an I/O action that +doesn’t do anything or because we don’t want the I/O action that’s made +up from a do block to have the result value of its last action, +but we want it to have a different result value, so we use +return to make an I/O action that always has our desired +result contained and we put it at the end.

        +
        +

        A do block can also have just one I/O action. In that case, +it’s the same as just writing the I/O action. Some people would prefer +writing then do return () in this case because the +else also has a do.

        +
        +

        Before we move on to files, let’s take a look at some functions that +are useful when dealing with I/O.

        +

        putStr is much like +putStrLn in that it takes a string as a parameter and +returns an I/O action that will print that string to the terminal, only +putStr doesn’t jump into a new line after printing out the +string while putStrLn does.

        +
        main = do   putStr "Hey, "
                     putStr "I'm "
        -            putStrLn "Andy!"
        -
        -
        
        -$ runhaskell putstr_test.hs
        -Hey, I'm Andy!
        -
        -

        Its type signature is putStr :: String -> IO (), so the result encapsulated within the resulting I/O action is the unit. A dud value, so it doesn’t make sense to bind it.

        -

        putChar takes a character and returns an I/O action that will print it out to the terminal.

        -
        
        -main = do   putChar 't'
        +            putStrLn "Andy!"
        +
        $ runhaskell putstr_test.hs
        +Hey, I'm Andy!
        +

        Its type signature is putStr :: String -> IO (), so +the result encapsulated within the resulting I/O action is the unit. A +dud value, so it doesn’t make sense to bind it.

        +

        putChar takes a character and +returns an I/O action that will print it out to the terminal.

        +
        main = do   putChar 't'
                     putChar 'e'
        -            putChar 'h'
        -
        -
        
        -$ runhaskell putchar_test.hs
        -teh
        -
        -

        putStr is actually defined recursively with the help of putChar. The edge condition of putStr is the empty string, so if we’re printing an empty string, just return an I/O action that does nothing by using return (). If it’s not empty, then print the first character of the string by doing putChar and then print of them using putStr.

        -
        
        -putStr :: String -> IO ()
        +            putChar 'h'
        +
        $ runhaskell putchar_test.hs
        +teh
        +

        putStr is actually defined recursively with the help of +putChar. The edge condition of putStr is the +empty string, so if we’re printing an empty string, just return an I/O +action that does nothing by using return (). If it’s not +empty, then print the first character of the string by doing +putChar and then print of them using +putStr.

        +
        putStr :: String -> IO ()
         putStr [] = return ()
         putStr (x:xs) = do
             putChar x
        -    putStr xs
        -
        -

        See how we can use recursion in I/O, just like we can use it in pure code. Just like in pure code, we define the edge case and then think what the result actually is. It’s an action that first outputs the first character and then outputs the rest of the string.

        -

        print takes a value of any type that’s an instance of Show (meaning that we know how to represent it as a string), calls show with that value to stringify it and then outputs that string to the terminal. Basically, it’s just putStrLn . show. It first runs show on a value and then feeds that to putStrLn, which returns an I/O action that will print out our value.

        -
        
        -main = do   print True
        +    putStr xs
        +

        See how we can use recursion in I/O, just like we can use it in pure +code. Just like in pure code, we define the edge case and then think +what the result actually is. It’s an action that first outputs the first +character and then outputs the rest of the string.

        +

        print takes a value of any type +that’s an instance of Show (meaning that we know how to +represent it as a string), calls show with that value to +stringify it and then outputs that string to the terminal. Basically, +it’s just putStrLn . show. It first runs show +on a value and then feeds that to putStrLn, which returns +an I/O action that will print out our value.

        +
        main = do   print True
                     print 2
                     print "haha"
                     print 3.2
        -            print [3,4,3]
        -
        -
        
        -$ runhaskell print_test.hs
        +            print [3,4,3]
        +
        $ runhaskell print_test.hs
         True
         2
         "haha"
         3.2
        -[3,4,3]
        -
        -

        As you can see, it’s a very handy function. Remember how we talked about how I/O actions are performed only when they fall into main or when we try to evaluate them in the GHCI prompt? When we type out a value (like 3 or [1,2,3]) and press the return key, GHCI actually uses print on that value to display it on our terminal!

        -
        
        -ghci> 3
        +[3,4,3]
        +

        As you can see, it’s a very handy function. Remember how we talked +about how I/O actions are performed only when they fall into +main or when we try to evaluate them in the GHCI prompt? +When we type out a value (like 3 or [1,2,3]) +and press the return key, GHCI actually uses print on that +value to display it on our terminal!

        +
        ghci> 3
         3
         ghci> print 3
         3
         ghci> map (++"!") ["hey","ho","woo"]
         ["hey!","ho!","woo!"]
         ghci> print (map (++"!") ["hey","ho","woo"])
        -["hey!","ho!","woo!"]
        -
        -

        When we want to print out strings, we usually use putStrLn because we don’t want the quotes around them, but for printing out values of other types to the terminal, print is used the most.

        -

        getChar is an I/O action that reads a character from the input. Thus, its type signature is getChar :: IO Char, because the result contained within the I/O action is a Char. Note that due to buffering, reading of the characters won’t actually happen until the user mashes the return key.

        -
        
        -main = do
        +["hey!","ho!","woo!"]
        +

        When we want to print out strings, we usually use +putStrLn because we don’t want the quotes around them, but +for printing out values of other types to the terminal, +print is used the most.

        +

        getChar is an I/O action that +reads a character from the input. Thus, its type signature is +getChar :: IO Char, because the result contained within the +I/O action is a Char. Note that due to buffering, reading +of the characters won’t actually happen until the user mashes the return +key.

        +
        main = do
             c <- getChar
             if c /= ' '
                 then do
                     putChar c
                     main
        -        else return ()
        -
        -

        This program looks like it should read a character and then check if it’s a space. If it is, halt execution and if it isn’t, print it to the terminal and then do the same thing all over again. Well, it kind of does, only not in the way you might expect. Check this out:

        -
        
        -$ runhaskell getchar_test.hs
        +        else return ()
        +

        This program looks like it should read a character and then check if +it’s a space. If it is, halt execution and if it isn’t, print it to the +terminal and then do the same thing all over again. Well, it kind of +does, only not in the way you might expect. Check this out:

        +
        $ runhaskell getchar_test.hs
         hello sir
        -hello
        -
        -

        The second line is the input. We input hello sir and then press return. Due to buffering, the execution of the program will begin only when after we’ve hit return and not after every inputted character. But once we press return, it acts on what we’ve been putting in so far. Try playing with this program to get a feel for it!

        -

        The when function is found in Control.Monad (to get access to it, do import Control.Monad). It’s interesting because in a do block it looks like a control flow statement, but it’s actually a normal function. It takes a boolean value and an I/O action if that boolean value is True, it returns the same I/O action that we supplied to it. However, if it’s False, it returns the return (), action, so an I/O action that doesn’t do anything. Here’s how we could rewrite the previous piece of code with which we demonstrated getChar by using when:

        -
        
        -import Control.Monad
        +hello
        +

        The second line is the input. We input hello sir and +then press return. Due to buffering, the execution of the program will +begin only when after we’ve hit return and not after every inputted +character. But once we press return, it acts on what we’ve been putting +in so far. Try playing with this program to get a feel for it!

        +

        The when function is found in +Control.Monad (to get access to it, do +import Control.Monad). It’s interesting because in a +do block it looks like a control flow statement, but it’s +actually a normal function. It takes a boolean value and an I/O action +if that boolean value is True, it returns the same I/O +action that we supplied to it. However, if it’s False, it +returns the return (), action, so an I/O action that +doesn’t do anything. Here’s how we could rewrite the previous piece of +code with which we demonstrated getChar by using +when:

        +
        import Control.Monad
         
         main = do
             c <- getChar
             when (c /= ' ') $ do
                 putChar c
        -        main
        -
        -

        So as you can see, it’s useful for encapsulating the if something then do some I/O action else return () pattern.

        -

        sequence takes a list of I/O actions and returns an I/O actions that will perform those actions one after the other. The result contained in that I/O action will be a list of the results of all the I/O actions that were performed. Its type signature is sequence :: [IO a] -> IO [a]. Doing this:

        -
        
        -main = do
        +        main
        +

        So as you can see, it’s useful for encapsulating the if +something then do some I/O action else return +() pattern.

        +

        sequence takes a list of I/O +actions and returns an I/O actions that will perform those actions one +after the other. The result contained in that I/O action will be a list +of the results of all the I/O actions that were performed. Its type +signature is sequence :: [IO a] -> IO [a]. Doing +this:

        +
        main = do
             a <- getLine
             b <- getLine
             c <- getLine
        -    print [a,b,c]
        -
        + print [a,b,c]

        Is exactly the same as doing this:.

        -
        
        -main = do
        +
        main = do
             rs <- sequence [getLine, getLine, getLine]
        -    print rs
        -
        -

        So sequence [getLine, getLine, getLine] makes an I/O action that will perform getLine three times. If we bind that action to a name, the result is a list of all the results, so in our case, a list of three things that the user entered at the prompt.

        -

        A common pattern with sequence is when we map functions like print or putStrLn over lists. Doing map print [1,2,3,4] won’t create an I/O action. It will create a list of I/O actions, because that’s like writing [print 1, print 2, print 3, print 4]. If we want to transform that list of I/O actions into an I/O action, we have to sequence it.

        -
        
        -ghci> sequence (map print [1,2,3,4,5])
        +    print rs
        +

        So sequence [getLine, getLine, getLine] makes an I/O +action that will perform getLine three times. If we bind +that action to a name, the result is a list of all the results, so in +our case, a list of three things that the user entered at the +prompt.

        +

        A common pattern with sequence is when we map functions +like print or putStrLn over lists. Doing +map print [1,2,3,4] won’t create an I/O action. It will +create a list of I/O actions, because that’s like writing +[print 1, print 2, print 3, print 4]. If we want to +transform that list of I/O actions into an I/O action, we have to +sequence it.

        +
        ghci> sequence (map print [1,2,3,4,5])
         1
         2
         3
         4
         5
        -[(),(),(),(),()]
        -
        -

        What’s with the [(),(),(),(),()] at the end? Well, when we evaluate an I/O action in GHCI, it’s performed and then its result is printed out, unless that result is (), in which case it’s not printed out. That’s why evaluating putStrLn "hehe" in GHCI just prints out hehe (because the contained result in putStrLn "hehe" is ()). But when we do getLine in GHCI, the result of that I/O action is printed out, because getLine has a type of IO String.

        -

        Because mapping a function that returns an I/O action over a list and then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM takes a function and a list, maps the function over the list and then sequences it. mapM_ does the same, only it throws away the result later. We usually use mapM_ when we don’t care what result our sequenced I/O actions have.

        -
        
        -ghci> mapM print [1,2,3]
        +[(),(),(),(),()]
        +

        What’s with the [(),(),(),(),()] at the end? Well, when +we evaluate an I/O action in GHCI, it’s performed and then its result is +printed out, unless that result is (), in which case it’s +not printed out. That’s why evaluating putStrLn "hehe" in +GHCI just prints out hehe (because the contained result in +putStrLn "hehe" is ()). But when we do +getLine in GHCI, the result of that I/O action is printed +out, because getLine has a type of +IO String.

        +

        Because mapping a function that returns an I/O action over a list and +then sequencing it is so common, the utility functions mapM and mapM_ were introduced. mapM +takes a function and a list, maps the function over the list and then +sequences it. mapM_ does the same, only it throws away the +result later. We usually use mapM_ when we don’t care what +result our sequenced I/O actions have.

        +
        ghci> mapM print [1,2,3]
         1
         2
         3
        @@ -306,21 +588,26 @@ 

        Hello, world!

        ghci> mapM_ print [1,2,3] 1 2 -3 -
        -

        forever takes an I/O action and returns an I/O action that just repeats the I/O action it got forever. It’s located in Control.Monad. This little program will indefinitely ask the user for some input and spit it back to him, CAPSLOCKED:

        -
        
        -import Control.Monad
        +3
        +

        forever takes an I/O action and +returns an I/O action that just repeats the I/O action it got forever. +It’s located in Control.Monad. This little program will +indefinitely ask the user for some input and spit it back to him, +CAPSLOCKED:

        +
        import Control.Monad
         import Data.Char
         
         main = forever $ do
             putStr "Give me some input: "
             l <- getLine
        -    putStrLn $ map toUpper l
        -
        -

        forM (located in Control.Monad) is like mapM, only that it has its parameters switched around. The first parameter is the list and the second one is the function to map over that list, which is then sequenced. Why is that useful? Well, with some creative use of lambdas and do notation, we can do stuff like this:

        -
        
        -import Control.Monad
        +    putStrLn $ map toUpper l
        +

        forM (located in +Control.Monad) is like mapM, only that it has +its parameters switched around. The first parameter is the list and the +second one is the function to map over that list, which is then +sequenced. Why is that useful? Well, with some creative use of lambdas +and do notation, we can do stuff like this:

        +
        import Control.Monad
         
         main = do
             colors <- forM [1,2,3,4] (\a -> do
        @@ -328,12 +615,28 @@ 

        Hello, world!

        color <- getLine return color) putStrLn "The colors that you associate with 1, 2, 3 and 4 are: " - mapM putStrLn colors -
        -

        The (\a -> do ... ) is a function that takes a number and returns an I/O action. We have to surround it with parentheses, otherwise the lambda thinks the last two I/O actions belong to it. Notice that we do return color in the inside do block. We do that so that the I/O action which the do block defines has the result of our color contained within it. We actually didn’t have to do that, because getLine already has that contained within it. Doing color <- getLine and then return color is just unpacking the result from getLine and then repackaging it again, so it’s the same as just doing getLine. The forM (called with its two parameters) produces an I/O action, whose result we bind to colors. colors is just a normal list that holds strings. At the end, we print out all those colors by doing mapM putStrLn colors.

        -

        You can think of forM as meaning: make an I/O action for every element in this list. What each I/O action will do can depend on the element that was used to make the action. Finally, perform those actions and bind their results to something. We don’t have to bind it, we can also just throw it away.

        -
        
        -$ runhaskell form_test.hs
        +    mapM putStrLn colors
        +

        The (\a -> do ... ) is a function that takes a number +and returns an I/O action. We have to surround it with parentheses, +otherwise the lambda thinks the last two I/O actions belong to it. +Notice that we do return color in the inside do +block. We do that so that the I/O action which the do block +defines has the result of our color contained within it. We actually +didn’t have to do that, because getLine already has that +contained within it. Doing color <- getLine and then +return color is just unpacking the result from +getLine and then repackaging it again, so it’s the same as +just doing getLine. The forM (called with its +two parameters) produces an I/O action, whose result we bind to +colors. colors is just a normal list that +holds strings. At the end, we print out all those colors by doing +mapM putStrLn colors.

        +

        You can think of forM as meaning: make an I/O action for +every element in this list. What each I/O action will do can depend on +the element that was used to make the action. Finally, perform those +actions and bind their results to something. We don’t have to bind it, +we can also just throw it away.

        +
        $ runhaskell form_test.hs
         Which color do you associate with the number 1?
         white
         Which color do you associate with the number 2?
        @@ -346,34 +649,71 @@ 

        Hello, world!

        white blue red -orange -
        -

        We could have actually done that without forM, only with forM it’s more readable. Normally we write forM when we want to map and sequence some actions that we define there on the spot using do notation. In the same vein, we could have replaced the last line with forM colors putStrLn.

        -

        In this section, we learned the basics of input and output. We also found out what I/O actions are, how they enable us to do input and output and when they are actually performed. To reiterate, I/O actions are values much like any other value in Haskell. We can pass them as parameters to functions and functions can return I/O actions as results. What’s special about them is that if they fall into the main function (or are the result in a GHCI line), they are performed. And that’s when they get to write stuff on your screen or play Yakety Sax through your speakers. Each I/O action can also encapsulate a result with which it tells you what it got from the real world.

        -

        Don’t think of a function like putStrLn as a function that takes a string and prints it to the screen. Think of it as a function that takes a string and returns an I/O action. That I/O action will, when performed, print beautiful poetry to your terminal.

        +orange
        +

        We could have actually done that without forM, only with +forM it’s more readable. Normally we write +forM when we want to map and sequence some actions that we +define there on the spot using do notation. In the same vein, +we could have replaced the last line with +forM colors putStrLn.

        +

        In this section, we learned the basics of input and output. We also +found out what I/O actions are, how they enable us to do input and +output and when they are actually performed. To reiterate, I/O actions +are values much like any other value in Haskell. We can pass them as +parameters to functions and functions can return I/O actions as results. +What’s special about them is that if they fall into the +main function (or are the result in a GHCI line), they are +performed. And that’s when they get to write stuff on your screen or +play Yakety Sax through your speakers. Each I/O action can also +encapsulate a result with which it tells you what it got from the real +world.

        +

        Don’t think of a function like putStrLn as a function +that takes a string and prints it to the screen. Think of it as a +function that takes a string and returns an I/O action. That I/O action +will, when performed, print beautiful poetry to your terminal.

        Files and streams

        -streams -

        getChar is an I/O action that reads a single character from the terminal. getLine is an I/O action that reads a line from the terminal. These two are pretty straightforward and most programming languages have some functions or statements that are parallel to them. But now, let’s meet getContents. getContents is an I/O action that reads everything from the standard input until it encounters an end-of-file character. Its type is getContents :: IO String. What’s cool about getContents is that it does lazy I/O. When we do foo <- getContents, it doesn’t read all of the input at once, store it in memory and then bind it to foo. No, it’s lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal later as we go along, when you really need it!”.

        -

        getContents is really useful when we’re piping the output from one program into the input of our program. In case you don’t know how piping works in unix-y systems, here’s a quick primer. Let’s make a text file that contains the following little haiku:

        -
        
        -I'm a lil' teapot
        +streams
        +

        getChar is an I/O action that reads a single character +from the terminal. getLine is an I/O action that reads a +line from the terminal. These two are pretty straightforward and most +programming languages have some functions or statements that are +parallel to them. But now, let’s meet getContents. getContents is +an I/O action that reads everything from the standard input until it +encounters an end-of-file character. Its type is +getContents :: IO String. What’s cool about +getContents is that it does lazy I/O. When we do +foo <- getContents, it doesn’t read all of the input at +once, store it in memory and then bind it to foo. No, it’s +lazy! It’ll say: “Yeah yeah, I’ll read the input from the terminal +later as we go along, when you really need it!”.

        +

        getContents is really useful when we’re piping the +output from one program into the input of our program. In case you don’t +know how piping works in unix-y systems, here’s a quick primer. Let’s +make a text file that contains the following little haiku:

        +
        I'm a lil' teapot
         What's with that airplane food, huh?
        -It's so small, tasteless
        -
        -

        Yeah, the haiku sucks, what of it? If anyone knows of any good haiku tutorials, let me know.

        -

        Now, recall the little program we wrote when we were introducing the forever function. It prompted the user for a line, returned it to him in CAPSLOCK and then did that all over again, indefinitely. Just so you don’t have to scroll all the way back, here it is again:

        -
        
        -import Control.Monad
        +It's so small, tasteless
        +

        Yeah, the haiku sucks, what of it? If anyone knows of any good haiku +tutorials, let me know.

        +

        Now, recall the little program we wrote when we were introducing the +forever function. It prompted the user for a line, returned +it to him in CAPSLOCK and then did that all over again, indefinitely. +Just so you don’t have to scroll all the way back, here it is again:

        +
        import Control.Monad
         import Data.Char
         
         main = forever $ do
             putStr "Give me some input: "
             l <- getLine
        -    putStrLn $ map toUpper l
        -
        -

        We’ll save that program as capslocker.hs or something and compile it. And then, we’re going to use a unix pipe to feed our text file directly to our little program. We’re going to use the help of the GNU cat program, which prints out a file that’s given to it as an argument. Check it out, booyaka!

        -
        
        -$ ghc --make capslocker
        +    putStrLn $ map toUpper l
        +

        We’ll save that program as capslocker.hs or something +and compile it. And then, we’re going to use a unix pipe to feed our +text file directly to our little program. We’re going to use the help of +the GNU cat program, which prints out a file that’s given to it +as an argument. Check it out, booyaka!

        +
        $ ghc --make capslocker
         [1 of 1] Compiling Main             ( capslocker.hs, capslocker.o )
         Linking capslocker ...
         $ cat haiku.txt
        @@ -384,36 +724,64 @@ 

        Files and streams

        I'M A LIL' TEAPOT WHAT'S WITH THAT AIRPLANE FOOD, HUH? IT'S SO SMALL, TASTELESS -capslocker <stdin>: hGetLine: end of file -
        -

        As you can see, piping the output of one program (in our case that was cat) to the input of another (capslocker) is done with the | character. What we’ve done is pretty much equivalent to just running capslocker, typing our haiku at the terminal and then issuing an end-of-file character (that’s usually done by pressing Ctrl-D). It’s like running cat haiku.txt and saying: “Wait, don’t print this out to the terminal, tell it to capslocker instead!”.

        -

        So what we’re essentially doing with that use of forever is taking the input and transforming it into some output. That’s why we can use getContents to make our program even shorter and better:

        -
        
        -import Data.Char
        +capslocker <stdin>: hGetLine: end of file
        +

        As you can see, piping the output of one program (in our case that +was cat) to the input of another (capslocker) is done +with the | character. What we’ve done is pretty much +equivalent to just running capslocker, typing our haiku at the +terminal and then issuing an end-of-file character (that’s usually done +by pressing Ctrl-D). It’s like running cat haiku.txt and +saying: “Wait, don’t print this out to the terminal, tell it to +capslocker instead!”.

        +

        So what we’re essentially doing with that use of forever +is taking the input and transforming it into some output. That’s why we +can use getContents to make our program even shorter and +better:

        +
        import Data.Char
         
         main = do
             contents <- getContents
        -    putStr (map toUpper contents)
        -
        -

        We run the getContents I/O action and name the string it produces contents. Then, we map toUpper over that string and print that to the terminal. Keep in mind that because strings are basically lists, which are lazy, and getContents is I/O lazy, it won’t try to read the whole content at once and store it into memory before printing out the capslocked version. Rather, it will print out the capslocked version as it reads it, because it will only read a line from the input when it really needs to.

        -
        
        -$ cat haiku.txt | ./capslocker
        +    putStr (map toUpper contents)
        +

        We run the getContents I/O action and name the string it +produces contents. Then, we map toUpper over +that string and print that to the terminal. Keep in mind that because +strings are basically lists, which are lazy, and +getContents is I/O lazy, it won’t try to read the whole +content at once and store it into memory before printing out the +capslocked version. Rather, it will print out the capslocked version as +it reads it, because it will only read a line from the input when it +really needs to.

        +
        $ cat haiku.txt | ./capslocker
         I'M A LIL' TEAPOT
         WHAT'S WITH THAT AIRPLANE FOOD, HUH?
        -IT'S SO SMALL, TASTELESS
        -
        -

        Cool, it works. What if we just run capslocker and try to type in the lines ourselves?

        -
        
        -$ ./capslocker
        +IT'S SO SMALL, TASTELESS
        +

        Cool, it works. What if we just run capslocker and try to +type in the lines ourselves?

        +
        $ ./capslocker
         hey ho
         HEY HO
         lets go
        -LETS GO
        -
        -

        We got out of that by pressing Ctrl-D. Pretty nice! As you can see, it prints out our capslocked input back to us line by line. When the result of getContents is bound to contents, it’s not represented in memory as a real string, but more like a promise that it will produce the string eventually. When we map toUpper over contents, that’s also a promise to map that function over the eventual contents. And finally when putStr happens, it says to the previous promise: “Hey, I need a capslocked line!”. It doesn’t have any lines yet, so it says to contents: “Hey, how about actually getting a line from the terminal?”. So that’s when getContents actually reads from the terminal and gives a line to the code that asked it to produce something tangible. That code then maps toUpper over that line and gives it to putStr, which prints it. And then, putStr says: “Hey, I need the next line, come on!” and this repeats until there’s no more input, which is signified by an end-of-file character.

        -

        Let’s make program that takes some input and prints out only those lines that are shorter than 10 characters. Observe:

        -
        
        -main = do
        +LETS GO
        +

        We got out of that by pressing Ctrl-D. Pretty nice! As you can see, +it prints out our capslocked input back to us line by line. When the +result of getContents is bound to contents, +it’s not represented in memory as a real string, but more like a promise +that it will produce the string eventually. When we map +toUpper over contents, that’s also a promise +to map that function over the eventual contents. And finally when +putStr happens, it says to the previous promise: “Hey, +I need a capslocked line!”. It doesn’t have any lines yet, so it +says to contents: “Hey, how about actually getting a +line from the terminal?”. So that’s when getContents +actually reads from the terminal and gives a line to the code that asked +it to produce something tangible. That code then maps +toUpper over that line and gives it to putStr, +which prints it. And then, putStr says: “Hey, I need +the next line, come on!” and this repeats until there’s no more +input, which is signified by an end-of-file character.

        +

        Let’s make program that takes some input and prints out only those +lines that are shorter than 10 characters. Observe:

        +
        main = do
             contents <- getContents
             putStr (shortLinesOnly contents)
         
        @@ -422,201 +790,348 @@ 

        Files and streams

        let allLines = lines input shortLines = filter (\line -> length line < 10) allLines result = unlines shortLines - in result -
        -

        We’ve made our I/O part of the program as short as possible. Because our program is supposed to take some input and print out some output based on the input, we can implement it by reading the input contents, running a function on them and then printing out what the function gave back.

        -

        The shortLinesOnly function works like this: it takes a string, like "short\nlooooooooooooooong\nshort again". That string has three lines, two of them are short and the middle one is long. It runs the lines function on that string, which converts it to ["short", "looooooooooooooong", "short again"], which we then bind to the name allLines. That list of string is then filtered so that only those lines that are shorter than 10 characters remain in the list, producing ["short", "short again"]. And finally, unlines joins that list into a single newline delimited string, giving "short\nshort again". Let’s give it a go.

        -
        
        -i'm short
        +    in  result
        +

        We’ve made our I/O part of the program as short as possible. Because +our program is supposed to take some input and print out some output +based on the input, we can implement it by reading the input contents, +running a function on them and then printing out what the function gave +back.

        +

        The shortLinesOnly function works like this: it takes a +string, like "short\nlooooooooooooooong\nshort again". That +string has three lines, two of them are short and the middle one is +long. It runs the lines function on that string, which +converts it to +["short", "looooooooooooooong", "short again"], which we +then bind to the name allLines. That list of string is then +filtered so that only those lines that are shorter than 10 characters +remain in the list, producing ["short", "short again"]. And +finally, unlines joins that list into a single newline +delimited string, giving "short\nshort again". Let’s give +it a go.

        +
        i'm short
         so am i
         i am a loooooooooong line!!!
         yeah i'm long so what hahahaha!!!!!!
         short line
         loooooooooooooooooooooooooooong
        -short
        -
        -
        
        -$ ghc --make shortlinesonly
        +short
        +
        $ ghc --make shortlinesonly
         [1 of 1] Compiling Main             ( shortlinesonly.hs, shortlinesonly.o )
         Linking shortlinesonly ...
         $ cat shortlines.txt | ./shortlinesonly
         i'm short
         so am i
        -short
        -
        -

        We pipe the contents of shortlines.txt into the output of shortlinesonly and as the output, we only get the short lines.

        -

        This pattern of getting some string from the input, transforming it with a function and then outputting that is so common that there exists a function which makes that even easier, called interact. interact takes a function of type String -> String as a parameter and returns an I/O action that will take some input, run that function on it and then print out the function’s result. Let’s modify our program to use that.

        -
        
        -main = interact shortLinesOnly
        +short
        +

        We pipe the contents of shortlines.txt into the output of +shortlinesonly and as the output, we only get the short +lines.

        +

        This pattern of getting some string from the input, transforming it +with a function and then outputting that is so common that there exists +a function which makes that even easier, called interact. interact takes a +function of type String -> String as a parameter and +returns an I/O action that will take some input, run that function on it +and then print out the function’s result. Let’s modify our program to +use that.

        +
        main = interact shortLinesOnly
         
         shortLinesOnly :: String -> String
         shortLinesOnly input =
             let allLines = lines input
                 shortLines = filter (\line -> length line < 10) allLines
                 result = unlines shortLines
        -    in  result
        -
        -

        Just to show that this can be achieved in much less code (even though it will be less readable) and to demonstrate our function composition skill, we’re going to rework that a bit further.

        -
        
        -main = interact $ unlines . filter ((<10) . length) . lines
        -
        -

        Wow, we actually reduced that to just one line, which is pretty cool!

        -

        interact can be used to make programs that are piped some contents into them and then dump some result out or it can be used to make programs that appear to take a line of input from the user, give back some result based on that line and then take another line and so on. There isn’t actually a real distinction between the two, it just depends on how the user is supposed to use them.

        -

        Let’s make a program that continuously reads a line and then tells us if the line is a palindrome or not. We could just use getLine to read a line, tell the user if it’s a palindrome and then run main all over again. But it’s simpler if we use interact. When using interact, think about what you need to do to transform some input into the desired output. In our case, we have to replace each line of the input with either "palindrome" or "not a palindrome". So we have to write a function that transforms something like "elephant\nABCBA\nwhatever" into "not a palindrome\npalindrome\nnot a palindrome". Let’s do this!

        -
        
        -respondPalindromes contents = unlines (map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") (lines contents))
        -    where   isPalindrome xs = xs == reverse xs
        -
        + in result
        +

        Just to show that this can be achieved in much less code (even though +it will be less readable) and to demonstrate our function composition +skill, we’re going to rework that a bit further.

        +
        main = interact $ unlines . filter ((<10) . length) . lines
        +

        Wow, we actually reduced that to just one line, which is pretty +cool!

        +

        interact can be used to make programs that are piped +some contents into them and then dump some result out or it can be used +to make programs that appear to take a line of input from the user, give +back some result based on that line and then take another line and so +on. There isn’t actually a real distinction between the two, it just +depends on how the user is supposed to use them.

        +

        Let’s make a program that continuously reads a line and then tells us +if the line is a palindrome or not. We could just use +getLine to read a line, tell the user if it’s a palindrome +and then run main all over again. But it’s simpler if we +use interact. When using interact, think about +what you need to do to transform some input into the desired output. In +our case, we have to replace each line of the input with either +"palindrome" or "not a palindrome". So we have +to write a function that transforms something like +"elephant\nABCBA\nwhatever" into +"not a palindrome\npalindrome\nnot a palindrome". Let’s do +this!

        +
        respondPalindromes contents = unlines (map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") (lines contents))
        +    where   isPalindrome xs = xs == reverse xs

        Let’s write this in point-free.

        -
        
        -respondPalindromes = unlines . map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") . lines
        -    where   isPalindrome xs = xs == reverse xs
        -
        -

        Pretty straightforward. First it turns something like "elephant\nABCBA\nwhatever" into ["elephant", "ABCBA", "whatever"] and then it maps that lambda over it, giving ["not a palindrome", "palindrome", "not a palindrome"] and then unlines joins that list into a single, newline delimited string. Now we can do

        -
        
        -main = interact respondPalindromes
        -
        +
        respondPalindromes = unlines . map (\xs -> if isPalindrome xs then "palindrome" else "not a palindrome") . lines
        +    where   isPalindrome xs = xs == reverse xs
        +

        Pretty straightforward. First it turns something like +"elephant\nABCBA\nwhatever" into +["elephant", "ABCBA", "whatever"] and then it maps that +lambda over it, giving +["not a palindrome", "palindrome", "not a palindrome"] and +then unlines joins that list into a single, newline +delimited string. Now we can do

        +
        main = interact respondPalindromes

        Let’s test this out:

        -
        
        -$ runhaskell palindromes.hs
        +
        $ runhaskell palindromes.hs
         hehe
         not a palindrome
         ABCBA
         palindrome
         cookie
        -not a palindrome
        -
        -
        -

        Even though we made a program that transforms one big string of input into another, it acts like we made a program that does it line by line. That’s because Haskell is lazy and it wants to print the first line of the result string, but it can’t because it doesn’t have the first line of the input yet. So as soon as we give it the first line of input, it prints the first line of the output. We get out of the program by issuing an end-of-line character.

        -

        We can also use this program by just piping a file into it. Let’s say we have this file:

        -
        
        -dogaroo
        +not a palindrome
        +

        Even though we made a program that transforms one big string of input +into another, it acts like we made a program that does it line by line. +That’s because Haskell is lazy and it wants to print the first line of +the result string, but it can’t because it doesn’t have the first line +of the input yet. So as soon as we give it the first line of input, it +prints the first line of the output. We get out of the program by +issuing an end-of-line character.

        +

        We can also use this program by just piping a file into it. Let’s say +we have this file:

        +
        dogaroo
         radar
         rotor
        -madam
        -
        -

        and we save it as words.txt. This is what we get by piping it into our program:

        -
        
        -$ cat words.txt | runhaskell palindromes.hs
        +madam
        +

        and we save it as words.txt. This is what we get by +piping it into our program:

        +
        $ cat words.txt | runhaskell palindromes.hs
         not a palindrome
         palindrome
         palindrome
        -palindrome
        -
        -

        Again, we get the same output as if we had run our program and put in the words ourselves at the standard input. We just don’t see the input that palindromes.hs because the input came from the file and not from us typing the words in.

        -

        So now you probably see how lazy I/O works and how we can use it to our advantage. You can just think in terms of what the output is supposed to be for some given input and write a function to do that transformation. In lazy I/O, nothing is eaten from the input until it absolutely has to be because what we want to print right now depends on that input.

        -

        So far, we’ve worked with I/O by printing out stuff to the terminal and reading from it. But what about reading and writing files? Well, in a way, we’ve already been doing that. One way to think about reading from the terminal is to imagine that it’s like reading from a (somewhat special) file. Same goes for writing to the terminal, it’s kind of like writing to a file. We can call these two files stdout and stdin, meaning standard output and standard input, respectively. Keeping that in mind, we’ll see that writing to and reading from files is very much like writing to the standard output and reading from the standard input.

        -

        We’ll start off with a really simple program that opens a file called girlfriend.txt, which contains a verse from Avril Lavigne’s #1 hit Girlfriend, and just prints it out to the terminal. Here’s girlfriend.txt:

        -
        
        -Hey! Hey! You! You!
        +palindrome
        +

        Again, we get the same output as if we had run our program and put in +the words ourselves at the standard input. We just don’t see the input +that palindromes.hs because the input came from the file +and not from us typing the words in.

        +

        So now you probably see how lazy I/O works and how we can use it to +our advantage. You can just think in terms of what the output is +supposed to be for some given input and write a function to do that +transformation. In lazy I/O, nothing is eaten from the input until it +absolutely has to be because what we want to print right now depends on +that input.

        +

        So far, we’ve worked with I/O by printing out stuff to the terminal +and reading from it. But what about reading and writing files? Well, in +a way, we’ve already been doing that. One way to think about reading +from the terminal is to imagine that it’s like reading from a (somewhat +special) file. Same goes for writing to the terminal, it’s kind of like +writing to a file. We can call these two files stdout and +stdin, meaning standard output and standard +input, respectively. Keeping that in mind, we’ll see that writing +to and reading from files is very much like writing to the standard +output and reading from the standard input.

        +

        We’ll start off with a really simple program that opens a file called +girlfriend.txt, which contains a verse from Avril Lavigne’s +#1 hit Girlfriend, and just prints it out to the terminal. +Here’s girlfriend.txt:

        +
        Hey! Hey! You! You!
         I don't like your girlfriend!
         No way! No way!
        -I think you need a new one!
        -
        +I think you need a new one!

        And here’s our program:

        -
        
        -import System.IO
        +
        import System.IO
         
         main = do
             handle <- openFile "girlfriend.txt" ReadMode
             contents <- hGetContents handle
             putStr contents
        -    hClose handle
        -
        + hClose handle

        Running it, we get the expected result:

        -
        
        -$ runhaskell girlfriend.hs
        +
        $ runhaskell girlfriend.hs
         Hey! Hey! You! You!
         I don't like your girlfriend!
         No way! No way!
        -I think you need a new one!
        -
        -

        Let’s go over this line by line. The first line is just four exclamations, to get our attention. In the second line, Avril tells us that she doesn’t like our current romantic partner. The third line serves to emphasize that disapproval, whereas the fourth line suggests we should seek out a new girlfriend.

        -

        Let’s also go over the program line by line! Our program is several I/O actions glued together with a do block. In the first line of the do block, we notice a new function called openFile. This is its type signature: openFile :: FilePath -> IOMode -> IO Handle. If you read that out loud, it states: openFile takes a file path and an IOMode and returns an I/O action that will open a file and have the file’s associated handle encapsulated as its result.

        -

        FilePath is just a type synonym for String, simply defined as:

        -
        
        -type FilePath = String
        -
        +I think you need a new one!
        +

        Let’s go over this line by line. The first line is just four +exclamations, to get our attention. In the second line, Avril tells us +that she doesn’t like our current romantic partner. The third line +serves to emphasize that disapproval, whereas the fourth line suggests +we should seek out a new girlfriend.

        +

        Let’s also go over the program line by line! Our program is several +I/O actions glued together with a do block. In the first line +of the do block, we notice a new function called openFile. This is its type signature: +openFile :: FilePath -> IOMode -> IO Handle. If you +read that out loud, it states: openFile takes a file path +and an IOMode and returns an I/O action that will open a +file and have the file’s associated handle encapsulated as its +result.

        +

        FilePath is just a type +synonym for String, simply defined as:

        +
        type FilePath = String

        IOMode is a type that’s defined like this:

        -
        
        -data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
        -
        -A FILE IN A CAKE!!! -

        Just like our type that represents the seven possible values for the days of the week, this type is an enumeration that represents what we want to do with our opened file. Very simple. Just note that this type is IOMode and not IO Mode. IO Mode would be the type of an I/O action that has a value of some type Mode as its result, but IOMode is just a simple enumeration.

        -

        Finally, it returns an I/O action that will open the specified file in the specified mode. If we bind that action to something we get a Handle. A value of type Handle represents where our file is. We’ll use that handle so we know which file to read from. It would be stupid to read a file but not bind that read to a handle because we wouldn’t be able to do anything with the file. So in our case, we bound the handle to handle.

        -

        In the next line, we see a function called hGetContents. It takes a Handle, so it knows which file to get the contents from and returns an IO String — an I/O action that holds as its result the contents of the file. This function is pretty much like getContents. The only difference is that getContents will automatically read from the standard input (that is from the terminal), whereas hGetContents takes a file handle which tells it which file to read from. In all other respects, they work the same. And just like getContents, hGetContents won’t attempt to read the file at once and store it in memory, but it will read it as needed. That’s really cool because we can treat contents as the whole contents of the file, but it’s not really loaded in memory. So if this were a really huge file, doing hGetContents wouldn’t choke up our memory, but it would read only what it needed to from the file, when it needed to.

        -

        Note the difference between the handle used to identify a file and the contents of the file, bound in our program to handle and contents. The handle is just something by which we know what our file is. If you imagine your whole file system to be a really big book and each file is a chapter in the book, the handle is a bookmark that shows where you’re currently reading (or writing) a chapter, whereas the contents are the actual chapter.

        -

        With putStr contents we just print the contents out to the standard output and then we do hClose, which takes a handle and returns an I/O action that closes the file. You have to close the file yourself after opening it with openFile!

        -

        Another way of doing what we just did is to use the withFile function, which has a type signature of withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a. It takes a path to a file, an IOMode and then it takes a function that takes a handle and returns some I/O action. What it returns is an I/O action that will open that file, do something we want with the file and then close it. The result encapsulated in the final I/O action that’s returned is the same as the result of the I/O action that the function we give it returns. This might sound a bit complicated, but it’s really simple, especially with lambdas, here’s our previous example rewritten to use withFile:

        -
        
        -import System.IO
        +
        data IOMode = ReadMode | WriteMode | AppendMode | ReadWriteMode
        +A FILE IN A CAKE!!! +

        Just like our type that represents the seven possible values for the +days of the week, this type is an enumeration that represents what we +want to do with our opened file. Very simple. Just note that this type +is IOMode and not IO Mode. +IO Mode would be the type of an I/O action that has a value +of some type Mode as its result, but IOMode is +just a simple enumeration.

        +

        Finally, it returns an I/O action that will open the specified file +in the specified mode. If we bind that action to something we get a +Handle. A value of type Handle represents +where our file is. We’ll use that handle so we know which file to read +from. It would be stupid to read a file but not bind that read to a +handle because we wouldn’t be able to do anything with the file. So in +our case, we bound the handle to handle.

        +

        In the next line, we see a function called hGetContents. It takes a +Handle, so it knows which file to get the contents from and +returns an IO String — an I/O action that holds as its +result the contents of the file. This function is pretty much like +getContents. The only difference is that +getContents will automatically read from the standard input +(that is from the terminal), whereas hGetContents takes a +file handle which tells it which file to read from. In all other +respects, they work the same. And just like getContents, +hGetContents won’t attempt to read the file at once and +store it in memory, but it will read it as needed. That’s really cool +because we can treat contents as the whole contents of the +file, but it’s not really loaded in memory. So if this were a really +huge file, doing hGetContents wouldn’t choke up our memory, +but it would read only what it needed to from the file, when it needed +to.

        +

        Note the difference between the handle used to identify a file and +the contents of the file, bound in our program to handle +and contents. The handle is just something by which we know +what our file is. If you imagine your whole file system to be a really +big book and each file is a chapter in the book, the handle is a +bookmark that shows where you’re currently reading (or writing) a +chapter, whereas the contents are the actual chapter.

        +

        With putStr contents we just print the contents out to +the standard output and then we do hClose, which takes a handle and returns +an I/O action that closes the file. You have to close the file yourself +after opening it with openFile!

        +

        Another way of doing what we just did is to use the withFile function, which has a type +signature of +withFile :: FilePath -> IOMode -> (Handle -> IO a) -> IO a. +It takes a path to a file, an IOMode and then it takes a +function that takes a handle and returns some I/O action. What it +returns is an I/O action that will open that file, do something we want +with the file and then close it. The result encapsulated in the final +I/O action that’s returned is the same as the result of the I/O action +that the function we give it returns. This might sound a bit +complicated, but it’s really simple, especially with lambdas, here’s our +previous example rewritten to use withFile:

        +
        import System.IO
         
         main = do
             withFile "girlfriend.txt" ReadMode (\handle -> do
                 contents <- hGetContents handle
        -        putStr contents)
        -
        -

        As you can see, it’s very similar to the previous piece of code. (\handle -> ... ) is the function that takes a handle and returns an I/O action and it’s usually done like this, with a lambda. The reason it has to take a function that returns an I/O action instead of just taking an I/O action to do and then close the file is because the I/O action that we’d pass to it wouldn’t know on which file to operate. This way, withFile opens the file and then passes the handle to the function we gave it. It gets an I/O action back from that function and then makes an I/O action that’s just like it, only it closes the file afterwards. Here’s how we can make our own withFile function:

        -
        
        -withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
        +        putStr contents)
        +

        As you can see, it’s very similar to the previous piece of code. +(\handle -> ... ) is the function that takes a handle +and returns an I/O action and it’s usually done like this, with a +lambda. The reason it has to take a function that returns an I/O action +instead of just taking an I/O action to do and then close the file is +because the I/O action that we’d pass to it wouldn’t know on which file +to operate. This way, withFile opens the file and then +passes the handle to the function we gave it. It gets an I/O action back +from that function and then makes an I/O action that’s just like it, +only it closes the file afterwards. Here’s how we can make our own +withFile function:

        +
        withFile' :: FilePath -> IOMode -> (Handle -> IO a) -> IO a
         withFile' path mode f = do
             handle <- openFile path mode
             result <- f handle
             hClose handle
        -    return result
        -
        -butter toast -

        We know the result will be an I/O action so we can just start off with a do. First we open the file and get a handle from it. Then, we apply handle to our function to get back the I/O action that does all the work. We bind that action to result, close the handle and then do return result. By returning the result encapsulated in the I/O action that we got from f, we make it so that our I/O action encapsulates the same result as the one we got from f handle. So if f handle returns an action that will read a number of lines from the standard input and write them to a file and have as its result encapsulated the number of lines it read, if we used that with withFile', the resulting I/O action would also have as its result the number of lines read.

        -

        Just like we have hGetContents that works like getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their counterparts without the h, only they take a handle as a parameter and operate on that specific file instead of operating on standard input or standard output. Example: putStrLn is a function that takes a string and returns an I/O action that will print out that string to the terminal and a newline after it. hPutStrLn takes a handle and a string and returns an I/O action that will write that string to the file associated with the handle and then put a newline after it. In the same vein, hGetLine takes a handle and returns an I/O action that reads a line from its file.

        -

        Loading files and then treating their contents as strings is so common that we have these three nice little functions to make our work even easier:

        -

        -readFile has a type signature of readFile :: FilePath -> IO String. -Remember, FilePath is just a fancy name for String. -readFile takes a path to a file and returns an I/O action that will read that file (lazily, of course) and bind its contents to something as a string. -It’s usually more handy than doing openFile and binding it to a handle and then doing hGetContents. -Here’s how we could have written our previous example with readFile: -

        -
        
        -import System.IO
        +    return result
        +butter toast +

        We know the result will be an I/O action so we can just start off +with a do. First we open the file and get a handle from it. +Then, we apply handle to our function to get back the I/O +action that does all the work. We bind that action to +result, close the handle and then do +return result. By returning the result +encapsulated in the I/O action that we got from f, we make +it so that our I/O action encapsulates the same result as the one we got +from f handle. So if f handle returns an +action that will read a number of lines from the standard input and +write them to a file and have as its result encapsulated the number of +lines it read, if we used that with withFile', the +resulting I/O action would also have as its result the number of lines +read.

        +

        Just like we have hGetContents that works like +getContents but for a specific file, there’s also hGetLine, hPutStr, hPutStrLn, hGetChar, etc. They work just like their +counterparts without the h, only they take a handle as a +parameter and operate on that specific file instead of operating on +standard input or standard output. Example: putStrLn is a +function that takes a string and returns an I/O action that will print +out that string to the terminal and a newline after it. +hPutStrLn takes a handle and a string and returns an I/O +action that will write that string to the file associated with the +handle and then put a newline after it. In the same vein, +hGetLine takes a handle and returns an I/O action that +reads a line from its file.

        +

        Loading files and then treating their contents as strings is so +common that we have these three nice little functions to make our work +even easier:

        +

        readFile has a type signature of +readFile :: FilePath -> IO String. Remember, +FilePath is just a fancy name for String. +readFile takes a path to a file and returns an I/O action +that will read that file (lazily, of course) and bind its contents to +something as a string. It’s usually more handy than doing +openFile and binding it to a handle and then doing +hGetContents. Here’s how we could have written our previous +example with readFile:

        +
        import System.IO
         
         main = do
             contents <- readFile "girlfriend.txt"
        -    putStr contents
        -
        -

        -Because we don’t get a handle with which to identify our file, we can’t close it manually, so Haskell does that for us when we use readFile. -

        -

        -writeFile has a type of writeFile :: FilePath -> String -> IO (). -It takes a path to a file and a string to write to that file and returns an I/O action that will do the writing. If such a file already exists, it will be stomped down to zero length before being written on. Here’s how to turn girlfriend.txt into a CAPSLOCKED version and write it to girlfriendcaps.txt: -

        -
        
        -import System.IO
        +    putStr contents
        +

        Because we don’t get a handle with which to identify our file, we +can’t close it manually, so Haskell does that for us when we use +readFile.

        +

        writeFile has a type of +writeFile :: FilePath -> String -> IO (). It takes a +path to a file and a string to write to that file and returns an I/O +action that will do the writing. If such a file already exists, it will +be stomped down to zero length before being written on. Here’s how to +turn girlfriend.txt into a CAPSLOCKED version and write it to +girlfriendcaps.txt:

        +
        import System.IO
         import Data.Char
         
         main = do
             contents <- readFile "girlfriend.txt"
        -    writeFile "girlfriendcaps.txt" (map toUpper contents)
        -
        -
        
        -$ runhaskell girlfriendtocaps.hs
        +    writeFile "girlfriendcaps.txt" (map toUpper contents)
        +
        $ runhaskell girlfriendtocaps.hs
         $ cat girlfriendcaps.txt
         HEY! HEY! YOU! YOU!
         I DON'T LIKE YOUR GIRLFRIEND!
         NO WAY! NO WAY!
        -I THINK YOU NEED A NEW ONE!
        -
        -

        appendFile has a type signature that’s just like writeFile, only appendFile doesn’t truncate the file to zero length if it already exists but it appends stuff to it.

        -

        Let’s say we have a file todo.txt that has one task per line that we have to do. Now let’s make a program that takes a line from the standard input and adds that to our to-do list.

        -
        
        -import System.IO
        +I THINK YOU NEED A NEW ONE!
        +

        appendFile has a type signature +that’s just like writeFile, only appendFile +doesn’t truncate the file to zero length if it already exists but it +appends stuff to it.

        +

        Let’s say we have a file todo.txt that has one task per line +that we have to do. Now let’s make a program that takes a line from the +standard input and adds that to our to-do list.

        +
        import System.IO
         
         main = do
             todoItem <- getLine
        -    appendFile "todo.txt" (todoItem ++ "\n")
        -
        -
        
        -$ runhaskell appendtodo.hs
        +    appendFile "todo.txt" (todoItem ++ "\n")
        +
        $ runhaskell appendtodo.hs
         Iron the dishes
         $ runhaskell appendtodo.hs
         Dust the dog
        @@ -625,33 +1140,79 @@ 

        Files and streams

        $ cat todo.txt Iron the dishes Dust the dog -Take salad out of the oven -
        -

        We needed to add the "\n" to the end of each line because getLine doesn’t give us a newline character at the end.

        -

        Ooh, one more thing. We talked about how doing contents <- hGetContents handle doesn’t cause the whole file to be read at once and stored in-memory. It’s I/O lazy, so doing this:

        -
        
        -main = do
        +Take salad out of the oven
        +

        We needed to add the "\n" to the end of each line +because getLine doesn’t give us a newline character at the +end.

        +

        Ooh, one more thing. We talked about how doing +contents <- hGetContents handle doesn’t cause the whole +file to be read at once and stored in-memory. It’s I/O lazy, so doing +this:

        +
        main = do
             withFile "something.txt" ReadMode (\handle -> do
                 contents <- hGetContents handle
        -        putStr contents)
        -
        -

        is actually like connecting a pipe from the file to the output. Just like you can think of lists as streams, you can also think of files as streams. This will read one line at a time and print it out to the terminal as it goes along. So you may be asking, how wide is this pipe then? How often will the disk be accessed? Well, for text files, the default buffering is line-buffering usually. That means that the smallest part of the file to be read at once is one line. That’s why in this case it actually reads a line, prints it to the output, reads the next line, prints it, etc. For binary files, the default buffering is usually block-buffering. That means that it will read the file chunk by chunk. The chunk size is some size that your operating system thinks is cool.

        -

        You can control how exactly buffering is done by using the hSetBuffering function. It takes a handle and a BufferMode and returns an I/O action that sets the buffering. BufferMode is a simple enumeration data type and the possible values it can hold are: NoBuffering, LineBuffering or BlockBuffering (Maybe Int). The Maybe Int is for how big the chunk should be, in bytes. If it’s Nothing, then the operating system determines the chunk size. NoBuffering means that it will be read one character at a time. NoBuffering usually sucks as a buffering mode because it has to access the disk so much.

        -

        Here’s our previous piece of code, only it doesn’t read it line by line but reads the whole file in chunks of 2048 bytes.

        -
        
        -main = do
        +        putStr contents)
        +

        is actually like connecting a pipe from the file to the output. Just +like you can think of lists as streams, you can also think of files as +streams. This will read one line at a time and print it out to the +terminal as it goes along. So you may be asking, how wide is this pipe +then? How often will the disk be accessed? Well, for text files, the +default buffering is line-buffering usually. That means that the +smallest part of the file to be read at once is one line. That’s why in +this case it actually reads a line, prints it to the output, reads the +next line, prints it, etc. For binary files, the default buffering is +usually block-buffering. That means that it will read the file chunk by +chunk. The chunk size is some size that your operating system thinks is +cool.

        +

        You can control how exactly buffering is done by using the +hSetBuffering function. It takes a handle and a +BufferMode and returns an I/O action that sets the +buffering. BufferMode is a simple enumeration data type and +the possible values it can hold are: NoBuffering, +LineBuffering or BlockBuffering (Maybe Int). +The Maybe Int is for how big the chunk should be, in bytes. +If it’s Nothing, then the operating system determines the +chunk size. NoBuffering means that it will be read one +character at a time. NoBuffering usually sucks as a +buffering mode because it has to access the disk so much.

        +

        Here’s our previous piece of code, only it doesn’t read it line by +line but reads the whole file in chunks of 2048 bytes.

        +
        main = do
             withFile "something.txt" ReadMode (\handle -> do
                 hSetBuffering handle $ BlockBuffering (Just 2048)
                 contents <- hGetContents handle
        -        putStr contents)
        -
        -

        Reading files in bigger chunks can help if we want to minimize disk access or when our file is actually a slow network resource.

        -

        We can also use hFlush, which is a function that takes a handle and returns an I/O action that will flush the buffer of the file associated with the handle. When we’re doing line-buffering, the buffer is flushed after every line. When we’re doing block-buffering, it’s after we’ve read a chunk. It’s also flushed after closing a handle. That means that when we’ve reached a newline character, the reading (or writing) mechanism reports all the data so far. But we can use hFlush to force that reporting of data that has been read so far. After flushing, the data is available to other programs that are running at the same time.

        -

        Think of reading a block-buffered file like this: your toilet bowl is set to flush itself after it has one gallon of water inside it. So you start pouring in water and once the gallon mark is reached, that water is automatically flushed and the data in the water that you’ve poured in so far is read. But you can flush the toilet manually too by pressing the button on the toilet. This makes the toilet flush and all the water (data) inside the toilet is read. In case you haven’t noticed, flushing the toilet manually is a metaphor for hFlush. This is not a very great analogy by programming analogy standards, but I wanted a real world object that can be flushed for the punchline.

        -

        We already made a program to add a new item to our to-do list in todo.txt, now let’s make a program to remove an item. I’ll just paste the code and then we’ll go over the program together so you see that it’s really easy. We’ll be using a few new functions from System.Directory and one new function from System.IO, but they’ll all be explained.

        -

        Anyway, here’s the program for removing an item from todo.txt:

        -
        
        -import System.IO
        +        putStr contents)
        +

        Reading files in bigger chunks can help if we want to minimize disk +access or when our file is actually a slow network resource.

        +

        We can also use hFlush, which is +a function that takes a handle and returns an I/O action that will flush +the buffer of the file associated with the handle. When we’re doing +line-buffering, the buffer is flushed after every line. When we’re doing +block-buffering, it’s after we’ve read a chunk. It’s also flushed after +closing a handle. That means that when we’ve reached a newline +character, the reading (or writing) mechanism reports all the data so +far. But we can use hFlush to force that reporting of data +that has been read so far. After flushing, the data is available to +other programs that are running at the same time.

        +

        Think of reading a block-buffered file like this: your toilet bowl is +set to flush itself after it has one gallon of water inside it. So you +start pouring in water and once the gallon mark is reached, that water +is automatically flushed and the data in the water that you’ve poured in +so far is read. But you can flush the toilet manually too by pressing +the button on the toilet. This makes the toilet flush and all the water +(data) inside the toilet is read. In case you haven’t noticed, flushing +the toilet manually is a metaphor for hFlush. This is not a +very great analogy by programming analogy standards, but I wanted a real +world object that can be flushed for the punchline.

        +

        We already made a program to add a new item to our to-do list in +todo.txt, now let’s make a program to remove an item. I’ll just +paste the code and then we’ll go over the program together so you see +that it’s really easy. We’ll be using a few new functions from +System.Directory and one new function from +System.IO, but they’ll all be explained.

        +

        Anyway, here’s the program for removing an item from +todo.txt:

        +
        import System.IO
         import System.Directory
         import Data.List
         
        @@ -671,19 +1232,72 @@ 

        Files and streams

        hClose handle hClose tempHandle removeFile "todo.txt" - renameFile tempName "todo.txt" -
        -

        At first, we just open todo.txt in read mode and bind its handle to handle.

        -

        Next up, we use a function that we haven’t met before which is from System.IOopenTempFile. Its name is pretty self-explanatory. It takes a path to a temporary directory and a template name for a file and opens a temporary file. We used "." for the temporary directory, because . denotes the current directory on just about any OS. We used "temp" as the template name for the temporary file, which means that the temporary file will be named temp plus some random characters. It returns an I/O action that makes the temporary file and the result in that I/O action is a pair of values: the name of the temporary file and a handle. We could just open a normal file called todo2.txt or something like that but it’s better practice to use openTempFile so you know you’re probably not overwriting anything. -

        -

        The reason we didn’t use getCurrentDirectory to get the current directory and then pass it to openTempFile but instead just passed "." to openTempFile is because . refers to the current directory on unix-like system and Windows

        -

        Next up, we bind the contents of todo.txt to contents. Then, split that string into a list of strings, each string one line. So todoTasks is now something like ["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. We zip the numbers from 0 onwards and that list with a function that takes a number, like 3, and a string, like "hey" and returns "3 - hey", so numberedTasks is ["0 - Iron the dishes", "1 - Dust the dog" .... We join that list of strings into a single newline delimited string with unlines and print that string out to the terminal. Note that instead of doing that, we could have also done mapM putStrLn numberedTasks

        -

        We ask the user which one they want to delete and wait for them to enter a number. Let’s say they want to delete number 1, which is Dust the dog, so they punch in 1. numberString is now "1" and because we want a number, not a string, we run read on that to get 1 and bind that to number.

        -

        Remember the delete and !! functions from Data.List. !! returns an element from a list with some index and delete deletes the first occurrence of an element in a list and returns a new list without that occurrence. (todoTasks !! number) (number is now 1) returns "Dust the dog". We bind todoTasks without the first occurrence of "Dust the dog" to newTodoItems and then join that into a single string with unlines before writing it to the temporary file that we opened. The old file is now unchanged and the temporary file contains all the lines that the old one does, except the one we deleted.

        -

        After that we close both the original and the temporary files and then we remove the original one with removeFile, which, as you can see, takes a path to a file and deletes it. After deleting the old todo.txt, we use renameFile to rename the temporary file to todo.txt. Be careful, removeFile and renameFile (which are both in System.Directory by the way) take file paths as their parameters, not handles.

        -

        And that’s that! We could have done this in even fewer lines, but we were very careful not to overwrite any existing files and politely asked the operating system to tell us where we can put our temporary file. Let’s give this a go!

        -
        
        -$ runhaskell deletetodo.hs
        +    renameFile tempName "todo.txt"
        +

        At first, we just open todo.txt in read mode and bind its +handle to handle.

        +

        Next up, we use a function that we haven’t met before which is from +System.IOopenTempFile. Its name is pretty +self-explanatory. It takes a path to a temporary directory and a +template name for a file and opens a temporary file. We used +"." for the temporary directory, because . +denotes the current directory on just about any OS. We used +"temp" as the template name for the temporary file, which +means that the temporary file will be named temp plus some +random characters. It returns an I/O action that makes the temporary +file and the result in that I/O action is a pair of values: the name of +the temporary file and a handle. We could just open a normal file called +todo2.txt or something like that but it’s better practice to +use openTempFile so you know you’re probably not +overwriting anything.

        +

        The reason we didn’t use getCurrentDirectory to get the +current directory and then pass it to openTempFile but +instead just passed "." to openTempFile is +because . refers to the current directory on unix-like +system and Windows

        +

        Next up, we bind the contents of todo.txt to +contents. Then, split that string into a list of strings, +each string one line. So todoTasks is now something like +["Iron the dishes", "Dust the dog", "Take salad out of the oven"]. +We zip the numbers from 0 onwards and that list with a function that +takes a number, like 3, and a string, like "hey" and +returns "3 - hey", so numberedTasks is +["0 - Iron the dishes", "1 - Dust the dog" .... We join +that list of strings into a single newline delimited string with +unlines and print that string out to the terminal. Note +that instead of doing that, we could have also done +mapM putStrLn numberedTasks

        +

        We ask the user which one they want to delete and wait for them to +enter a number. Let’s say they want to delete number 1, which is +Dust the dog, so they punch in 1. +numberString is now "1" and because we want a +number, not a string, we run read on that to get +1 and bind that to number.

        +

        Remember the delete and !! functions from +Data.List. !! returns an element from a list +with some index and delete deletes the first occurrence of +an element in a list and returns a new list without that occurrence. +(todoTasks !! number) (number is now 1) +returns "Dust the dog". We bind todoTasks +without the first occurrence of "Dust the dog" to +newTodoItems and then join that into a single string with +unlines before writing it to the temporary file that we +opened. The old file is now unchanged and the temporary file contains +all the lines that the old one does, except the one we deleted.

        +

        After that we close both the original and the temporary files and +then we remove the original one with removeFile, which, as you can see, takes a +path to a file and deletes it. After deleting the old todo.txt, +we use renameFile to rename the +temporary file to todo.txt. Be careful, removeFile +and renameFile (which are both in +System.Directory by the way) take file paths as their +parameters, not handles.

        +

        And that’s that! We could have done this in even fewer lines, but we +were very careful not to overwrite any existing files and politely asked +the operating system to tell us where we can put our temporary file. +Let’s give this a go!

        +
        $ runhaskell deletetodo.hs
         These are your TO-DO items:
         0 - Iron the dishes
         1 - Dust the dog
        @@ -703,18 +1317,45 @@ 

        Files and streams

        0 $ cat todo.txt -Take salad out of the oven -
        +Take salad out of the oven

        Command line arguments

        -COMMAND LINE ARGUMENTS!!! ARGH -

        Dealing with command line arguments is pretty much a necessity if you want to make a script or application that runs on a terminal. Luckily, Haskell’s standard library has a nice way of getting command line arguments of a program.

        -

        In the previous section, we made one program for adding a to-do item to our to-do list and one program for removing an item. There are two problems with the approach we took. The first one is that we just hardcoded the name of our to-do file in our code. We just decided that the file will be named todo.txt and that the user will never have a need for managing several to-do lists.

        -

        One way to solve that is to always ask the user which file they want to use as their to-do list. We used that approach when we wanted to know which item the user wants to delete. It works, but it’s not so good, because it requires the user to run the program, wait for the program to ask something and then tell that to the program. That’s called an interactive program and the difficult bit with interactive command line programs is this — what if you want to automate the execution of that program, like with a batch script? It’s harder to make a batch script that interacts with a program than a batch script that just calls one program or several of them.

        -

        That’s why it’s sometimes better to have the user tell the program what they want when they run the program, instead of having the program ask the user once it’s run. And what better way to have the user tell the program what they want it to do when they run it than via command line arguments!

        -

        The System.Environment module has two cool I/O actions. One is getArgs, which has a type of getArgs :: IO [String] and is an I/O action that will get the arguments that the program was run with and have as its contained result a list with the arguments. getProgName has a type of getProgName :: IO String and is an I/O action that contains the program name.

        +COMMAND LINE ARGUMENTS!!! ARGH +

        Dealing with command line arguments is pretty much a necessity if you +want to make a script or application that runs on a terminal. Luckily, +Haskell’s standard library has a nice way of getting command line +arguments of a program.

        +

        In the previous section, we made one program for adding a to-do item +to our to-do list and one program for removing an item. There are two +problems with the approach we took. The first one is that we just +hardcoded the name of our to-do file in our code. We just decided that +the file will be named todo.txt and that the user will never +have a need for managing several to-do lists.

        +

        One way to solve that is to always ask the user which file they want +to use as their to-do list. We used that approach when we wanted to know +which item the user wants to delete. It works, but it’s not so good, +because it requires the user to run the program, wait for the program to +ask something and then tell that to the program. That’s called an +interactive program and the difficult bit with interactive command line +programs is this — what if you want to automate the execution of that +program, like with a batch script? It’s harder to make a batch script +that interacts with a program than a batch script that just calls one +program or several of them.

        +

        That’s why it’s sometimes better to have the user tell the program +what they want when they run the program, instead of having the program +ask the user once it’s run. And what better way to have the user tell +the program what they want it to do when they run it than via command +line arguments!

        +

        The System.Environment module has two cool I/O actions. +One is getArgs, which has a type of +getArgs :: IO [String] and is an I/O action that will get +the arguments that the program was run with and have as its contained +result a list with the arguments. getProgName has a type of +getProgName :: IO String and is an I/O action that contains +the program name.

        Here’s a small program that demonstrates how these two work:

        -
        
        - import System.Environment
        +
        import System.Environment
          import Data.List
         
          main = do
        @@ -723,31 +1364,50 @@ 

        Command line arguments

        putStrLn "The arguments are:" mapM putStrLn args putStrLn "The program name is:" - putStrLn progName -
        -

        We bind getArgs and progName to args and progName. We say The arguments are: and then for every argument in args, we do putStrLn. Finally, we also print out the program name. Let’s compile this as arg-test.

        -
        
        -$ ./arg-test first second w00t "multi word arg"
        +    putStrLn progName
        +

        We bind getArgs and progName to +args and progName. We say +The arguments are: and then for every argument in +args, we do putStrLn. Finally, we also print +out the program name. Let’s compile this as arg-test.

        +
        $ ./arg-test first second w00t "multi word arg"
         The arguments are:
         first
         second
         w00t
         multi word arg
         The program name is:
        -arg-test
        -
        -

        Nice. Armed with this knowledge you could create some cool command line apps. In fact, let’s go ahead and make one. In the previous section, we made a separate program for adding tasks and a separate program for deleting them. Now, we’re going to join that into one program, what it does will depend on the command line arguments. We’re also going to make it so it can operate on different files, not just todo.txt.

        -

        We’ll call it simply todo and it’ll be able to do (haha!) three different things:

        +arg-test
        +

        Nice. Armed with this knowledge you could create some cool command +line apps. In fact, let’s go ahead and make one. In the previous +section, we made a separate program for adding tasks and a separate +program for deleting them. Now, we’re going to join that into one +program, what it does will depend on the command line arguments. We’re +also going to make it so it can operate on different files, not just +todo.txt.

        +

        We’ll call it simply todo and it’ll be able to do (haha!) +three different things:

          -
        • View tasks
        • -
        • Add tasks
        • -
        • Delete tasks
        • +
        • View tasks
        • +
        • Add tasks
        • +
        • Delete tasks
        -

        We’re not going to concern ourselves with possible bad input too much right now.

        -

        Our program will be made so that if we want to add the task Find the magic sword of power to the file todo.txt, we have to punch in todo add todo.txt "Find the magic sword of power" in our terminal. To view the tasks we’ll just do todo view todo.txt and to remove the task with the index of 2, we’ll do todo remove todo.txt 2.

        -

        We’ll start by making a dispatch association list. It’s going to be a simple association list that has command line arguments as keys and functions as their corresponding values. All these functions will be of type [String] -> IO (). They’re going to take the argument list as a parameter and return an I/O action that does the viewing, adding, deleting, etc.

        -
        
        -import System.Environment
        +

        We’re not going to concern ourselves with possible bad input too much +right now.

        +

        Our program will be made so that if we want to add the task +Find the magic sword of power to the file +todo.txt, we have to punch in +todo add todo.txt "Find the magic sword of power" in our +terminal. To view the tasks we’ll just do +todo view todo.txt and to remove the task with the index of +2, we’ll do todo remove todo.txt 2.

        +

        We’ll start by making a dispatch association list. It’s going to be a +simple association list that has command line arguments as keys and +functions as their corresponding values. All these functions will be of +type [String] -> IO (). They’re going to take the +argument list as a parameter and return an I/O action that does the +viewing, adding, deleting, etc.

        +
        import System.Environment
         import System.Directory
         import System.IO
         import Data.List
        @@ -756,37 +1416,74 @@ 

        Command line arguments

        dispatch = [ ("add", add) , ("view", view) , ("remove", remove) - ] -
        -

        We have yet to define main, add, view and remove, so let’s start with main:

        -
        
        -main = do
        +            ]
        +

        We have yet to define main, add, +view and remove, so let’s start with +main:

        +
        main = do
             (command:args) <- getArgs
             let (Just action) = lookup command dispatch
        -    action args
        -
        -

        First, we get the arguments and bind them to (command:args). If you remember your pattern matching, this means that the first argument will get bound to command and the rest of them will get bound to args. If we call our program like todo add todo.txt "Spank the monkey", command will be "add" and args will be ["todo.txt", "Spank the monkey"].

        -

        In the next line, we look up our command in the dispatch list. Because "add" points to add, we get Just add as a result. We use pattern matching again to extract our function out of the Maybe. What happens if our command isn’t in the dispatch list? Well then the lookup will return Nothing, but we said we won’t concern ourselves with failing gracefully too much, so the pattern matching will fail and our program will throw a fit.

        -

        Finally, we call our action function with the rest of the argument list. That will return an I/O action that either adds an item, displays a list of items or deletes an item and because that action is part of the main do block, it will get performed. If we follow our concrete example so far and our action function is add, it will get called with args (so ["todo.txt", "Spank the monkey"]) and return an I/O action that adds Spank the monkey to todo.txt.

        -

        Great! All that’s left now is to implement add, view and remove. Let’s start with add:

        -
        
        -add :: [String] -> IO ()
        -add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
        -
        -

        If we call our program like todo add todo.txt "Spank the monkey", the "add" will get bound to command in the first pattern match in the main block, whereas ["todo.txt", "Spank the monkey"] will get passed to the function that we get from the dispatch list. So, because we’re not dealing with bad input right now, we just pattern match against a list with those two elements right away and return an I/O action that appends that line to the end of the file, along with a newline character.

        -

        Next, let’s implement the list viewing functionality. If we want to view the items in a file, we do todo view todo.txt. So in the first pattern match, command will be "view" and args will be ["todo.txt"].

        -
        
        -view :: [String] -> IO ()
        +    action args
        +

        First, we get the arguments and bind them to +(command:args). If you remember your pattern matching, this +means that the first argument will get bound to command and +the rest of them will get bound to args. If we call our +program like todo add todo.txt "Spank the monkey", +command will be "add" and args +will be ["todo.txt", "Spank the monkey"].

        +

        In the next line, we look up our command in the dispatch list. +Because "add" points to add, we get +Just add as a result. We use pattern matching again to +extract our function out of the Maybe. What happens if our +command isn’t in the dispatch list? Well then the lookup will return +Nothing, but we said we won’t concern ourselves with +failing gracefully too much, so the pattern matching will fail and our +program will throw a fit.

        +

        Finally, we call our action function with the rest of +the argument list. That will return an I/O action that either adds an +item, displays a list of items or deletes an item and because that +action is part of the main do block, it will get +performed. If we follow our concrete example so far and our +action function is add, it will get called +with args (so +["todo.txt", "Spank the monkey"]) and return an I/O action +that adds Spank the monkey to todo.txt.

        +

        Great! All that’s left now is to implement add, +view and remove. Let’s start with +add:

        +
        add :: [String] -> IO ()
        +add [fileName, todoItem] = appendFile fileName (todoItem ++ "\n")
        +

        If we call our program like +todo add todo.txt "Spank the monkey", the +"add" will get bound to command in the first +pattern match in the main block, whereas +["todo.txt", "Spank the monkey"] will get passed to the +function that we get from the dispatch list. So, because we’re not +dealing with bad input right now, we just pattern match against a list +with those two elements right away and return an I/O action that appends +that line to the end of the file, along with a newline character.

        +

        Next, let’s implement the list viewing functionality. If we want to +view the items in a file, we do todo view todo.txt. So in +the first pattern match, command will be +"view" and args will be +["todo.txt"].

        +
        view :: [String] -> IO ()
         view [fileName] = do
             contents <- readFile fileName
             let todoTasks = lines contents
                 numberedTasks = zipWith (\n line -> show n ++ " - " ++ line) [0..] todoTasks
        -    putStr $ unlines numberedTasks
        -
        -

        We already did pretty much the same thing in the program that only deleted tasks when we were displaying the tasks so that the user can choose one for deletion, only here we just display the tasks.

        -

        And finally, we’re going to implement remove. It’s going to be very similar to the program that only deleted the tasks, so if you don’t understand how deleting an item here works, check out the explanation under that program. The main difference is that we’re not hardcoding todo.txt but getting it as an argument. We’re also not prompting the user for the task number to delete, we’re getting it as an argument.

        -
        
        -remove :: [String] -> IO ()
        +    putStr $ unlines numberedTasks
        +

        We already did pretty much the same thing in the program that only +deleted tasks when we were displaying the tasks so that the user can +choose one for deletion, only here we just display the tasks.

        +

        And finally, we’re going to implement remove. It’s going +to be very similar to the program that only deleted the tasks, so if you +don’t understand how deleting an item here works, check out the +explanation under that program. The main difference is that we’re not +hardcoding todo.txt but getting it as an argument. We’re also +not prompting the user for the task number to delete, we’re getting it +as an argument.

        +
        remove :: [String] -> IO ()
         remove [fileName, numberString] = do
             handle <- openFile fileName ReadMode
             (tempName, tempHandle) <- openTempFile "." "temp"
        @@ -798,12 +1495,13 @@ 

        Command line arguments

        hClose handle hClose tempHandle removeFile fileName - renameFile tempName fileName -
        -

        We opened up the file based on fileName and opened a temporary file, deleted the line with the index that the user wants to delete, wrote that to the temporary file, removed the original file and renamed the temporary file back to fileName.

        + renameFile tempName fileName
        +

        We opened up the file based on fileName and opened a +temporary file, deleted the line with the index that the user wants to +delete, wrote that to the temporary file, removed the original file and +renamed the temporary file back to fileName.

        Here’s the whole program at once, in all its glory!

        -
        
        -import System.Environment
        +
        import System.Environment
         import System.Directory
         import System.IO
         import Data.List
        @@ -841,14 +1539,23 @@ 

        Command line arguments

        hClose handle hClose tempHandle removeFile fileName - renameFile tempName fileName -
        -fresh baked salad -

        To summarize our solution: we made a dispatch association that maps from commands to functions that take some command line arguments and return an I/O action. We see what the command is and based on that we get the appropriate function from the dispatch list. We call that function with the rest of the command line arguments to get back an I/O action that will do the appropriate thing and then just perform that action!

        -

        In other languages, we might have implemented this with a big switch case statement or whatever, but using higher order functions allows us to just tell the dispatch list to give us the appropriate function and then tell that function to give us an I/O action for some command line arguments.

        + renameFile tempName fileName
        +fresh baked salad +

        To summarize our solution: we made a dispatch association that maps +from commands to functions that take some command line arguments and +return an I/O action. We see what the command is and based on that we +get the appropriate function from the dispatch list. We call that +function with the rest of the command line arguments to get back an I/O +action that will do the appropriate thing and then just perform that +action!

        +

        In other languages, we might have implemented this with a big switch +case statement or whatever, but using higher order functions allows us +to just tell the dispatch list to give us the appropriate function and +then tell that function to give us an I/O action for some command line +arguments.

        Let’s try our app out!

        -
        
        -$ ./todo view todo.txt
        +
        $ ./todo view todo.txt
         0 - Iron the dishes
         1 - Dust the dog
         2 - Take salad out of the oven
        @@ -866,149 +1573,249 @@ 

        Command line arguments

        $ ./todo view todo.txt 0 - Iron the dishes 1 - Dust the dog -2 - Pick up children from drycleaners -
        -

        Another cool thing about this is that it’s easy to add extra functionality. Just add an entry in the dispatch association list and implement the corresponding function and you’re laughing! As an exercise, you can try implementing a bump function that will take a file and a task number and return an I/O action that bumps that task to the top of the to-do list.

        -

        You could make this program fail a bit more gracefully in case of bad input (for example, if someone runs todo UP YOURS HAHAHAHA) by making an I/O action that just reports there has been an error (say, errorExit :: IO ()) and then check for possible erroneous input and if there is erroneous input, perform the error reporting I/O action. Another way is to use exceptions, which we will meet soon.

        +2 - Pick up children from drycleaners
        +

        Another cool thing about this is that it’s easy to add extra +functionality. Just add an entry in the dispatch association list and +implement the corresponding function and you’re laughing! As an +exercise, you can try implementing a bump function that +will take a file and a task number and return an I/O action that bumps +that task to the top of the to-do list.

        +

        You could make this program fail a bit more gracefully in case of bad +input (for example, if someone runs todo UP YOURS HAHAHAHA) +by making an I/O action that just reports there has been an error (say, +errorExit :: IO ()) and then check for possible erroneous +input and if there is erroneous input, perform the error reporting I/O +action. Another way is to use exceptions, which we will meet soon.

        Randomness

        -this picture is the ultimate source of randomness and wackiness -

        Many times while programming, you need to get some random data. Maybe you’re making a game where a die needs to be thrown or you need to generate some test data to test out your program. There are a lot of uses for random data when programming. Well, actually, pseudo-random, because we all know that the only true source of randomness is a monkey on a unicycle with a cheese in one hand and its butt in the other. In this section, we’ll take a look at how to make Haskell generate seemingly random data.

        -

        In most other programming languages, you have functions that give you back some random number. Each time you call that function, you get back a (hopefully) different random number. How about Haskell? Well, remember, Haskell is a pure functional language. What that means is that it has referential transparency. What THAT means is that a function, if given the same parameters twice, must produce the same result twice. That’s really cool because it allows us to reason differently about programs and it enables us to defer evaluation until we really need it. If I call a function, I can be sure that it won’t do any funny stuff before giving me the results. All that matters are its results. However, this makes it a bit tricky for getting random numbers. If I have a function like this:

        -
        
        -randomNumber :: (Num a) => a
        -randomNumber = 4
        -
        -

        It’s not very useful as a random number function because it will always return 4, even though I can assure you that the 4 is completely random, because I used a die to determine it.

        -

        How do other languages make seemingly random numbers? Well, they take various info from your computer, like the current time, how much and where you moved your mouse and what kind of noises you made behind your computer and based on that, give a number that looks really random. The combination of those factors (that randomness) is probably different in any given moment in time, so you get a different random number.

        -

        Ah. So in Haskell, we can make a random number then if we make a function that takes as its parameter that randomness and based on that returns some number (or other data type).

        -

        Enter the System.Random module. It has all the functions that satisfy our need for randomness. Let’s just dive into one of the functions it exports then, namely random. Here’s its type: random :: (RandomGen g, Random a) => g -> (a, g). Whoa! Some new typeclasses in this type declaration up in here! The RandomGen typeclass is for types that can act as sources of randomness. The Random typeclass is for things that can take on random values. A boolean value can take on a random value, namely True or False. A number can also take up a plethora of different random values. Can a function take on a random value? I don’t think so, probably not! If we try to translate the type declaration of random to English, we get something like: it takes a random generator (that’s our source of randomness) and returns a random value and a new random generator. Why does it also return a new generator as well as a random value? Well, we’ll see in a moment.

        -

        To use our random function, we have to get our hands on one of those random generators. The System.Random module exports a cool type, namely StdGen that is an instance of the RandomGen typeclass. We can either make a StdGen manually or we can tell the system to give us one based on a multitude of sort of random stuff.

        -

        To manually make a random generator, use the mkStdGen function. It has a type of mkStdGen :: Int -> StdGen. It takes an integer and based on that, gives us a random generator. Okay then, let’s try using random and mkStdGen in tandem to get a (hardly random) number.

        -
        
        -ghci> random (mkStdGen 100)
        -
        -
        
        -<interactive>:1:0:
        +
        +

        Many times while programming, you need to get some random data. Maybe +you’re making a game where a die needs to be thrown or you need to +generate some test data to test out your program. There are a lot of +uses for random data when programming. Well, actually, pseudo-random, +because we all know that the only true source of randomness is a monkey +on a unicycle with a cheese in one hand and its butt in the other. In +this section, we’ll take a look at how to make Haskell generate +seemingly random data.

        +

        In most other programming languages, you have functions that give you +back some random number. Each time you call that function, you get back +a (hopefully) different random number. How about Haskell? Well, +remember, Haskell is a pure functional language. What that means is that +it has referential transparency. What THAT means is that a function, if +given the same parameters twice, must produce the same result twice. +That’s really cool because it allows us to reason differently about +programs and it enables us to defer evaluation until we really need it. +If I call a function, I can be sure that it won’t do any funny stuff +before giving me the results. All that matters are its results. However, +this makes it a bit tricky for getting random numbers. If I have a +function like this:

        +
        randomNumber :: (Num a) => a
        +randomNumber = 4
        +

        It’s not very useful as a random number function because it will +always return 4, even though I can assure you that the 4 is +completely random, because I used a die to determine it.

        +

        How do other languages make seemingly random numbers? Well, they take +various info from your computer, like the current time, how much and +where you moved your mouse and what kind of noises you made behind your +computer and based on that, give a number that looks really random. The +combination of those factors (that randomness) is probably different in +any given moment in time, so you get a different random number.

        +

        Ah. So in Haskell, we can make a random number then if we make a +function that takes as its parameter that randomness and based on that +returns some number (or other data type).

        +

        Enter the System.Random module. It has all the functions +that satisfy our need for randomness. Let’s just dive into one of the +functions it exports then, namely random. Here’s its type: +random :: (RandomGen g, Random a) => g -> (a, g). +Whoa! Some new typeclasses in this type declaration up in here! The +RandomGen typeclass is for types that +can act as sources of randomness. The Random typeclass is for things that can take +on random values. A boolean value can take on a random value, namely +True or False. A number can also take up a +plethora of different random values. Can a function take on a random +value? I don’t think so, probably not! If we try to translate the type +declaration of random to English, we get something like: it +takes a random generator (that’s our source of randomness) and returns a +random value and a new random generator. Why does it also return a new +generator as well as a random value? Well, we’ll see in a moment.

        +

        To use our random function, we have to get our hands on +one of those random generators. The System.Random module +exports a cool type, namely StdGen that +is an instance of the RandomGen typeclass. We can either +make a StdGen manually or we can tell the system to give us +one based on a multitude of sort of random stuff.

        +

        To manually make a random generator, use the mkStdGen function. It has a type of +mkStdGen :: Int -> StdGen. It takes an integer and based +on that, gives us a random generator. Okay then, let’s try using +random and mkStdGen in tandem to get a (hardly +random) number.

        +
        ghci> random (mkStdGen 100)
        +
        <interactive>:1:0:
             Ambiguous type variable `a' in the constraint:
               `Random a' arising from a use of `random' at <interactive>:1:0-20
        -    Probable fix: add a type signature that fixes these type variable(s)
        -
        -

        What’s this? Ah, right, the random function can return a value of any type that’s part of the Random typeclass, so we have to inform Haskell what kind of type we want. Also let’s not forget that it returns a random value and a random generator in a pair.

        -
        
        -ghci> random (mkStdGen 100) :: (Int, StdGen)
        -(-1352021624,651872571 1655838864)
        -
        -

        Finally! A number that looks kind of random! The first component of the tuple is our number whereas the second component is a textual representation of our new random generator. What happens if we call random with the same random generator again?

        -
        
        -ghci> random (mkStdGen 100) :: (Int, StdGen)
        -(-1352021624,651872571 1655838864)
        -
        -

        Of course. The same result for the same parameters. So let’s try giving it a different random generator as a parameter.

        -
        
        -ghci> random (mkStdGen 949494) :: (Int, StdGen)
        -(539963926,466647808 1655838864)
        -
        -

        Alright, cool, great, a different number. We can use the type annotation to get different types back from that function.

        -
        
        -ghci> random (mkStdGen 949488) :: (Float, StdGen)
        +    Probable fix: add a type signature that fixes these type variable(s)
        +

        What’s this? Ah, right, the random function can return a +value of any type that’s part of the Random typeclass, so +we have to inform Haskell what kind of type we want. Also let’s not +forget that it returns a random value and a random generator in a +pair.

        +
        ghci> random (mkStdGen 100) :: (Int, StdGen)
        +(-1352021624,651872571 1655838864)
        +

        Finally! A number that looks kind of random! The first component of +the tuple is our number whereas the second component is a textual +representation of our new random generator. What happens if we call +random with the same random generator again?

        +
        ghci> random (mkStdGen 100) :: (Int, StdGen)
        +(-1352021624,651872571 1655838864)
        +

        Of course. The same result for the same parameters. So let’s try +giving it a different random generator as a parameter.

        +
        ghci> random (mkStdGen 949494) :: (Int, StdGen)
        +(539963926,466647808 1655838864)
        +

        Alright, cool, great, a different number. We can use the type +annotation to get different types back from that function.

        +
        ghci> random (mkStdGen 949488) :: (Float, StdGen)
         (0.8938442,1597344447 1655838864)
         ghci> random (mkStdGen 949488) :: (Bool, StdGen)
         (False,1485632275 40692)
         ghci> random (mkStdGen 949488) :: (Integer, StdGen)
        -(1691547873,1597344447 1655838864)
        -
        -

        Let’s make a function that simulates tossing a coin three times. If random didn’t return a new generator along with a random value, we’d have to make this function take three random generators as a parameter and then return coin tosses for each of them. But that sounds wrong because if one generator can make a random value of type Int (which can take on a load of different values), it should be able to make three coin tosses (which can take on precisely eight combinations). So this is where random returning a new generator along with a value really comes in handy.

        -

        We’ll represent a coin with a simple Bool. True is tails, False is heads.

        -
        
        -threeCoins :: StdGen -> (Bool, Bool, Bool)
        +(1691547873,1597344447 1655838864)
        +

        Let’s make a function that simulates tossing a coin three times. If +random didn’t return a new generator along with a random +value, we’d have to make this function take three random generators as a +parameter and then return coin tosses for each of them. But that sounds +wrong because if one generator can make a random value of type +Int (which can take on a load of different values), it +should be able to make three coin tosses (which can take on precisely +eight combinations). So this is where random returning a +new generator along with a value really comes in handy.

        +

        We’ll represent a coin with a simple Bool. +True is tails, False is heads.

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

        We call random with the generator we got as a parameter to get a coin and a new generator. Then we call it again, only this time with our new generator, to get the second coin. We do the same for the third coin. Had we called it with the same generator every time, all the coins would have had the same value and we’d only be able to get (False, False, False) or (True, True, True) as a result.

        -
        
        -ghci> threeCoins (mkStdGen 21)
        +    in  (firstCoin, secondCoin, thirdCoin)
        +

        We call random with the generator we got as a parameter +to get a coin and a new generator. Then we call it again, only this time +with our new generator, to get the second coin. We do the same for the +third coin. Had we called it with the same generator every time, all the +coins would have had the same value and we’d only be able to get +(False, False, False) or (True, True, True) as +a result.

        +
        ghci> threeCoins (mkStdGen 21)
         (True,True,True)
         ghci> threeCoins (mkStdGen 22)
         (True,False,True)
         ghci> threeCoins (mkStdGen 943)
         (True,False,True)
         ghci> threeCoins (mkStdGen 944)
        -(True,True,True)
        -
        -

        Notice that we didn’t have to do random gen :: (Bool, StdGen). That’s because we already specified that we want booleans in the type declaration of the function. That’s why Haskell can infer that we want a boolean value in this case.

        -

        So what if we want to flip four coins? Or five? Well, there’s a function called randoms that takes a generator and returns an infinite sequence of values based on that generator.

        -
        
        -ghci> take 5 $ randoms (mkStdGen 11) :: [Int]
        +(True,True,True)
        +

        Notice that we didn’t have to do +random gen :: (Bool, StdGen). That’s because we already +specified that we want booleans in the type declaration of the function. +That’s why Haskell can infer that we want a boolean value in this +case.

        +

        So what if we want to flip four coins? Or five? Well, there’s a +function called randoms that takes a +generator and returns an infinite sequence of values based on that +generator.

        +
        ghci> take 5 $ randoms (mkStdGen 11) :: [Int]
         [-1807975507,545074951,-1015194702,-1622477312,-502893664]
         ghci> take 5 $ randoms (mkStdGen 11) :: [Bool]
         [True,True,True,True,False]
         ghci> take 5 $ randoms (mkStdGen 11) :: [Float]
        -[7.904789e-2,0.62691015,0.26363158,0.12223756,0.38291094]
        -
        -

        Why doesn’t randoms return a new generator as well as a list? We could implement the randoms function very easily like this:

        -
        
        -randoms' :: (RandomGen g, Random a) => g -> [a]
        -randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
        -
        -

        A recursive definition. We get a random value and a new generator from the current generator and then make a list that has the value as its head and random numbers based on the new generator as its tail. Because we have to be able to potentially generate an infinite amount of numbers, we can’t give the new random generator back.

        -

        We could make a function that generates a finite stream of numbers and a new generator like this:

        -
        
        -finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)
        +[7.904789e-2,0.62691015,0.26363158,0.12223756,0.38291094]
        +

        Why doesn’t randoms return a new generator as well as a +list? We could implement the randoms function very easily +like this:

        +
        randoms' :: (RandomGen g, Random a) => g -> [a]
        +randoms' gen = let (value, newGen) = random gen in value:randoms' newGen
        +

        A recursive definition. We get a random value and a new generator +from the current generator and then make a list that has the value as +its head and random numbers based on the new generator as its tail. +Because we have to be able to potentially generate an infinite amount of +numbers, we can’t give the new random generator back.

        +

        We could make a function that generates a finite stream of numbers +and a new generator like this:

        +
        finiteRandoms :: (RandomGen g, Random a, Num n) => n -> g -> ([a], g)
         finiteRandoms 0 gen = ([], gen)
         finiteRandoms n gen =
             let (value, newGen) = random gen
                 (restOfList, finalGen) = finiteRandoms (n-1) newGen
        -    in  (value:restOfList, finalGen)
        -
        -

        Again, a recursive definition. We say that if we want 0 numbers, we just return an empty list and the generator that was given to us. For any other number of random values, we first get one random number and a new generator. That will be the head. Then we say that the tail will be n - 1 numbers generated with the new generator. Then we return the head and the rest of the list joined and the final generator that we got from getting the n - 1 random numbers.

        -

        What if we want a random value in some sort of range? All the random integers so far were outrageously big or small. What if we want to throw a die? Well, we use randomR for that purpose. It has a type of randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), meaning that it’s kind of like random, only it takes as its first parameter a pair of values that set the lower and upper bounds and the final value produced will be within those bounds.

        -
        
        -ghci> randomR (1,6) (mkStdGen 359353)
        +    in  (value:restOfList, finalGen)
        +

        Again, a recursive definition. We say that if we want 0 numbers, we +just return an empty list and the generator that was given to us. For +any other number of random values, we first get one random number and a +new generator. That will be the head. Then we say that the tail will be +n - 1 numbers generated with the new generator. Then we return +the head and the rest of the list joined and the final generator that we +got from getting the n - 1 random numbers.

        +

        What if we want a random value in some sort of range? All the random +integers so far were outrageously big or small. What if we want to throw +a die? Well, we use randomR for that +purpose. It has a type of +randomR :: (RandomGen g, Random a) :: (a, a) -> g -> (a, g), +meaning that it’s kind of like random, only it takes as its +first parameter a pair of values that set the lower and upper bounds and +the final value produced will be within those bounds.

        +
        ghci> randomR (1,6) (mkStdGen 359353)
         (6,1494289578 40692)
         ghci> randomR (1,6) (mkStdGen 35935335)
        -(3,1250031057 40692)
        -
        -

        There’s also randomRs, which produces a stream of random values within our defined ranges. Check this out:

        -
        
        -ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
        -"ndkxbvmomg"
        -
        +(3,1250031057 40692)
        +

        There’s also randomRs, which +produces a stream of random values within our defined ranges. Check this +out:

        +
        ghci> take 10 $ randomRs ('a','z') (mkStdGen 3) :: [Char]
        +"ndkxbvmomg"

        Nice, looks like a super secret password or something.

        -

        You may be asking yourself, what does this section have to do with I/O anyway? We haven’t done anything concerning I/O so far. Well, so far we’ve always made our random number generator manually by making it with some arbitrary integer. The problem is, if we do that in our real programs, they will always return the same random numbers, which is no good for us. That’s why System.Random offers the getStdGen I/O action, which has a type of IO StdGen. When your program starts, it asks the system for a good random number generator and stores that in a so-called global generator. getStdGen fetches you that global random generator when you bind it to something.

        +

        You may be asking yourself, what does this section have to do with +I/O anyway? We haven’t done anything concerning I/O so far. Well, so far +we’ve always made our random number generator manually by making it with +some arbitrary integer. The problem is, if we do that in our real +programs, they will always return the same random numbers, which is no +good for us. That’s why System.Random offers the getStdGen I/O action, which has a type of +IO StdGen. When your program starts, it asks the system for +a good random number generator and stores that in a so-called global +generator. getStdGen fetches you that global random +generator when you bind it to something.

        Here’s a simple program that generates a random string.

        -
        
        -import System.Random
        +
        import System.Random
         
         main = do
             gen <- getStdGen
        -    putStr $ take 20 (randomRs ('a','z') gen)
        -
        -
        
        -$ runhaskell random_string.hs
        +    putStr $ take 20 (randomRs ('a','z') gen)
        +
        $ runhaskell random_string.hs
         pybphhzzhuepknbykxhe
         $ runhaskell random_string.hs
         eiqgcxykivpudlsvvjpg
         $ runhaskell random_string.hs
         nzdceoconysdgcyqjruo
         $ runhaskell random_string.hs
        -bakzhnnuzrkgvesqplrx
        -
        -

        Be careful though, just performing getStdGen twice will ask the system for the same global generator twice. If you do this:

        -
        
        -import System.Random
        +bakzhnnuzrkgvesqplrx
        +

        Be careful though, just performing getStdGen twice will +ask the system for the same global generator twice. If you do this:

        +
        import System.Random
         
         main = do
             gen <- getStdGen
             putStrLn $ take 20 (randomRs ('a','z') gen)
             gen2 <- getStdGen
        -    putStr $ take 20 (randomRs ('a','z') gen2)
        -
        -

        you will get the same string printed out twice! One way to get two different strings of length 20 is to set up an infinite stream and then take the first 20 characters and print them out in one line and then take the second set of 20 characters and print them out in the second line. For this, we can use the splitAt function from Data.List, which splits a list at some index and returns a tuple that has the first part as the first component and the second part as the second component.

        -
        
        -import System.Random
        +    putStr $ take 20 (randomRs ('a','z') gen2)
        +

        you will get the same string printed out twice! One way to get two +different strings of length 20 is to set up an infinite stream and then +take the first 20 characters and print them out in one line and then +take the second set of 20 characters and print them out in the second +line. For this, we can use the splitAt function from +Data.List, which splits a list at some index and returns a +tuple that has the first part as the first component and the second part +as the second component.

        +
        import System.Random
         import Data.List
         
         main = do
        @@ -1017,22 +1824,25 @@ 

        Randomness

        (first20, rest) = splitAt 20 randomChars (second20, _) = splitAt 20 rest putStrLn first20 - putStr second20 -
        -

        Another way is to use the newStdGen action, which splits our current random generator into two generators. It updates the global random generator with one of them and encapsulates the other as its result.

        -
        
        -import System.Random
        +    putStr second20
        +

        Another way is to use the newStdGen action, which splits our current +random generator into two generators. It updates the global random +generator with one of them and encapsulates the other as its result.

        +
        import System.Random
         
         main = do
             gen <- getStdGen
             putStrLn $ take 20 (randomRs ('a','z') gen)
             gen' <- newStdGen
        -    putStr $ take 20 (randomRs ('a','z') gen')
        -
        -

        Not only do we get a new random generator when we bind newStdGen to something, the global one gets updated as well, so if we do getStdGen again and bind it to something, we’ll get a generator that’s not the same as gen.

        -

        Here’s a little program that will make the user guess which number it’s thinking of.

        -
        
        -import System.Random
        +    putStr $ take 20 (randomRs ('a','z') gen')
        +

        Not only do we get a new random generator when we bind +newStdGen to something, the global one gets updated as +well, so if we do getStdGen again and bind it to something, +we’ll get a generator that’s not the same as gen.

        +

        Here’s a little program that will make the user guess which number +it’s thinking of.

        +
        import System.Random
         import Control.Monad(when)
         
         main = do
        @@ -1049,16 +1859,46 @@ 

        Randomness

        if randNumber == number then putStrLn "You are correct!" else putStrLn $ "Sorry, it was " ++ show randNumber - askForNumber newGen -
        -jack of diamonds -

        We make a function askForNumber, which takes a random number generator and returns an I/O action that will prompt the user for a number and tell him if he guessed it right. In that function, we first generate a random number and a new generator based on the generator that we got as a parameter and call them randNumber and newGen. Let’s say that the number generated was 7. Then we tell the user to guess which number we’re thinking of. We perform getLine and bind its result to numberString. When the user enters 7, numberString becomes "7". Next, we use when to check if the string the user entered is an empty string. If it is, an empty I/O action of return () is performed, which effectively ends the program. If it isn’t, the action consisting of that do block right there gets performed. We use read on numberString to convert it to a number, so number is now 7.

        -

        Excuse me! If the user gives us some input here that read can’t read (like "haha"), our program will crash with an ugly error message. If you don’t want your program to crash on erroneous input, use reads, which returns an empty list when it fails to read a string. When it succeeds, it returns a singleton list with a tuple that has our desired value as one component and a string with what it didn’t consume as the other.

        -

        We check if the number that we entered is equal to the one generated randomly and give the user the appropriate message. And then we call askForNumber recursively, only this time with the new generator that we got, which gives us an I/O action that’s just like the one we performed, only it depends on a different generator and we perform it.

        -

        main consists of just getting a random generator from the system and calling askForNumber with it to get the initial action.

        + askForNumber newGen
        +jack of diamonds +

        We make a function askForNumber, which takes a random +number generator and returns an I/O action that will prompt the user for +a number and tell him if he guessed it right. In that function, we first +generate a random number and a new generator based on the generator that +we got as a parameter and call them randNumber and +newGen. Let’s say that the number generated was +7. Then we tell the user to guess which number we’re +thinking of. We perform getLine and bind its result to +numberString. When the user enters 7, +numberString becomes "7". Next, we use +when to check if the string the user entered is an empty +string. If it is, an empty I/O action of return () is +performed, which effectively ends the program. If it isn’t, the action +consisting of that do block right there gets performed. We use +read on numberString to convert it to a +number, so number is now 7.

        +
        +

        Excuse me! If the user gives us some input here that +read can’t read (like "haha"), our program +will crash with an ugly error message. If you don’t want your program to +crash on erroneous input, use reads, +which returns an empty list when it fails to read a string. When it +succeeds, it returns a singleton list with a tuple that has our desired +value as one component and a string with what it didn’t consume as the +other.

        +
        +

        We check if the number that we entered is equal to the one generated +randomly and give the user the appropriate message. And then we call +askForNumber recursively, only this time with the new +generator that we got, which gives us an I/O action that’s just like the +one we performed, only it depends on a different generator and we +perform it.

        +

        main consists of just getting a random generator from +the system and calling askForNumber with it to get the +initial action.

        Here’s our program in action!

        -
        
        -$ runhaskell guess_the_number.hs
        +
        $ runhaskell guess_the_number.hs
         Which number in the range from 1 to 10 am I thinking of? 4
         Sorry, it was 3
         Which number in the range from 1 to 10 am I thinking of? 10
        @@ -1067,12 +1907,9 @@ 

        Randomness

        Sorry, it was 4 Which number in the range from 1 to 10 am I thinking of? 5 Sorry, it was 10 -Which number in the range from 1 to 10 am I thinking of? - -
        +Which number in the range from 1 to 10 am I thinking of?

        Another way to make this same program is like this:

        -
        
        -import System.Random
        +
        import System.Random
         import Control.Monad(when)
         
         main = do
        @@ -1086,42 +1923,133 @@ 

        Randomness

        then putStrLn "You are correct!" else putStrLn $ "Sorry, it was " ++ show randNumber newStdGen - main -
        -

        It’s very similar to the previous version, only instead of making a function that takes a generator and then calls itself recursively with the new updated generator, we do all the work in main. After telling the user whether they were correct in their guess or not, we update the global generator and then call main again. Both approaches are valid but I like the first one more since it does less stuff in main and also provides us with a function that we can reuse easily.

        + main
        +

        It’s very similar to the previous version, only instead of making a +function that takes a generator and then calls itself recursively with +the new updated generator, we do all the work in main. +After telling the user whether they were correct in their guess or not, +we update the global generator and then call main again. +Both approaches are valid but I like the first one more since it does +less stuff in main and also provides us with a function +that we can reuse easily.

        Bytestrings

        -like normal string, only they byte … what a pedestrian pun this is -

        Lists are a cool and useful data structure. So far, we’ve used them pretty much everywhere. There are a multitude of functions that operate on them and Haskell’s laziness allows us to exchange the for and while loops of other languages for filtering and mapping over lists, because evaluation will only happen once it really needs to, so things like infinite lists (and even infinite lists of infinite lists!) are no problem for us. That’s why lists can also be used to represent streams, either when reading from the standard input or when reading from files. We can just open a file and read it as a string, even though it will only be accessed when the need arises.

        -

        However, processing files as strings has one drawback: it tends to be slow. As you know, String is a type synonym for [Char]. Chars don’t have a fixed size, because it takes several bytes to represent a character from, say, Unicode. Furthermore, lists are really lazy. If you have a list like [1,2,3,4], it will be evaluated only when completely necessary. So the whole list is sort of a promise of a list. Remember that [1,2,3,4] is syntactic sugar for 1:2:3:4:[]. When the first element of the list is forcibly evaluated (say by printing it), the rest of the list 2:3:4:[] is still just a promise of a list, and so on. So you can think of lists as promises that the next element will be delivered once it really has to and along with it, the promise of the element after it. It doesn’t take a big mental leap to conclude that processing a simple list of numbers as a series of promises might not be the most efficient thing in the world.

        -

        That overhead doesn’t bother us so much most of the time, but it turns out to be a liability when reading big files and manipulating them. That’s why Haskell has bytestrings. Bytestrings are sort of like lists, only each element is one byte (or 8 bits) in size. The way they handle laziness is also different.

        -

        Bytestrings come in two flavors: strict and lazy ones. Strict bytestrings reside in Data.ByteString and they do away with the laziness completely. There are no promises involved; a strict bytestring represents a series of bytes in an array. You can’t have things like infinite strict bytestrings. If you evaluate the first byte of a strict bytestring, you have to evaluate it whole. The upside is that there’s less overhead because there are no thunks (the technical term for promise) involved. The downside is that they’re likely to fill your memory up faster because they’re read into memory at once.

        -

        The other variety of bytestrings resides in Data.ByteString.Lazy. They’re lazy, but not quite as lazy as lists. Like we said before, there are as many thunks in a list as there are elements. That’s what makes them kind of slow for some purposes. Lazy bytestrings take a different approach — they are stored in chunks (not to be confused with thunks!), each chunk has a size of 32 KiB. So if you evaluate a byte in a lazy bytestring (by printing it or something), the first 32 KiB will be evaluated. After that, it’s just a promise for the rest of the chunks. Lazy bytestrings are kind of like lists of strict bytestrings with a size of 32 KiB. When you process a file with lazy bytestrings, it will be read chunk by chunk. This is cool because it won’t cause the memory usage to skyrocket and the 32 KiB probably fits neatly into your CPU’s L2 cache.

        -

        If you look through the documentation for Data.ByteString.Lazy, you’ll see that it has a lot of functions that have the same names as the ones from Data.List, only the type signatures have ByteString instead of [a] and Word8 instead of a in them. The functions with the same names mostly act the same as the ones that work on lists. Because the names are the same, we’re going to do a qualified import in a script and then load that script into GHCI to play with bytestrings.

        -
        
        -import qualified Data.ByteString.Lazy as B
        -import qualified Data.ByteString as S
        -
        -

        B has lazy bytestring types and functions, whereas S has strict ones. We’ll mostly be using the lazy version.

        -

        The function pack has the type signature pack :: [Word8] -> ByteString. What that means is that it takes a list of bytes of type Word8 and returns a ByteString. You can think of it as taking a list, which is lazy, and making it less lazy, so that it’s lazy only at 32 KiB intervals.

        -

        What’s the deal with that Word8 type? Well, it’s like Int, only that it has a much smaller range, namely 0-255. It represents an 8-bit number. And just like Int, it’s in the Num typeclass. For instance, we know that the value 5 is polymorphic in that it can act like any numeral type. Well, it can also take the type of Word8.

        -
        
        -ghci> B.pack [99,97,110]
        +
        +

        Lists are a cool and useful data structure. So far, we’ve used them +pretty much everywhere. There are a multitude of functions that operate +on them and Haskell’s laziness allows us to exchange the for and while +loops of other languages for filtering and mapping over lists, because +evaluation will only happen once it really needs to, so things like +infinite lists (and even infinite lists of infinite lists!) are no +problem for us. That’s why lists can also be used to represent streams, +either when reading from the standard input or when reading from files. +We can just open a file and read it as a string, even though it will +only be accessed when the need arises.

        +

        However, processing files as strings has one drawback: it tends to be +slow. As you know, String is a type synonym for +[Char]. Chars don’t have a fixed size, because +it takes several bytes to represent a character from, say, Unicode. +Furthermore, lists are really lazy. If you have a list like +[1,2,3,4], it will be evaluated only when completely +necessary. So the whole list is sort of a promise of a list. Remember +that [1,2,3,4] is syntactic sugar for +1:2:3:4:[]. When the first element of the list is forcibly +evaluated (say by printing it), the rest of the list +2:3:4:[] is still just a promise of a list, and so on. So +you can think of lists as promises that the next element will be +delivered once it really has to and along with it, the promise of the +element after it. It doesn’t take a big mental leap to conclude that +processing a simple list of numbers as a series of promises might not be +the most efficient thing in the world.

        +

        That overhead doesn’t bother us so much most of the time, but it +turns out to be a liability when reading big files and manipulating +them. That’s why Haskell has bytestrings. Bytestrings +are sort of like lists, only each element is one byte (or 8 bits) in +size. The way they handle laziness is also different.

        +

        Bytestrings come in two flavors: strict and lazy ones. Strict +bytestrings reside in Data.ByteString and they do away with +the laziness completely. There are no promises involved; a strict +bytestring represents a series of bytes in an array. You can’t have +things like infinite strict bytestrings. If you evaluate the first byte +of a strict bytestring, you have to evaluate it whole. The upside is +that there’s less overhead because there are no thunks (the technical +term for promise) involved. The downside is that they’re likely +to fill your memory up faster because they’re read into memory at +once.

        +

        The other variety of bytestrings resides in +Data.ByteString.Lazy. They’re lazy, but not quite as lazy +as lists. Like we said before, there are as many thunks in a list as +there are elements. That’s what makes them kind of slow for some +purposes. Lazy bytestrings take a different approach — they are stored +in chunks (not to be confused with thunks!), each chunk has a size of 32 +KiB. So if you evaluate a byte in a lazy bytestring (by printing it or +something), the first 32 KiB will be evaluated. After that, it’s just a +promise for the rest of the chunks. Lazy bytestrings are kind of like +lists of strict bytestrings with a size of 32 KiB. When you process a +file with lazy bytestrings, it will be read chunk by chunk. This is cool +because it won’t cause the memory usage to skyrocket and the 32 KiB +probably fits neatly into your CPU’s L2 cache.

        +

        If you look through the documentation +for Data.ByteString.Lazy, you’ll see that it has a lot of +functions that have the same names as the ones from +Data.List, only the type signatures have +ByteString instead of [a] and +Word8 instead of a in them. The functions with +the same names mostly act the same as the ones that work on lists. +Because the names are the same, we’re going to do a qualified import in +a script and then load that script into GHCI to play with +bytestrings.

        +
        import qualified Data.ByteString.Lazy as B
        +import qualified Data.ByteString as S
        +

        B has lazy bytestring types and functions, whereas +S has strict ones. We’ll mostly be using the lazy +version.

        +

        The function pack has the type +signature pack :: [Word8] -> ByteString. What that means +is that it takes a list of bytes of type Word8 and returns +a ByteString. You can think of it as taking a list, which +is lazy, and making it less lazy, so that it’s lazy only at 32 KiB +intervals.

        +

        What’s the deal with that Word8 type? Well, it’s like +Int, only that it has a much smaller range, namely 0-255. +It represents an 8-bit number. And just like Int, it’s in +the Num typeclass. For instance, we know that the value +5 is polymorphic in that it can act like any numeral type. +Well, it can also take the type of Word8.

        +
        ghci> B.pack [99,97,110]
         Chunk "can" Empty
         ghci> B.pack [98..120]
        -Chunk "bcdefghijklmnopqrstuvwx" Empty
        -
        -

        As you can see, you usually don’t have to worry about the Word8 too much, because the type system can make the numbers choose that type. If you try to use a big number, like 336 as a Word8, it will just wrap around to 80.

        -

        We packed only a handful of values into a ByteString, so they fit inside one chunk. The Empty is like the [] for lists.

        -

        unpack is the inverse function of pack. It takes a bytestring and turns it into a list of bytes.

        -

        fromChunks takes a list of strict bytestrings and converts it to a lazy bytestring. toChunks takes a lazy bytestring and converts it to a list of strict ones.

        -
        
        -ghci> B.fromChunks [S.pack [40,41,42], S.pack [43,44,45], S.pack [46,47,48]]
        -Chunk "()*" (Chunk "+,-" (Chunk "./0" Empty))
        -
        -

        This is good if you have a lot of small strict bytestrings and you want to process them efficiently without joining them into one big strict bytestring in memory first.

        -

        The bytestring version of : is called cons It takes a byte and a bytestring and puts the byte at the beginning. It’s lazy though, so it will make a new chunk even if the first chunk in the bytestring isn’t full. That’s why it’s better to use the strict version of cons, cons' if you’re going to be inserting a lot of bytes at the beginning of a bytestring.

        -
        
        -ghci> B.cons 85 $ B.pack [80,81,82,84]
        +Chunk "bcdefghijklmnopqrstuvwx" Empty
        +

        As you can see, you usually don’t have to worry about the +Word8 too much, because the type system can make the +numbers choose that type. If you try to use a big number, like +336 as a Word8, it will just wrap around to +80.

        +

        We packed only a handful of values into a ByteString, so +they fit inside one chunk. The Empty is like the +[] for lists.

        +

        unpack is the inverse function of +pack. It takes a bytestring and turns it into a list of +bytes.

        +

        fromChunks takes a list of strict +bytestrings and converts it to a lazy bytestring. toChunks takes a lazy bytestring and +converts it to a list of strict ones.

        +
        ghci> B.fromChunks [S.pack [40,41,42], S.pack [43,44,45], S.pack [46,47,48]]
        +Chunk "()*" (Chunk "+,-" (Chunk "./0" Empty))
        +

        This is good if you have a lot of small strict bytestrings and you +want to process them efficiently without joining them into one big +strict bytestring in memory first.

        +

        The bytestring version of : is called cons It takes a byte and a bytestring and +puts the byte at the beginning. It’s lazy though, so it will make a new +chunk even if the first chunk in the bytestring isn’t full. That’s why +it’s better to use the strict version of cons, cons' if you’re going to be inserting a +lot of bytes at the beginning of a bytestring.

        +
        ghci> B.cons 85 $ B.pack [80,81,82,84]
         Chunk "U" (Chunk "PQRT" Empty)
         ghci> B.cons' 85 $ B.pack [80,81,82,84]
         Chunk "UPQRT" Empty
        @@ -1129,14 +2057,38 @@ 

        Bytestrings

        Chunk "2" (Chunk "3" (Chunk "4" (Chunk "5" (Chunk "6" (Chunk "7" (Chunk "8" (Chunk "9" (Chunk ":" (Chunk ";" (Chunk "<" Empty)))))))))) ghci> foldr B.cons' B.empty [50..60] -Chunk "23456789:;<" Empty -
        -

        As you can see, empty makes an empty bytestring. See the difference between cons and cons'? With the foldr, we started with an empty bytestring and then went over the list of numbers from the right, adding each number to the beginning of the bytestring. When we used cons, we ended up with one chunk for every byte, which kind of defeats the purpose.

        -

        Otherwise, the bytestring modules have a load of functions that are analogous to those in Data.List, including, but not limited to, head, tail, init, null, length, map, reverse, foldl, foldr, concat, takeWhile, filter, etc.

        -

        It also has functions that have the same name and behave the same as some functions found in System.IO, only Strings are replaced with ByteStrings. For instance, the readFile function in System.IO has a type of readFile :: FilePath -> IO String, while the readFile from the bytestring modules has a type of readFile :: FilePath -> IO ByteString. Watch out, if you’re using strict bytestrings and you attempt to read a file, it will read it into memory at once! With lazy bytestrings, it will read it into neat chunks.

        -

        Let’s make a simple program that takes two filenames as command-line arguments and copies the first file into the second file. Note that System.Directory already has a function called copyFile, but we’re going to implement our own file copying function and program anyway.

        -
        
        -import System.Environment
        +Chunk "23456789:;<" Empty
        +

        As you can see, empty makes an +empty bytestring. See the difference between cons and +cons'? With the foldr, we started with an +empty bytestring and then went over the list of numbers from the right, +adding each number to the beginning of the bytestring. When we used +cons, we ended up with one chunk for every byte, which kind +of defeats the purpose.

        +

        Otherwise, the bytestring modules have a load of functions that are +analogous to those in Data.List, including, but not limited +to, head, tail, init, +null, length, map, +reverse, foldl, foldr, +concat, takeWhile, filter, +etc.

        +

        It also has functions that have the same name and behave the same as +some functions found in System.IO, only +Strings are replaced with ByteStrings. For +instance, the readFile function in System.IO +has a type of readFile :: FilePath -> IO String, while +the readFile from the bytestring +modules has a type of +readFile :: FilePath -> IO ByteString. Watch out, if +you’re using strict bytestrings and you attempt to read a file, it will +read it into memory at once! With lazy bytestrings, it will read it into +neat chunks.

        +

        Let’s make a simple program that takes two filenames as command-line +arguments and copies the first file into the second file. Note that +System.Directory already has a function called +copyFile, but we’re going to implement our own file copying +function and program anyway.

        +
        import System.Environment
         import qualified Data.ByteString.Lazy as B
         
         main = do
        @@ -1146,46 +2098,130 @@ 

        Bytestrings

        copyFile :: FilePath -> FilePath -> IO () copyFile source dest = do contents <- B.readFile source - B.writeFile dest contents -
        -

        We make our own function that takes two FilePaths (remember, FilePath is just a synonym for String) and returns an I/O action that will copy one file into another using bytestring. In the main function, we just get the arguments and call our function with them to get the I/O action, which is then performed.

        -
        
        -$ runhaskell bytestringcopy.hs something.txt ../../something.txt
        -
        -

        Notice that a program that doesn’t use bytestrings could look just like this, the only difference is that we used B.readFile and B.writeFile instead of readFile and writeFile. Many times, you can convert a program that uses normal strings to a program that uses bytestrings by just doing the necessary imports and then putting the qualified module names in front of some functions. Sometimes, you have to convert functions that you wrote to work on strings so that they work on bytestrings, but that’s not hard.

        -

        Whenever you need better performance in a program that reads a lot of data into strings, give bytestrings a try, chances are you’ll get some good performance boosts with very little effort on your part. I usually write programs by using normal strings and then convert them to use bytestrings if the performance is not satisfactory.

        + B.writeFile dest contents
        +

        We make our own function that takes two FilePaths +(remember, FilePath is just a synonym for +String) and returns an I/O action that will copy one file +into another using bytestring. In the main function, we +just get the arguments and call our function with them to get the I/O +action, which is then performed.

        +
        $ runhaskell bytestringcopy.hs something.txt ../../something.txt
        +

        Notice that a program that doesn’t use bytestrings could look just +like this, the only difference is that we used B.readFile +and B.writeFile instead of readFile and +writeFile. Many times, you can convert a program that uses +normal strings to a program that uses bytestrings by just doing the +necessary imports and then putting the qualified module names in front +of some functions. Sometimes, you have to convert functions that you +wrote to work on strings so that they work on bytestrings, but that’s +not hard.

        +

        Whenever you need better performance in a program that reads a lot of +data into strings, give bytestrings a try, chances are you’ll get some +good performance boosts with very little effort on your part. I usually +write programs by using normal strings and then convert them to use +bytestrings if the performance is not satisfactory.

        Exceptions

        -timberr!!!! -

        All languages have procedures, functions, and pieces of code that might fail in some way. That’s just a fact of life. Different languages have different ways of handling those failures. In C, we usually use some abnormal return value (like -1 or a null pointer) to indicate that what a function returned shouldn’t be treated like a normal value. Java and C#, on the other hand, tend to use exceptions to handle failure. When an exception is thrown, the control flow jumps to some code that we’ve defined that does some cleanup and then maybe re-throws the exception so that some other error handling code can take care of some other stuff.

        -

        Haskell has a very good type system. Algebraic data types allow for types like Maybe and Either and we can use values of those types to represent results that may be there or not. In C, returning, say, -1 on failure is completely a matter of convention. It only has special meaning to humans. If we’re not careful, we might treat these abnormal values as ordinary ones and then they can cause havoc and dismay in our code. Haskell’s type system gives us some much-needed safety in that aspect. A function a -> Maybe b clearly indicates that it may produce a b wrapped in Just or that it may return Nothing. The type is different from just plain a -> b and if we try to use those two functions interchangeably, the compiler will complain at us.

        -

        Despite having expressive types that support failed computations, Haskell still has support for exceptions, because they make more sense in I/O contexts. A lot of things can go wrong when dealing with the outside world because it is so unreliable. For instance, when opening a file, a bunch of things can go wrong. The file might be locked, it might not be there at all or the hard disk drive or something might not be there at all. So it’s good to be able to jump to some error handling part of our code when such an error occurs.

        -

        Okay, so I/O code (i.e. impure code) can throw exceptions. It makes sense. But what about pure code? Well, it can throw exceptions too. Think about the div and head functions. They have types of (Integral a) => a -> a -> a and [a] -> a, respectively. No Maybe or Either in their return type and yet they can both fail! div explodes in your face if you try to divide by zero and head throws a tantrum when you give it an empty list.

        -
        
        -ghci> 4 `div` 0
        +timberr!!!!
        +

        All languages have procedures, functions, and pieces of code that +might fail in some way. That’s just a fact of life. Different languages +have different ways of handling those failures. In C, we usually use +some abnormal return value (like -1 or a null pointer) to +indicate that what a function returned shouldn’t be treated like a +normal value. Java and C#, on the other hand, tend to use exceptions to +handle failure. When an exception is thrown, the control flow jumps to +some code that we’ve defined that does some cleanup and then maybe +re-throws the exception so that some other error handling code can take +care of some other stuff.

        +

        Haskell has a very good type system. Algebraic data types allow for +types like Maybe and Either and we can use +values of those types to represent results that may be there or not. In +C, returning, say, -1 on failure is completely a matter of +convention. It only has special meaning to humans. If we’re not careful, +we might treat these abnormal values as ordinary ones and then they can +cause havoc and dismay in our code. Haskell’s type system gives us some +much-needed safety in that aspect. A function +a -> Maybe b clearly indicates that it may produce a +b wrapped in Just or that it may return +Nothing. The type is different from just plain +a -> b and if we try to use those two functions +interchangeably, the compiler will complain at us.

        +

        Despite having expressive types that support failed computations, +Haskell still has support for exceptions, because they make more sense +in I/O contexts. A lot of things can go wrong when dealing with the +outside world because it is so unreliable. For instance, when opening a +file, a bunch of things can go wrong. The file might be locked, it might +not be there at all or the hard disk drive or something might not be +there at all. So it’s good to be able to jump to some error handling +part of our code when such an error occurs.

        +

        Okay, so I/O code (i.e. impure code) can throw exceptions. It makes +sense. But what about pure code? Well, it can throw exceptions too. +Think about the div and head functions. They +have types of (Integral a) => a -> a -> a and +[a] -> a, respectively. No Maybe or +Either in their return type and yet they can both fail! +div explodes in your face if you try to divide by zero and +head throws a tantrum when you give it an empty list.

        +
        ghci> 4 `div` 0
         *** Exception: divide by zero
         ghci> head []
        -*** Exception: Prelude.head: empty list
        -
        -Stop right there, criminal scum! Nobody breaks the law on my watch! Now pay your fine or it’s off to jail. -

        Pure code can throw exceptions, but it they can only be caught in the I/O part of our code (when we’re inside a do block that goes into main). That’s because you don’t know when (or if) anything will be evaluated in pure code, because it is lazy and doesn’t have a well-defined order of execution, whereas I/O code does.

        -

        Earlier, we talked about how we should spend as little time as possible in the I/O part of our program. The logic of our program should reside mostly within our pure functions, because their results are dependant only on the parameters that the functions are called with. When dealing with pure functions, you only have to think about what a function returns, because it can’t do anything else. This makes your life easier. Even though doing some logic in I/O is necessary (like opening files and the like), it should preferably be kept to a minimum. Pure functions are lazy by default, which means that we don’t know when they will be evaluated and that it really shouldn’t matter. However, once pure functions start throwing exceptions, it matters when they are evaluated. That’s why we can only catch exceptions thrown from pure functions in the I/O part of our code. And that’s bad, because we want to keep the I/O part as small as possible. However, if we don’t catch them in the I/O part of our code, our program crashes. The solution? Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful type system and use types like Either and Maybe to represent results that may have failed.

        -

        That’s why we’ll just be looking at how to use I/O exceptions for now. I/O exceptions are exceptions that are caused when something goes wrong while we are communicating with the outside world in an I/O action that’s part of main. For example, we can try opening a file and then it turns out that the file has been deleted or something. Take a look at this program that opens a file whose name is given to it as a command line argument and tells us how many lines the file has.

        -
        
        -import System.Environment
        +*** Exception: Prelude.head: empty list
        + +

        Pure code can throw exceptions, but it they can only be caught in the +I/O part of our code (when we’re inside a do block that goes +into main). That’s because you don’t know when (or if) +anything will be evaluated in pure code, because it is lazy and doesn’t +have a well-defined order of execution, whereas I/O code does.

        +

        Earlier, we talked about how we should spend as little time as +possible in the I/O part of our program. The logic of our program should +reside mostly within our pure functions, because their results are +dependant only on the parameters that the functions are called with. +When dealing with pure functions, you only have to think about what a +function returns, because it can’t do anything else. This makes your +life easier. Even though doing some logic in I/O is necessary (like +opening files and the like), it should preferably be kept to a minimum. +Pure functions are lazy by default, which means that we don’t know when +they will be evaluated and that it really shouldn’t matter. However, +once pure functions start throwing exceptions, it matters when they are +evaluated. That’s why we can only catch exceptions thrown from pure +functions in the I/O part of our code. And that’s bad, because we want +to keep the I/O part as small as possible. However, if we don’t catch +them in the I/O part of our code, our program crashes. The solution? +Don’t mix exceptions and pure code. Take advantage of Haskell’s powerful +type system and use types like Either and +Maybe to represent results that may have failed.

        +

        That’s why we’ll just be looking at how to use I/O exceptions for +now. I/O exceptions are exceptions that are caused when something goes +wrong while we are communicating with the outside world in an I/O action +that’s part of main. For example, we can try opening a file +and then it turns out that the file has been deleted or something. Take +a look at this program that opens a file whose name is given to it as a +command line argument and tells us how many lines the file has.

        +
        import System.Environment
         import System.IO
         
         main = do (fileName:_) <- getArgs
                   contents <- readFile fileName
        -          putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
        -
        -

        A very simple program. We perform the getArgs I/O action and bind the first string in the list that it yields to fileName. Then we call the contents of the file with that name contents. Lastly, we apply lines to those contents to get a list of lines and then we get the length of that list and give it to show to get a string representation of that number. It works as expected, but what happens when we give it the name of a file that doesn’t exist?

        -
        
        -$ runhaskell linecount.hs i_dont_exist.txt
        -linecount.hs: i_dont_exist.txt: openFile: does not exist (No such file or directory)
        -
        -

        Aha, we get an error from GHC, telling us that the file does not exist. Our program crashes. What if we wanted to print out a nicer message if the file doesn’t exist? One way to do that is to check if the file exists before trying to open it by using the doesFileExist function from System.Directory.

        -
        
        -import System.Environment
        +          putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!"
        +

        A very simple program. We perform the getArgs I/O action +and bind the first string in the list that it yields to +fileName. Then we call the contents of the file with that +name contents. Lastly, we apply lines to those +contents to get a list of lines and then we get the length of that list +and give it to show to get a string representation of that +number. It works as expected, but what happens when we give it the name +of a file that doesn’t exist?

        +
        $ runhaskell linecount.hs i_dont_exist.txt
        +linecount.hs: i_dont_exist.txt: openFile: does not exist (No such file or directory)
        +

        Aha, we get an error from GHC, telling us that the file does not +exist. Our program crashes. What if we wanted to print out a nicer +message if the file doesn’t exist? One way to do that is to check if the +file exists before trying to open it by using the doesFileExist function from +System.Directory.

        +
        import System.Environment
         import System.IO
         import System.Directory
         
        @@ -1194,17 +2230,49 @@ 

        Exceptions

        if fileExists then do contents <- readFile fileName putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!" - else do putStrLn "The file doesn't exist!" -
        -

        We did fileExists <- doesFileExist fileName because doesFileExist has a type of doesFileExist :: FilePath -> IO Bool, which means that it returns an I/O action that has as its result a boolean value which tells us if the file exists. We can’t just use doesFileExist in an if expression directly.

        -

        Another solution here would be to use exceptions. It’s perfectly acceptable to use them in this context. A file not existing is an exception that arises from I/O, so catching it in I/O is fine and dandy.

        -

        To deal with this by using exceptions, we’re going to take advantage of the catch function from System.IO.Error. Its type is catch :: IO a -> (IOError -> IO a) -> IO a. It takes two parameters. The first one is an I/O action. For instance, it could be an I/O action that tries to open a file. The second one is the so-called handler. If the first I/O action passed to catch throws an I/O exception, that exception gets passed to the handler, which then decides what to do. So the final result is an I/O action that will either act the same as the first parameter or it will do what the handler tells it if the first I/O action throws an exception.

        -non sequitor -

        If you’re familiar with try-catch blocks in languages like Java or Python, the catch function is similar to them. The first parameter is the thing to try, kind of like the stuff in the try block in other, imperative languages. The second parameter is the handler that takes an exception, just like most catch blocks take exceptions that you can then examine to see what happened. The handler is invoked if an exception is thrown.

        -

        The handler takes a value of type IOError, which is a value that signifies that an I/O exception occurred. It also carries information regarding the type of the exception that was thrown. How this type is implemented depends on the implementation of the language itself, which means that we can’t inspect values of the type IOError by pattern matching against them, just like we can’t pattern match against values of type IO something. We can use a bunch of useful predicates to find out stuff about values of type IOError as we’ll learn in a second.

        + else do putStrLn "The file doesn't exist!"
        +

        We did fileExists <- doesFileExist fileName because +doesFileExist has a type of +doesFileExist :: FilePath -> IO Bool, which means that +it returns an I/O action that has as its result a boolean value which +tells us if the file exists. We can’t just use +doesFileExist in an if expression directly.

        +

        Another solution here would be to use exceptions. It’s perfectly +acceptable to use them in this context. A file not existing is an +exception that arises from I/O, so catching it in I/O is fine and +dandy.

        +

        To deal with this by using exceptions, we’re going to take advantage +of the catch function from +System.IO.Error. Its type is +catch :: IO a -> (IOError -> IO a) -> IO a. It +takes two parameters. The first one is an I/O action. For instance, it +could be an I/O action that tries to open a file. The second one is the +so-called handler. If the first I/O action passed to catch +throws an I/O exception, that exception gets passed to the handler, +which then decides what to do. So the final result is an I/O action that +will either act the same as the first parameter or it will do what the +handler tells it if the first I/O action throws an exception.

        +non sequitor +

        If you’re familiar with try-catch blocks in languages like +Java or Python, the catch function is similar to them. The +first parameter is the thing to try, kind of like the stuff in the +try block in other, imperative languages. The second parameter +is the handler that takes an exception, just like most catch +blocks take exceptions that you can then examine to see what happened. +The handler is invoked if an exception is thrown.

        +

        The handler takes a value of type IOError, which is a +value that signifies that an I/O exception occurred. It also carries +information regarding the type of the exception that was thrown. How +this type is implemented depends on the implementation of the language +itself, which means that we can’t inspect values of the type +IOError by pattern matching against them, just like we +can’t pattern match against values of type IO +something. We can use a bunch of useful predicates to +find out stuff about values of type IOError as we’ll learn +in a second.

        So let’s put our new friend catch to use!

        -
        
        -import System.Environment
        +
        import System.Environment
         import System.IO
         import System.IO.Error
         
        @@ -1216,20 +2284,34 @@ 

        Exceptions

        putStrLn $ "The file has " ++ show (length (lines contents)) ++ " lines!" handler :: IOError -> IO () -handler e = putStrLn "Whoops, had some trouble!" -
        -

        First of all, you’ll see that put backticks around it so that we can use it as an infix function, because it takes two parameters. Using it as an infix function makes it more readable. So toTry `catch` handler is the same as catch toTry handler, which fits well with its type. toTry is the I/O action that we try to carry out and handler is the function that takes an IOError and returns an action to be carried out in case of an exception.

        +handler e = putStrLn "Whoops, had some trouble!"
        +

        First of all, you’ll see that put backticks around it so that we can +use it as an infix function, because it takes two parameters. Using it +as an infix function makes it more readable. So +toTry `catch` handler is the same as +catch toTry handler, which fits well with its type. +toTry is the I/O action that we try to carry out and +handler is the function that takes an IOError +and returns an action to be carried out in case of an exception.

        Let’s give this a go:

        -
        
        -$ runhaskell count_lines.hs i_exist.txt
        +
        $ runhaskell count_lines.hs i_exist.txt
         The file has 3 lines!
         
         $ runhaskell count_lines.hs i_dont_exist.txt
        -Whoops, had some trouble!
        -
        -

        In the handler, we didn’t check to see what kind of IOError we got. We just say "Whoops, had some trouble!" for any kind of error. Just catching all types of exceptions in one handler is bad practice in Haskell just like it is in most other languages. What if some other exception happens that we don’t want to catch, like us interrupting the program or something? That’s why we’re going to do the same thing that’s usually done in other languages as well: we’ll check to see what kind of exception we got. If it’s the kind of exception we’re waiting to catch, we do our stuff. If it’s not, we throw that exception back into the wild. Let’s modify our program to catch only the exceptions caused by a file not existing.

        -
        
        -import System.Environment
        +Whoops, had some trouble!
        +

        In the handler, we didn’t check to see what kind of +IOError we got. We just say +"Whoops, had some trouble!" for any kind of error. Just +catching all types of exceptions in one handler is bad practice in +Haskell just like it is in most other languages. What if some other +exception happens that we don’t want to catch, like us interrupting the +program or something? That’s why we’re going to do the same thing that’s +usually done in other languages as well: we’ll check to see what kind of +exception we got. If it’s the kind of exception we’re waiting to catch, +we do our stuff. If it’s not, we throw that exception back into the +wild. Let’s modify our program to catch only the exceptions caused by a +file not existing.

        +
        import System.Environment
         import System.IO
         import System.IO.Error
         
        @@ -1243,35 +2325,84 @@ 

        Exceptions

        handler :: IOError -> IO () handler e | isDoesNotExistError e = putStrLn "The file doesn't exist!" - | otherwise = ioError e -
        -

        Everything stays the same except the handler, which we modified to only catch a certain group of I/O exceptions. Here we used two new functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError is a predicate over IOErrors, which means that it’s a function that takes an IOError and returns a True or False, meaning it has a type of isDoesNotExistError :: IOError -> Bool. We use it on the exception that gets passed to our handler to see if it’s an error caused by a file not existing. We use guard syntax here, but we could have also used an if else. If it’s not caused by a file not existing, we re-throw the exception that was passed by the handler with the ioError function. It has a type of ioError :: IOException -> IO a, so it takes an IOError and produces an I/O action that will throw it. The I/O action has a type of IO a, because it never actually yields a result, so it can act as IO anything.

        -

        So if the exception thrown in the toTry I/O action that we glued together with a do block isn’t caused by a file not existing, toTry `catch` handler will catch that and then re-throw it. Pretty cool, huh?

        -

        There are several predicates that act on IOError and if a guard doesn’t evaluate to True, evaluation falls through to the next guard. The predicates that act on IOError are:

        + | otherwise = ioError e
        +

        Everything stays the same except the handler, which we modified to +only catch a certain group of I/O exceptions. Here we used two new +functions from System.IO.ErrorisDoesNotExistError and ioError. isDoesNotExistError +is a predicate over IOErrors, which means that it’s a +function that takes an IOError and returns a +True or False, meaning it has a type of +isDoesNotExistError :: IOError -> Bool. We use it on the +exception that gets passed to our handler to see if it’s an error caused +by a file not existing. We use guard syntax here, but +we could have also used an if else. If it’s not caused by a +file not existing, we re-throw the exception that was passed by the +handler with the ioError function. It has a type of +ioError :: IOException -> IO a, so it takes an +IOError and produces an I/O action that will throw it. The +I/O action has a type of IO a, because it never actually +yields a result, so it can act as IO anything.

        +

        So if the exception thrown in the toTry I/O action that +we glued together with a do block isn’t caused by a file not +existing, toTry `catch` handler will catch that and then +re-throw it. Pretty cool, huh?

        +

        There are several predicates that act on IOError and if +a guard doesn’t evaluate to True, evaluation falls through +to the next guard. The predicates that act on IOError +are:

          -
        • isAlreadyExistsError
        • -
        • isDoesNotExistError
        • -
        • isAlreadyInUseError
        • -
        • isFullError
        • -
        • isEOFError
        • -
        • isIllegalOperation
        • -
        • isPermissionError
        • -
        • isUserError
        • +
        • isAlreadyExistsError
        • +
        • isDoesNotExistError
        • +
        • isAlreadyInUseError
        • +
        • isFullError
        • +
        • isEOFError
        • +
        • isIllegalOperation
        • +
        • isPermissionError
        • +
        • isUserError
        -

        Most of these are pretty self-explanatory. isUserError evaluates to True when we use the function userError to make the exception, which is used for making exceptions from our code and equipping them with a string. For instance, you can do ioError $ userError "remote computer unplugged!", although it’s preferred you use types like Either and Maybe to express possible failure instead of throwing exceptions yourself with userError.

        +

        Most of these are pretty self-explanatory. isUserError +evaluates to True when we use the function userError to make the exception, which is +used for making exceptions from our code and equipping them with a +string. For instance, you can do +ioError $ userError "remote computer unplugged!", although +it’s preferred you use types like Either and +Maybe to express possible failure instead of throwing +exceptions yourself with userError.

        So you could have a handler that looks something like this:

        -
        
        -handler :: IOError -> IO ()
        +
        handler :: IOError -> IO ()
         handler e
             | isDoesNotExistError e = putStrLn "The file doesn't exist!"
             | isFullError e = freeSomeSpace
             | isIllegalOperation e = notifyCops
        -    | otherwise = ioError e
        -
        -

        Where notifyCops and freeSomeSpace are some I/O actions that you define. Be sure to re-throw exceptions if they don’t match any of your criteria, otherwise you’re causing your program to fail silently in some cases where it shouldn’t.

        -

        System.IO.Error also exports functions that enable us to ask our exceptions for some attributes, like what the handle of the file that caused the error is, or what the filename is. These start with ioe and you can see a full list of them in the documentation. Say we want to print the filename that caused our error. We can’t print the fileName that we got from getArgs, because only the IOError is passed to the handler and the handler doesn’t know about anything else. A function depends only on the parameters it was called with. That’s why we can use the ioeGetFileName function, which has a type of ioeGetFileName :: IOError -> Maybe FilePath. It takes an IOError as a parameter and maybe returns a FilePath (which is just a type synonym for String, remember, so it’s kind of the same thing). Basically, what it does is it extracts the file path from the IOError, if it can. Let’s modify our program to print out the file path that’s responsible for the exception occurring.

        -
        
        -import System.Environment
        +    | otherwise = ioError e
        +

        Where notifyCops and freeSomeSpace are some +I/O actions that you define. Be sure to re-throw exceptions if they +don’t match any of your criteria, otherwise you’re causing your program +to fail silently in some cases where it shouldn’t.

        +

        System.IO.Error also exports functions that enable us to +ask our exceptions for some attributes, like what the handle of the file +that caused the error is, or what the filename is. These start with +ioe and you can see a full +list of them in the documentation. Say we want to print the filename +that caused our error. We can’t print the fileName that we +got from getArgs, because only the IOError is +passed to the handler and the handler doesn’t know about anything else. +A function depends only on the parameters it was called with. That’s why +we can use the ioeGetFileName +function, which has a type of +ioeGetFileName :: IOError -> Maybe FilePath. It takes an +IOError as a parameter and maybe returns a +FilePath (which is just a type synonym for +String, remember, so it’s kind of the same thing). +Basically, what it does is it extracts the file path from the +IOError, if it can. Let’s modify our program to print out +the file path that’s responsible for the exception occurring.

        +
        import System.Environment
         import System.IO
         import System.IO.Error
         
        @@ -1287,17 +2418,42 @@ 

        Exceptions

        | isDoesNotExistError e = case ioeGetFileName e of Just path -> putStrLn $ "Whoops! File does not exist at: " ++ path Nothing -> putStrLn "Whoops! File does not exist at unknown location!" - | otherwise = ioError e -
        -

        In the guard where isDoesNotExistError is True, we used a case expression to call ioeGetFileName with e and then pattern match against the Maybe value that it returned. Using case expressions is commonly used when you want to pattern match against something without bringing in a new function.

        -

        You don’t have to use one handler to catch exceptions in your whole I/O part. You can just cover certain parts of your I/O code with catch or you can cover several of them with catch and use different handlers for them, like so:

        -
        
        -main = do toTry `catch` handler1
        +    | otherwise = ioError e
        +

        In the guard where isDoesNotExistError is +True, we used a case expression to call +ioeGetFileName with e and then pattern match +against the Maybe value that it returned. Using +case expressions is commonly used when you want to pattern +match against something without bringing in a new function.

        +

        You don’t have to use one handler to catch exceptions in +your whole I/O part. You can just cover certain parts of your I/O code +with catch or you can cover several of them with +catch and use different handlers for them, like so:

        +
        main = do toTry `catch` handler1
                   thenTryThis `catch` handler2
        -          launchRockets
        -
        -

        Here, toTry uses handler1 as the handler and thenTryThis uses handler2. launchRockets isn’t a parameter to catch, so whichever exceptions it might throw will likely crash our program, unless launchRockets uses catch internally to handle its own exceptions. Of course toTry, thenTryThis and launchRockets are I/O actions that have been glued together using do syntax and hypothetically defined somewhere else. This is kind of similar to try-catch blocks of other languages, where you can surround your whole program in a single try-catch or you can use a more fine-grained approach and use different ones in different parts of your code to control what kind of error handling happens where.

        -

        Now you know how to deal with I/O exceptions! Throwing exceptions from pure code and dealing with them hasn’t been covered here, mainly because, like we said, Haskell offers much better ways to indicate errors than reverting to I/O to catch them. Even when glueing together I/O actions that might fail, I prefer to have their type be something like IO (Either a b), meaning that they’re normal I/O actions but the result that they yield when performed is of type Either a b, meaning it’s either Left a or Right b.

        + launchRockets
        +

        Here, toTry uses handler1 as the handler +and thenTryThis uses handler2. +launchRockets isn’t a parameter to catch, so +whichever exceptions it might throw will likely crash our program, +unless launchRockets uses catch internally to +handle its own exceptions. Of course toTry, +thenTryThis and launchRockets are I/O actions +that have been glued together using do syntax and +hypothetically defined somewhere else. This is kind of similar to +try-catch blocks of other languages, where you can surround +your whole program in a single try-catch or you can use a more +fine-grained approach and use different ones in different parts of your +code to control what kind of error handling happens where.

        +

        Now you know how to deal with I/O exceptions! Throwing exceptions +from pure code and dealing with them hasn’t been covered here, mainly +because, like we said, Haskell offers much better ways to indicate +errors than reverting to I/O to catch them. Even when glueing together +I/O actions that might fail, I prefer to have their type be something +like IO (Either a b), meaning that they’re normal I/O +actions but the result that they yield when performed is of type +Either a b, meaning it’s either Left a or +Right b.

        • diff --git a/docs/introduction.html b/docs/introduction.html index a181fb8..21544c1 100644 --- a/docs/introduction.html +++ b/docs/introduction.html @@ -29,54 +29,142 @@
        -

        Introduction

        - +

        Introduction

        About this tutorial

        -

        -Welcome to Learn You a Haskell for Great Good! -If you’re reading this, chances are you want to learn Haskell. Well, you’ve come to the right place, but let’s talk about this tutorial a bit first. -

        -

        -I decided to write this because I wanted to solidify my own knowledge of Haskell and because I thought I could help people new to Haskell learn it from my perspective. There are quite a few tutorials on Haskell floating around on the internet. When I was starting out in Haskell, I didn’t learn from just one resource. The way I learned it was by reading several different tutorials and articles because each explained something in a different way than the other did. By going through several resources, I was able to put together the pieces and it all just came falling into place. So this is an attempt at adding another useful resource for learning Haskell so you have a bigger chance of finding one you like. -

        -bird -

        -This tutorial is aimed at people who have experience in imperative programming languages (C, C++, Java, Python …) but haven’t programmed in a functional language before (Haskell, ML, OCaml …). Although I bet that even if you don’t have any significant programming experience, a smart person such as yourself will be able to follow along and learn Haskell. -

        -

        -The channel #haskell on the Libera.Chat network is a great place to ask questions if you’re feeling stuck. People there are extremely nice, patient and understanding to newbies. -

        -

        -I failed to learn Haskell approximately 2 times before finally grasping it because it all just seemed too weird to me and I didn’t get it. But then once it just “clicked” and after getting over that initial hurdle, it was pretty much smooth sailing. I guess what I’m trying to say is: Haskell is great and if you’re interested in programming you should really learn it even if it seems weird at first. Learning Haskell is much like learning to program for the first time — it’s fun! It forces you to think differently, which brings us to the next section … -

        - +

        Welcome to Learn You a Haskell for Great Good! If +you’re reading this, chances are you want to learn Haskell. Well, you’ve +come to the right place, but let’s talk about this tutorial a bit +first.

        +

        I decided to write this because I wanted to solidify my own knowledge +of Haskell and because I thought I could help people new to Haskell +learn it from my perspective. There are quite a few tutorials on Haskell +floating around on the internet. When I was starting out in Haskell, I +didn’t learn from just one resource. The way I learned it was by reading +several different tutorials and articles because each explained +something in a different way than the other did. By going through +several resources, I was able to put together the pieces and it all just +came falling into place. So this is an attempt at adding another useful +resource for learning Haskell so you have a bigger chance of finding one +you like.

        +bird +

        This tutorial is aimed at people who have experience in imperative +programming languages (C, C++, Java, Python …) but haven’t programmed in +a functional language before (Haskell, ML, OCaml …). Although I bet that +even if you don’t have any significant programming experience, a smart +person such as yourself will be able to follow along and learn +Haskell.

        +

        The channel #haskell on the Libera.Chat network is a great place to +ask questions if you’re feeling stuck. People there are extremely nice, +patient and understanding to newbies.

        +

        I failed to learn Haskell approximately 2 times before finally +grasping it because it all just seemed too weird to me and I didn’t get +it. But then once it just “clicked” and after getting over that initial +hurdle, it was pretty much smooth sailing. I guess what I’m trying to +say is: Haskell is great and if you’re interested in programming you +should really learn it even if it seems weird at first. Learning Haskell +is much like learning to program for the first time — it’s fun! It +forces you to think differently, which brings us to the next section +…

        So what’s Haskell?

        -

        -fx -Haskell is a purely functional programming language. -In imperative languages you get things done by giving the computer a sequence of tasks and then it executes them. While executing them, it can change state. For instance, you set variable a to 5 and then do some stuff and then set it to something else. You have control flow structures for doing some action several times. In purely functional programming you don’t tell the computer what to do as such but rather you tell it what stuff is. The factorial of a number is the product of all the numbers from 1 to that number, the sum of a list of numbers is the first number plus the sum of all the other numbers, and so on. You express that in the form of functions. You also can’t set a variable to something and then set it to something else later. If you say that a is 5, you can’t say it’s something else later because you just said it was 5. What are you, some kind of liar? So in purely functional languages, a function has no side effects. The only thing a function can do is calculate something and return it as a result. At first, this seems kind of limiting but it actually has some very nice consequences: if a function is called twice with the same parameters, it’s guaranteed to return the same result. That’s called referential transparency and not only does it allow the compiler to reason about the program’s behavior, but it also allows you to easily deduce (and even prove) that a function is correct and then build more complex functions by gluing simple functions together. -

        -

        -lazy -Haskell is lazy. That means that unless specifically told otherwise, Haskell won’t execute functions and calculate things until it’s really forced to show you a result. That goes well with referential transparency and it allows you to think of programs as a series of transformations on data. It also allows cool things such as infinite data structures. Say you have an immutable list of numbers xs = [1,2,3,4,5,6,7,8] and a function doubleMe which multiplies every element by 2 and then returns a new list. If we wanted to multiply our list by 8 in an imperative language and did doubleMe(doubleMe(doubleMe(xs))), it would probably pass through the list once and make a copy and then return it. Then it would pass through the list another two times and return the result. In a lazy language, calling doubleMe on a list without forcing it to show you the result ends up in the program sort of telling you “Yeah yeah, I’ll do it later!”. But once you want to see the result, the first doubleMe tells the second one it wants the result, now! The second one says that to the third one and the third one reluctantly gives back a doubled 1, which is a 2. The second one receives that and gives back 4 to the first one. The first one sees that and tells you the first element is 8. So it only does one pass through the list and only when you really need it. That way when you want something from a lazy language you can just take some initial data and efficiently transform and mend it so it resembles what you want at the end. -

        -

        -boat -Haskell is statically typed. When you compile your program, the compiler knows which piece of code is a number, which is a string and so on. That means that a lot of possible errors are caught at compile time. If you try to add together a number and a string, the compiler will whine at you. Haskell uses a very good type system that has type inference. That means that you don’t have to explicitly label every piece of code with a type because the type system can intelligently figure out a lot about it. If you say a = 5 + 4, you don’t have to tell Haskell that a is a number, it can figure that out by itself. Type inference also allows your code to be more general. If a function you make takes two parameters and adds them together and you don’t explicitly state their type, the function will work on any two parameters that act like numbers. -

        -

        -Haskell is elegant and concise. Because it uses a lot of high level concepts, Haskell programs are usually shorter than their imperative equivalents. And shorter programs are easier to maintain than longer ones and have less bugs. -

        -

        - Haskell was made by some really smart folk (with PhDs). Work on Haskell began in 1987 when a committee of researchers got together to design a kick-ass language. In 2003 the Haskell Report was published, which defines a stable version of the language. -

        +

        fx Haskell is a purely +functional programming language. In imperative languages you +get things done by giving the computer a sequence of tasks and then it +executes them. While executing them, it can change state. For instance, +you set variable a to 5 and then do some stuff and then set +it to something else. You have control flow structures for doing some +action several times. In purely functional programming you don’t tell +the computer what to do as such but rather you tell it what stuff +is. The factorial of a number is the product of all the numbers +from 1 to that number, the sum of a list of numbers is the first number +plus the sum of all the other numbers, and so on. You express that in +the form of functions. You also can’t set a variable to something and +then set it to something else later. If you say that a is +5, you can’t say it’s something else later because you just said it was +5. What are you, some kind of liar? So in purely functional languages, a +function has no side effects. The only thing a function can do is +calculate something and return it as a result. At first, this seems kind +of limiting but it actually has some very nice consequences: if a +function is called twice with the same parameters, it’s guaranteed to +return the same result. That’s called referential transparency and not +only does it allow the compiler to reason about the program’s behavior, +but it also allows you to easily deduce (and even prove) that a function +is correct and then build more complex functions by gluing simple +functions together.

        +

        lazy Haskell is lazy. +That means that unless specifically told otherwise, Haskell won’t +execute functions and calculate things until it’s really forced to show +you a result. That goes well with referential transparency and it allows +you to think of programs as a series of transformations on +data. It also allows cool things such as infinite data +structures. Say you have an immutable list of numbers +xs = [1,2,3,4,5,6,7,8] and a function doubleMe +which multiplies every element by 2 and then returns a new list. If we +wanted to multiply our list by 8 in an imperative language and did +doubleMe(doubleMe(doubleMe(xs))), it would probably pass +through the list once and make a copy and then return it. Then it would +pass through the list another two times and return the result. In a lazy +language, calling doubleMe on a list without forcing it to +show you the result ends up in the program sort of telling you “Yeah +yeah, I’ll do it later!”. But once you want to see the result, the first +doubleMe tells the second one it wants the result, now! The +second one says that to the third one and the third one reluctantly +gives back a doubled 1, which is a 2. The second one receives that and +gives back 4 to the first one. The first one sees that and tells you the +first element is 8. So it only does one pass through the list and only +when you really need it. That way when you want something from a lazy +language you can just take some initial data and efficiently transform +and mend it so it resembles what you want at the end.

        +

        boat Haskell is statically +typed. When you compile your program, the compiler knows which +piece of code is a number, which is a string and so on. That means that +a lot of possible errors are caught at compile time. If you try to add +together a number and a string, the compiler will whine at you. Haskell +uses a very good type system that has type inference. +That means that you don’t have to explicitly label every piece of code +with a type because the type system can intelligently figure out a lot +about it. If you say a = 5 + 4, you don’t have to tell +Haskell that a is a number, it can figure that out by +itself. Type inference also allows your code to be more general. If a +function you make takes two parameters and adds them together and you +don’t explicitly state their type, the function will work on any two +parameters that act like numbers.

        +

        Haskell is elegant and concise. Because it uses a +lot of high level concepts, Haskell programs are usually shorter than +their imperative equivalents. And shorter programs are easier to +maintain than longer ones and have less bugs.

        +

        Haskell was made by some really smart folk (with +PhDs). Work on Haskell began in 1987 when a committee of researchers got +together to design a kick-ass language. In 2003 the Haskell Report was +published, which defines a stable version of the language.

        What you need to dive in

        -

        -A text editor and a Haskell compiler. You probably already have your favorite text editor installed so we won’t waste time on that. For the purposes of this tutorial we’ll be using GHC, the most widely used Haskell compiler. The best way to get started is to download GHCup, which is the recommended Haskell installer. -

        -

        -GHC can take a Haskell file (they usually have a .hs extension) and compile it but it also has an interactive mode which allows you to interactively interact with files. Interactively. You can call functions from files that you load and the results are displayed immediately. For learning it’s a lot easier and faster than compiling every time you make a change and then running the program from the prompt. The interactive mode is invoked by typing in ghci at your prompt. If you have defined some functions in a file called, say, myfunctions.hs, you load up those functions by typing in :l myfunctions and then you can play with them, provided myfunctions.hs is in the same folder from which ghci was invoked. If you change the .hs file, just run :l myfunctions again or do :r, which is equivalent because it reloads the current file. The usual workflow for me when playing around in stuff is defining some functions in a .hs file, loading it up and messing around with them and then changing the .hs file, loading it up again and so on. This is also what we’ll be doing here. -

        +

        A text editor and a Haskell compiler. You probably already have your +favorite text editor installed so we won’t waste time on that. For the +purposes of this tutorial we’ll be using GHC, the most widely used +Haskell compiler. The best way to get started is to download GHCup, which is the +recommended Haskell installer.

        +

        GHC can take a Haskell file (they usually have a .hs extension) and +compile it but it also has an interactive mode which allows you to +interactively interact with files. Interactively. You can call functions +from files that you load and the results are displayed immediately. For +learning it’s a lot easier and faster than compiling every time you make +a change and then running the program from the prompt. The interactive +mode is invoked by typing in ghci at your prompt. If you +have defined some functions in a file called, say, +myfunctions.hs, you load up those functions by typing in +:l myfunctions and then you can play with them, provided +myfunctions.hs is in the same folder from which +ghci was invoked. If you change the .hs file, just run +:l myfunctions again or do :r, which is +equivalent because it reloads the current file. The usual workflow for +me when playing around in stuff is defining some functions in a .hs +file, loading it up and messing around with them and then changing the +.hs file, loading it up again and so on. This is also what we’ll be +doing here.

        • diff --git a/docs/making-our-own-types-and-typeclasses.html b/docs/making-our-own-types-and-typeclasses.html index 2c596b1..846bfb0 100644 --- a/docs/making-our-own-types-and-typeclasses.html +++ b/docs/making-our-own-types-and-typeclasses.html @@ -31,134 +31,210 @@
        -

        Making Our Own Types and Typeclasses

        -

        In the previous chapters, we covered some existing Haskell types and typeclasses. In this chapter, we’ll learn how to make our own and how to put them to work!

        +

        Making Our Own Types and +Typeclasses

        +

        In the previous chapters, we covered some existing Haskell types and +typeclasses. In this chapter, we’ll learn how to make our own and how to +put them to work!

        Algebraic data types intro

        -

        So far, we’ve run into a lot of data types. Bool, Int, Char, Maybe, etc. But how do we make our own? Well, one way is to use the data keyword to define a type. Let’s see how the Bool type is defined in the standard library.

        -
        
        -data Bool = False | True
        -
        -

        data means that we’re defining a new data type. The part before the = denotes the type, which is Bool. The parts after the = are value constructors. They specify the different values that this type can have. The | is read as or. So we can read this as: the Bool type can have a value of True or False. Both the type name and the value constructors have to be capital cased.

        -

        In a similar fashion, we can think of the Int type as being defined like this:

        -
        
        -data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
        -
        -caveman -

        The first and last value constructors are the minimum and maximum possible values of Int. It’s not actually defined like this, the ellipses are here because we omitted a heapload of numbers, so this is just for illustrative purposes.

        -

        Now, let’s think about how we would represent a shape in Haskell. One way would be to use tuples. A circle could be denoted as (43.1, 55.0, 10.4) where the first and second fields are the coordinates of the circle’s center and the third field is the radius. Sounds OK, but those could also represent a 3D vector or anything else. A better solution would be to make our own type to represent a shape. Let’s say that a shape can be a circle or a rectangle. Here it is:

        -
        
        -data Shape = Circle Float Float Float | Rectangle Float Float Float Float
        -
        -

        -Now what’s this? Think of it like this. The Circle value constructor has three fields, which take floats. So when we write a value constructor, we can optionally add some types after it and those types define the values it will contain. Here, the first two fields are the coordinates of its center, the third one its radius. The Rectangle value constructor has four fields which accept floats. The first two are the coordinates to its upper left corner and the second two are coordinates to its lower right one. -

        -

        Now when I say fields, I actually mean parameters. Value constructors are actually functions that ultimately return a value of a data type. Let’s take a look at the type signatures for these two value constructors.

        -
        
        -ghci> :t Circle
        +

        So far, we’ve run into a lot of data types. Bool, +Int, Char, Maybe, etc. But how do +we make our own? Well, one way is to use the data +keyword to define a type. Let’s see how the Bool type is +defined in the standard library.

        +
        data Bool = False | True
        +

        data means that we’re defining a new data type. The part +before the = denotes the type, which is Bool. +The parts after the = are value +constructors. They specify the different values that this type +can have. The | is read as or. So we can read this +as: the Bool type can have a value of True or +False. Both the type name and the value constructors have +to be capital cased.

        +

        In a similar fashion, we can think of the Int type as +being defined like this:

        +
        data Int = -2147483648 | -2147483647 | ... | -1 | 0 | 1 | 2 | ... | 2147483647
        +caveman +

        The first and last value constructors are the minimum and maximum +possible values of Int. It’s not actually defined like +this, the ellipses are here because we omitted a heapload of numbers, so +this is just for illustrative purposes.

        +

        Now, let’s think about how we would represent a shape in Haskell. One +way would be to use tuples. A circle could be denoted as +(43.1, 55.0, 10.4) where the first and second fields are +the coordinates of the circle’s center and the third field is the +radius. Sounds OK, but those could also represent a 3D vector or +anything else. A better solution would be to make our own type to +represent a shape. Let’s say that a shape can be a circle or a +rectangle. Here it is:

        +
        data Shape = Circle Float Float Float | Rectangle Float Float Float Float
        +

        Now what’s this? Think of it like this. The Circle value +constructor has three fields, which take floats. So when we write a +value constructor, we can optionally add some types after it and those +types define the values it will contain. Here, the first two fields are +the coordinates of its center, the third one its radius. The +Rectangle value constructor has four fields which accept +floats. The first two are the coordinates to its upper left corner and +the second two are coordinates to its lower right one.

        +

        Now when I say fields, I actually mean parameters. Value constructors +are actually functions that ultimately return a value of a data type. +Let’s take a look at the type signatures for these two value +constructors.

        +
        ghci> :t Circle
         Circle :: Float -> Float -> Float -> Shape
         ghci> :t Rectangle
        -Rectangle :: Float -> Float -> Float -> Float -> Shape
        -
        -

        Cool, so value constructors are functions like everything else. Who would have thought? Let’s make a function that takes a shape and returns its surface.

        -
        
        -surface :: Shape -> Float
        +Rectangle :: Float -> Float -> Float -> Float -> Shape
        +

        Cool, so value constructors are functions like everything else. Who +would have thought? Let’s make a function that takes a shape and returns +its surface.

        +
        surface :: Shape -> Float
         surface (Circle _ _ r) = pi * r ^ 2
        -surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
        -
        -

        The first notable thing here is the type declaration. It says that the function takes a shape and returns a float. We couldn’t write a type declaration of Circle -> Float because Circle is not a type, Shape is. Just like we can’t write a function with a type declaration of True -> Int. The next thing we notice here is that we can pattern match against constructors. We pattern matched against constructors before (all the time actually) when we pattern matched against values like [] or False or 5, only those values didn’t have any fields. We just write a constructor and then bind its fields to names. Because we’re interested in the radius, we don’t actually care about the first two fields, which tell us where the circle is.

        -
        
        -ghci> surface $ Circle 10 20 10
        +surface (Rectangle x1 y1 x2 y2) = (abs $ x2 - x1) * (abs $ y2 - y1)
        +

        The first notable thing here is the type declaration. It says that +the function takes a shape and returns a float. We couldn’t write a type +declaration of Circle -> Float because +Circle is not a type, Shape is. Just like we +can’t write a function with a type declaration of +True -> Int. The next thing we notice here is that we +can pattern match against constructors. We pattern matched against +constructors before (all the time actually) when we pattern matched +against values like [] or False or +5, only those values didn’t have any fields. We just write +a constructor and then bind its fields to names. Because we’re +interested in the radius, we don’t actually care about the first two +fields, which tell us where the circle is.

        +
        ghci> surface $ Circle 10 20 10
         314.15927
         ghci> surface $ Rectangle 0 0 100 100
        -10000.0
        -
        -

        Yay, it works! But if we try to just print out Circle 10 20 5 in the prompt, we’ll get an error. That’s because Haskell doesn’t know how to display our data type as a string (yet). Remember, when we try to print a value out in the prompt, Haskell first runs the show function to get the string representation of our value and then it prints that out to the terminal. To make our Shape type part of the Show typeclass, we modify it like this:

        -
        
        -data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)
        -
        -

        We won’t concern ourselves with deriving too much for now. Let’s just say that if we add deriving (Show) at the end of a data declaration, Haskell automagically makes that type part of the Show typeclass. So now, we can do this:

        -
        
        -ghci> Circle 10 20 5
        +10000.0
        +

        Yay, it works! But if we try to just print out +Circle 10 20 5 in the prompt, we’ll get an error. That’s +because Haskell doesn’t know how to display our data type as a string +(yet). Remember, when we try to print a value out in the prompt, Haskell +first runs the show function to get the string +representation of our value and then it prints that out to the terminal. +To make our Shape type part of the Show +typeclass, we modify it like this:

        +
        data Shape = Circle Float Float Float | Rectangle Float Float Float Float deriving (Show)
        +

        We won’t concern ourselves with deriving too much for now. Let’s just +say that if we add deriving (Show) at the end of a +data declaration, Haskell automagically makes that type part of +the Show typeclass. So now, we can do this:

        +
        ghci> Circle 10 20 5
         Circle 10.0 20.0 5.0
         ghci> Rectangle 50 230 60 90
        -Rectangle 50.0 230.0 60.0 90.0
        -
        -

        Value constructors are functions, so we can map them and partially apply them and everything. If we want a list of concentric circles with different radii, we can do this.

        -
        
        -ghci> map (Circle 10 20) [4,5,6,6]
        -[Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]
        -
        -

        Our data type is good, although it could be better. Let’s make an intermediate data type that defines a point in two-dimensional space. Then we can use that to make our shapes more understandable.

        -
        
        -data Point = Point Float Float deriving (Show)
        -data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
        -
        -

        Notice that when defining a point, we used the same name for the data type and the value constructor. This has no special meaning, although it’s common to use the same name as the type if there’s only one value constructor. So now the Circle has two fields, one is of type Point and the other of type Float. This makes it easier to understand what’s what. Same goes for the rectangle. We have to adjust our surface function to reflect these changes.

        -
        
        -surface :: Shape -> Float
        +Rectangle 50.0 230.0 60.0 90.0
        +

        Value constructors are functions, so we can map them and partially +apply them and everything. If we want a list of concentric circles with +different radii, we can do this.

        +
        ghci> map (Circle 10 20) [4,5,6,6]
        +[Circle 10.0 20.0 4.0,Circle 10.0 20.0 5.0,Circle 10.0 20.0 6.0,Circle 10.0 20.0 6.0]
        +

        Our data type is good, although it could be better. Let’s make an +intermediate data type that defines a point in two-dimensional space. +Then we can use that to make our shapes more understandable.

        +
        data Point = Point Float Float deriving (Show)
        +data Shape = Circle Point Float | Rectangle Point Point deriving (Show)
        +

        Notice that when defining a point, we used the same name for the data +type and the value constructor. This has no special meaning, although +it’s common to use the same name as the type if there’s only one value +constructor. So now the Circle has two fields, one is of +type Point and the other of type Float. This +makes it easier to understand what’s what. Same goes for the rectangle. +We have to adjust our surface function to reflect these +changes.

        +
        surface :: Shape -> Float
         surface (Circle _ r) = pi * r ^ 2
        -surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
        -
        -

        The only thing we had to change were the patterns. We disregarded the whole point in the circle pattern. In the rectangle pattern, we just used a nested pattern matching to get the fields of the points. If we wanted to reference the points themselves for some reason, we could have used as-patterns.

        -
        
        -ghci> surface (Rectangle (Point 0 0) (Point 100 100))
        +surface (Rectangle (Point x1 y1) (Point x2 y2)) = (abs $ x2 - x1) * (abs $ y2 - y1)
        +

        The only thing we had to change were the patterns. We disregarded the +whole point in the circle pattern. In the rectangle pattern, we just +used a nested pattern matching to get the fields of the points. If we +wanted to reference the points themselves for some reason, we could have +used as-patterns.

        +
        ghci> surface (Rectangle (Point 0 0) (Point 100 100))
         10000.0
         ghci> surface (Circle (Point 0 0) 24)
        -1809.5574
        -
        -

        How about a function that nudges a shape? It takes a shape, the amount to move it on the x axis and the amount to move it on the y axis and then returns a new shape that has the same dimensions, only it’s located somewhere else.

        -
        
        -nudge :: Shape -> Float -> Float -> Shape
        +1809.5574
        +

        How about a function that nudges a shape? It takes a shape, the +amount to move it on the x axis and the amount to move it on the y axis +and then returns a new shape that has the same dimensions, only it’s +located somewhere else.

        +
        nudge :: Shape -> Float -> Float -> Shape
         nudge (Circle (Point x y) r) a b = Circle (Point (x+a) (y+b)) r
        -nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))
        -
        -

        Pretty straightforward. We add the nudge amounts to the points that denote the position of the shape.

        -
        
        -ghci> nudge (Circle (Point 34 34) 10) 5 10
        -Circle (Point 39.0 44.0) 10.0
        -
        -

        If we don’t want to deal directly with points, we can make some auxiliary functions that create shapes of some size at the zero coordinates and then nudge those.

        -
        
        -baseCircle :: Float -> Shape
        +nudge (Rectangle (Point x1 y1) (Point x2 y2)) a b = Rectangle (Point (x1+a) (y1+b)) (Point (x2+a) (y2+b))
        +

        Pretty straightforward. We add the nudge amounts to the points that +denote the position of the shape.

        +
        ghci> nudge (Circle (Point 34 34) 10) 5 10
        +Circle (Point 39.0 44.0) 10.0
        +

        If we don’t want to deal directly with points, we can make some +auxiliary functions that create shapes of some size at the zero +coordinates and then nudge those.

        +
        baseCircle :: Float -> Shape
         baseCircle r = Circle (Point 0 0) r
         
         baseRect :: Float -> Float -> Shape
        -baseRect width height = Rectangle (Point 0 0) (Point width height)
        -
        -
        
        -ghci> nudge (baseRect 40 100) 60 23
        -Rectangle (Point 60.0 23.0) (Point 100.0 123.0)
        -
        -

        You can, of course, export your data types in your modules. To do that, just write your type along with the functions you are exporting and then add some parentheses and in them specify the value constructors that you want to export for it, separated by commas. If you want to export all the value constructors for a given type, just write ...

        -

        If we wanted to export the functions and types that we defined here in a module, we could start it off like this:

        -
        
        -module Shapes
        +baseRect width height = Rectangle (Point 0 0) (Point width height)
        +
        ghci> nudge (baseRect 40 100) 60 23
        +Rectangle (Point 60.0 23.0) (Point 100.0 123.0)
        +

        You can, of course, export your data types in your modules. To do +that, just write your type along with the functions you are exporting +and then add some parentheses and in them specify the value constructors +that you want to export for it, separated by commas. If you want to +export all the value constructors for a given type, just write +...

        +

        If we wanted to export the functions and types that we defined here +in a module, we could start it off like this:

        +
        module Shapes
         ( Point(..)
         , Shape(..)
         , surface
         , nudge
         , baseCircle
         , baseRect
        -) where
        -
        -

        By doing Shape(..), we exported all the value constructors for Shape, so that means that whoever imports our module can make shapes by using the Rectangle and Circle value constructors. It’s the same as writing Shape (Rectangle, Circle).

        -

        We could also opt not to export any value constructors for Shape by just writing Shape in the export statement. That way, someone importing our module could only make shapes by using the auxiliary functions baseCircle and baseRect. Data.Map uses that approach. You can’t create a map by doing Map.Map [(1,2),(3,4)] because it doesn’t export that value constructor. However, you can make a mapping by using one of the auxiliary functions like Map.fromList. Remember, value constructors are just functions that take the fields as parameters and return a value of some type (like Shape) as a result. So when we choose not to export them, we just prevent the person importing our module from using those functions, but if some other functions that are exported return a type, we can use them to make values of our custom data types.

        -

        Not exporting the value constructors of a data types makes them more abstract in such a way that we hide their implementation. Also, whoever uses our module can’t pattern match against the value constructors.

        +) where
        +

        By doing Shape(..), we exported all the value +constructors for Shape, so that means that whoever imports +our module can make shapes by using the Rectangle and +Circle value constructors. It’s the same as writing +Shape (Rectangle, Circle).

        +

        We could also opt not to export any value constructors for +Shape by just writing Shape in the export +statement. That way, someone importing our module could only make shapes +by using the auxiliary functions baseCircle and +baseRect. Data.Map uses that approach. You +can’t create a map by doing Map.Map [(1,2),(3,4)] because +it doesn’t export that value constructor. However, you can make a +mapping by using one of the auxiliary functions like +Map.fromList. Remember, value constructors are just +functions that take the fields as parameters and return a value of some +type (like Shape) as a result. So when we choose not to +export them, we just prevent the person importing our module from using +those functions, but if some other functions that are exported return a +type, we can use them to make values of our custom data types.

        +

        Not exporting the value constructors of a data types makes them more +abstract in such a way that we hide their implementation. Also, whoever +uses our module can’t pattern match against the value constructors.

        Record syntax

        -record -

        OK, we’ve been tasked with creating a data type that describes a person. The info that we want to store about that person is: first name, last name, age, height, phone number, and favorite ice-cream flavor. I don’t know about you, but that’s all I ever want to know about a person. Let’s give it a go!

        -
        
        -data Person = Person String String Int Float String String deriving (Show)
        -
        -

        O-kay. The first field is the first name, the second is the last name, the third is the age and so on. Let’s make a person.

        -
        
        -ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
        +record
        +

        OK, we’ve been tasked with creating a data type that describes a +person. The info that we want to store about that person is: first name, +last name, age, height, phone number, and favorite ice-cream flavor. I +don’t know about you, but that’s all I ever want to know about a person. +Let’s give it a go!

        +
        data Person = Person String String Int Float String String deriving (Show)
        +

        O-kay. The first field is the first name, the second is the last +name, the third is the age and so on. Let’s make a person.

        +
        ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
         ghci> guy
        -Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
        -
        -

        That’s kind of cool, although slightly unreadable. What if we want to create a function to get separate info from a person? A function that gets some person’s first name, a function that gets some person’s last name, etc. Well, we’d have to define them kind of like this.

        -
        
        -firstName :: Person -> String
        +Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
        +

        That’s kind of cool, although slightly unreadable. What if we want to +create a function to get separate info from a person? A function that +gets some person’s first name, a function that gets some person’s last +name, etc. Well, we’d have to define them kind of like this.

        +
        firstName :: Person -> String
         firstName (Person firstname _ _ _ _ _) = firstname
         
         lastName :: Person -> String
        @@ -174,65 +250,99 @@ 

        Record syntax

        phoneNumber (Person _ _ _ _ number _) = number flavor :: Person -> String -flavor (Person _ _ _ _ _ flavor) = flavor -
        -

        Whew! I certainly did not enjoy writing that! Despite being very cumbersome and BORING to write, this method works.

        -
        
        -ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
        +flavor (Person _ _ _ _ _ flavor) = flavor
        +

        Whew! I certainly did not enjoy writing that! Despite being very +cumbersome and BORING to write, this method works.

        +
        ghci> let guy = Person "Buddy" "Finklestein" 43 184.2 "526-2928" "Chocolate"
         ghci> firstName guy
         "Buddy"
         ghci> height guy
         184.2
         ghci> flavor guy
        -"Chocolate"
        -
        +"Chocolate"

        There must be a better way, you say! Well no, there isn’t, sorry.

        -

        Just kidding, there is. Hahaha! The makers of Haskell were very smart and anticipated this scenario. They included an alternative way to write data types. Here’s how we could achieve the above functionality with record syntax.

        -
        
        -data Person = Person { firstName :: String
        +

        Just kidding, there is. Hahaha! The makers of Haskell were very smart +and anticipated this scenario. They included an alternative way to write +data types. Here’s how we could achieve the above functionality with +record syntax.

        +
        data Person = Person { firstName :: String
                              , lastName :: String
                              , age :: Int
                              , height :: Float
                              , phoneNumber :: String
                              , flavor :: String
        -                     } deriving (Show) 
        -

        So instead of just naming the field types one after another and separating them with spaces, we use curly brackets. First we write the name of the field, for instance, firstName and then we write a double colon :: (also called Paamayim Nekudotayim, haha) and then we specify the type. The resulting data type is exactly the same. The main benefit of this is that it creates functions that lookup fields in the data type. By using record syntax to create this data type, Haskell automatically made these functions: firstName, lastName, age, height, phoneNumber and flavor.

        -
        
        -ghci> :t flavor
        +                     } deriving (Show)
        +

        So instead of just naming the field types one after another and +separating them with spaces, we use curly brackets. First we write the +name of the field, for instance, firstName and then we +write a double colon :: (also called Paamayim Nekudotayim, +haha) and then we specify the type. The resulting data type is exactly +the same. The main benefit of this is that it creates functions that +lookup fields in the data type. By using record syntax to create this +data type, Haskell automatically made these functions: +firstName, lastName, age, +height, phoneNumber and +flavor.

        +
        ghci> :t flavor
         flavor :: Person -> String
         ghci> :t firstName
        -firstName :: Person -> String
        -
        -

        There’s another benefit to using record syntax. When we derive Show for the type, it displays it differently if we use record syntax to define and instantiate the type. Say we have a type that represents a car. We want to keep track of the company that made it, the model name and its year of production. Watch.

        -
        
        -data Car = Car String String Int deriving (Show)
        -
        -
        
        -ghci> Car "Ford" "Mustang" 1967
        -Car "Ford" "Mustang" 1967
        -
        -

        If we define it using record syntax, we can make a new car like this.

        -
        
        -data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)
        -
        -
        
        -ghci> Car {company="Ford", model="Mustang", year=1967}
        -Car {company = "Ford", model = "Mustang", year = 1967}
        -
        -

        When making a new car, we don’t have to necessarily put the fields in the proper order, as long as we list all of them. But if we don’t use record syntax, we have to specify them in order.

        -

        Use record syntax when a constructor has several fields and it’s not obvious which field is which. If we make a 3D vector data type by doing data Vector = Vector Int Int Int, it’s pretty obvious that the fields are the components of a vector. However, in our Person and Car types, it wasn’t so obvious and we greatly benefited from using record syntax.

        +firstName :: Person -> String
        +

        There’s another benefit to using record syntax. When we derive +Show for the type, it displays it differently if we use +record syntax to define and instantiate the type. Say we have a type +that represents a car. We want to keep track of the company that made +it, the model name and its year of production. Watch.

        +
        data Car = Car String String Int deriving (Show)
        +
        ghci> Car "Ford" "Mustang" 1967
        +Car "Ford" "Mustang" 1967
        +

        If we define it using record syntax, we can make a new car like +this.

        +
        data Car = Car {company :: String, model :: String, year :: Int} deriving (Show)
        +
        ghci> Car {company="Ford", model="Mustang", year=1967}
        +Car {company = "Ford", model = "Mustang", year = 1967}
        +

        When making a new car, we don’t have to necessarily put the fields in +the proper order, as long as we list all of them. But if we don’t use +record syntax, we have to specify them in order.

        +

        Use record syntax when a constructor has several fields and it’s not +obvious which field is which. If we make a 3D vector data type by doing +data Vector = Vector Int Int Int, it’s pretty obvious that +the fields are the components of a vector. However, in our +Person and Car types, it wasn’t so obvious and +we greatly benefited from using record syntax.

        Type parameters

        -

        A value constructor can take some values parameters and then produce a new value. For instance, the Car constructor takes three values and produces a car value. In a similar manner, type constructors can take types as parameters to produce new types. This might sound a bit too meta at first, but it’s not that complicated. If you’re familiar with templates in C++, you’ll see some parallels. To get a clear picture of what type parameters work like in action, let’s take a look at how a type we’ve already met is implemented.

        -
        
        -data Maybe a = Nothing | Just a
        -
        -yeti -

        The a here is the type parameter. And because there’s a type parameter involved, we call Maybe a type constructor. Depending on what we want this data type to hold when it’s not Nothing, this type constructor can end up producing a type of Maybe Int, Maybe Car, Maybe String, etc. No value can have a type of just Maybe, because that’s not a type per se, it’s a type constructor. In order for this to be a real type that a value can be part of, it has to have all its type parameters filled up.

        -

        So if we pass Char as the type parameter to Maybe, we get a type of Maybe Char. The value Just 'a' has a type of Maybe Char, for example.

        -

        You might not know it, but we used a type that has a type parameter before we used Maybe. That type is the list type. Although there’s some syntactic sugar in play, the list type takes a parameter to produce a concrete type. Values can have an [Int] type, a [Char] type, a [[String]] type, but you can’t have a value that just has a type of [].

        +

        A value constructor can take some values parameters and then produce +a new value. For instance, the Car constructor takes three +values and produces a car value. In a similar manner, type +constructors can take types as parameters to produce new types. +This might sound a bit too meta at first, but it’s not that complicated. +If you’re familiar with templates in C++, you’ll see some parallels. To +get a clear picture of what type parameters work like in action, let’s +take a look at how a type we’ve already met is implemented.

        +
        data Maybe a = Nothing | Just a
        +yeti +

        The a here is the type parameter. And because there’s a +type parameter involved, we call Maybe a type constructor. +Depending on what we want this data type to hold when it’s not +Nothing, this type constructor can end up producing a type +of Maybe Int, Maybe Car, +Maybe String, etc. No value can have a type of just +Maybe, because that’s not a type per se, it’s a type +constructor. In order for this to be a real type that a value can be +part of, it has to have all its type parameters filled up.

        +

        So if we pass Char as the type parameter to +Maybe, we get a type of Maybe Char. The value +Just 'a' has a type of Maybe Char, for +example.

        +

        You might not know it, but we used a type that has a type parameter +before we used Maybe. That type is the list type. Although +there’s some syntactic sugar in play, the list type takes a parameter to +produce a concrete type. Values can have an [Int] type, a +[Char] type, a [[String]] type, but you can’t +have a value that just has a type of [].

        Let’s play around with the Maybe type.

        -
        
        -ghci> Just "Haha"
        +
        ghci> Just "Haha"
         Just "Haha"
         ghci> Just 84
         Just 84
        @@ -243,62 +353,113 @@ 

        Type parameters

        ghci> :t Nothing Nothing :: Maybe a ghci> Just 10 :: Maybe Double -Just 10.0 -
        -

        Type parameters are useful because we can make different types with them depending on what kind of types we want contained in our data type. When we do :t Just "Haha", the type inference engine figures it out to be of the type Maybe [Char], because if the a in the Just a is a string, then the a in Maybe a must also be a string.

        -

        Notice that the type of Nothing is Maybe a. Its type is polymorphic. If some function requires a Maybe Int as a parameter, we can give it a Nothing, because a Nothing doesn’t contain a value anyway and so it doesn’t matter. The Maybe a type can act like a Maybe Int if it has to, just like 5 can act like an Int or a Double. Similarly, the type of the empty list is [a]. An empty list can act like a list of anything. That’s why we can do [1,2,3] ++ [] and ["ha","ha","ha"] ++ [].

        -

        Using type parameters is very beneficial, but only when using them makes sense. Usually we use them when our data type would work regardless of the type of the value it then holds inside it, like with our Maybe a type. If our type acts as some kind of box, it’s good to use them. We could change our Car data type from this:

        -
        
        -data Car = Car { company :: String
        +Just 10.0
        +

        Type parameters are useful because we can make different types with +them depending on what kind of types we want contained in our data type. +When we do :t Just "Haha", the type inference engine +figures it out to be of the type Maybe [Char], because if +the a in the Just a is a string, then the +a in Maybe a must also be a string.

        +

        Notice that the type of Nothing is Maybe a. +Its type is polymorphic. If some function requires a +Maybe Int as a parameter, we can give it a +Nothing, because a Nothing doesn’t contain a +value anyway and so it doesn’t matter. The Maybe a type can +act like a Maybe Int if it has to, just like 5 +can act like an Int or a Double. Similarly, +the type of the empty list is [a]. An empty list can act +like a list of anything. That’s why we can do [1,2,3] ++ [] +and ["ha","ha","ha"] ++ [].

        +

        Using type parameters is very beneficial, but only when using them +makes sense. Usually we use them when our data type would work +regardless of the type of the value it then holds inside it, like with +our Maybe a type. If our type acts as some kind of box, +it’s good to use them. We could change our Car data type +from this:

        +
        data Car = Car { company :: String
                        , model :: String
                        , year :: Int
        -               } deriving (Show)
        -
        + } deriving (Show)

        To this:

        -
        
        -data Car a b c = Car { company :: a
        +
        data Car a b c = Car { company :: a
                              , model :: b
                              , year :: c
        -                     } deriving (Show)
        -
        -

        But would we really benefit? The answer is: probably no, because we’d just end up defining functions that only work on the Car String String Int type. For instance, given our first definition of Car, we could make a function that displays the car’s properties in a nice little text.

        -
        
        -tellCar :: Car -> String
        -tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
        -
        -
        
        -ghci> let stang = Car {company="Ford", model="Mustang", year=1967}
        +                     } deriving (Show)
        +

        But would we really benefit? The answer is: probably no, because we’d +just end up defining functions that only work on the +Car String String Int type. For instance, given our first +definition of Car, we could make a function that displays +the car’s properties in a nice little text.

        +
        tellCar :: Car -> String
        +tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
        +
        ghci> let stang = Car {company="Ford", model="Mustang", year=1967}
         ghci> tellCar stang
        -"This Ford Mustang was made in 1967"
        -
        -

        A cute little function! The type declaration is cute and it works nicely. Now what if Car was Car a b c?

        -
        
        -tellCar :: (Show a) => Car String String a -> String
        -tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
        -
        -

        We’d have to force this function to take a Car type of (Show a) => Car String String a. You can see that the type signature is more complicated and the only benefit we’d actually get would be that we can use any type that’s an instance of the Show typeclass as the type for c.

        -
        
        -ghci> tellCar (Car "Ford" "Mustang" 1967)
        +"This Ford Mustang was made in 1967"
        +

        A cute little function! The type declaration is cute and it works +nicely. Now what if Car was Car a b c?

        +
        tellCar :: (Show a) => Car String String a -> String
        +tellCar (Car {company = c, model = m, year = y}) = "This " ++ c ++ " " ++ m ++ " was made in " ++ show y
        +

        We’d have to force this function to take a Car type of +(Show a) => Car String String a. You can see that the +type signature is more complicated and the only benefit we’d actually +get would be that we can use any type that’s an instance of the +Show typeclass as the type for c.

        +
        ghci> tellCar (Car "Ford" "Mustang" 1967)
         "This Ford Mustang was made in 1967"
         ghci> tellCar (Car "Ford" "Mustang" "nineteen sixty seven")
         "This Ford Mustang was made in \"nineteen sixty seven\""
         ghci> :t Car "Ford" "Mustang" 1967
         Car "Ford" "Mustang" 1967 :: (Num t) => Car [Char] [Char] t
         ghci> :t Car "Ford" "Mustang" "nineteen sixty seven"
        -Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char]
        -
        -meekrat -

        In real life though, we’d end up using Car String String Int most of the time and so it would seem that parameterizing the Car type isn’t really worth it. We usually use type parameters when the type that’s contained inside the data type’s various value constructors isn’t really that important for the type to work. A list of stuff is a list of stuff and it doesn’t matter what the type of that stuff is, it can still work. If we want to sum a list of numbers, we can specify later in the summing function that we specifically want a list of numbers. Same goes for Maybe. Maybe represents an option of either having nothing or having one of something. It doesn’t matter what the type of that something is.

        - -

        Another example of a parameterized type that we’ve already met is Map k v from Data.Map. The k is the type of the keys in a map and the v is the type of the values. This is a good example of where type parameters are very useful. Having maps parameterized enables us to have mappings from any type to any other type, as long as the type of the key is part of the Ord typeclass. If we were defining a mapping type, we could add a typeclass constraint in the data declaration:

        -
        
        -data (Ord k) => Map k v = ...
        -
        -

        However, it’s a very strong convention in Haskell to never add typeclass constraints in data declarations. Why? Well, because we don’t benefit a lot, but we end up writing more class constraints, even when we don’t need them. If we put or don’t put the Ord k constraint in the data declaration for Map k v, we’re going to have to put the constraint into functions that assume the keys in a map can be ordered. But if we don’t put the constraint in the data declaration, we don’t have to put (Ord k) => in the type declarations of functions that don’t care whether the keys can be ordered or not. An example of such a function is toList, that just takes a mapping and converts it to an associative list. Its type signature is toList :: Map k a -> [(k, a)]. If Map k v had a type constraint in its data declaration, the type for toList would have to be toList :: (Ord k) => Map k a -> [(k, a)], even though the function doesn’t do any comparing of keys by order.

        -

        So don’t put type constraints into data declarations even if it seems to make sense, because you’ll have to put them into the function type declarations either way.

        -

        Let’s implement a 3D vector type and add some operations for it. We’ll be using a parameterized type because even though it will usually contain numeric types, it will still support several of them.

        -
        
        -data Vector a = Vector a a a deriving (Show)
        +Car "Ford" "Mustang" "nineteen sixty seven" :: Car [Char] [Char] [Char]
        +meekrat +

        In real life though, we’d end up using +Car String String Int most of the time and so it would seem +that parameterizing the Car type isn’t really worth it. We +usually use type parameters when the type that’s contained inside the +data type’s various value constructors isn’t really that important for +the type to work. A list of stuff is a list of stuff and it doesn’t +matter what the type of that stuff is, it can still work. If we want to +sum a list of numbers, we can specify later in the summing function that +we specifically want a list of numbers. Same goes for +Maybe. Maybe represents an option of either +having nothing or having one of something. It doesn’t matter what the +type of that something is.

        +

        Another example of a parameterized type that we’ve already met is +Map k v from Data.Map. The k is +the type of the keys in a map and the v is the type of the +values. This is a good example of where type parameters are very useful. +Having maps parameterized enables us to have mappings from any type to +any other type, as long as the type of the key is part of the +Ord typeclass. If we were defining a mapping type, we could +add a typeclass constraint in the data declaration:

        +
        data (Ord k) => Map k v = ...
        +

        However, it’s a very strong convention in Haskell to never +add typeclass constraints in data declarations. Why? Well, +because we don’t benefit a lot, but we end up writing more class +constraints, even when we don’t need them. If we put or don’t put the +Ord k constraint in the data declaration for +Map k v, we’re going to have to put the constraint into +functions that assume the keys in a map can be ordered. But if we don’t +put the constraint in the data declaration, we don’t have to put +(Ord k) => in the type declarations of functions that +don’t care whether the keys can be ordered or not. An example of such a +function is toList, that just takes a mapping and converts +it to an associative list. Its type signature is +toList :: Map k a -> [(k, a)]. If Map k v +had a type constraint in its data declaration, the type for +toList would have to be +toList :: (Ord k) => Map k a -> [(k, a)], even though +the function doesn’t do any comparing of keys by order.

        +

        So don’t put type constraints into data declarations even if +it seems to make sense, because you’ll have to put them into the +function type declarations either way.

        +

        Let’s implement a 3D vector type and add some operations for it. +We’ll be using a parameterized type because even though it will usually +contain numeric types, it will still support several of them.

        +
        data Vector a = Vector a a a deriving (Show)
         
         vplus :: (Num t) => Vector t -> Vector t -> Vector t
         (Vector i j k) `vplus` (Vector l m n) = Vector (i+l) (j+m) (k+n)
        @@ -307,12 +468,30 @@ 

        Type parameters

        (Vector i j k) `vectMult` m = Vector (i*m) (j*m) (k*m) scalarMult :: (Num t) => Vector t -> Vector t -> t -(Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n -
        -

        vplus is for adding two vectors together. Two vectors are added just by adding their corresponding components. scalarMult is for the scalar product of two vectors and vectMult is for multiplying a vector with a scalar. These functions can operate on types of Vector Int, Vector Integer, Vector Float, whatever, as long as the a from Vector a is from the Num typeclass. Also, if you examine the type declaration for these functions, you’ll see that they can operate only on vectors of the same type and the numbers involved must also be of the type that is contained in the vectors. Notice that we didn’t put a Num class constraint in the data declaration, because we’d have to repeat it in the functions anyway.

        -

        Once again, it’s very important to distinguish between the type constructor and the value constructor. When declaring a data type, the part before the = is the type constructor and the constructors after it (possibly separated by |’s) are value constructors. Giving a function a type of Vector t t t -> Vector t t t -> t would be wrong, because we have to put types in type declaration and the vector type constructor takes only one parameter, whereas the value constructor takes three. Let’s play around with our vectors.

        -
        
        -ghci> Vector 3 5 8 `vplus` Vector 9 2 8
        +(Vector i j k) `scalarMult` (Vector l m n) = i*l + j*m + k*n
        +

        vplus is for adding two vectors together. Two vectors +are added just by adding their corresponding components. +scalarMult is for the scalar product of two vectors and +vectMult is for multiplying a vector with a scalar. These +functions can operate on types of Vector Int, +Vector Integer, Vector Float, whatever, as +long as the a from Vector a is from the +Num typeclass. Also, if you examine the type declaration +for these functions, you’ll see that they can operate only on vectors of +the same type and the numbers involved must also be of the type that is +contained in the vectors. Notice that we didn’t put a Num +class constraint in the data declaration, because we’d have to +repeat it in the functions anyway.

        +

        Once again, it’s very important to distinguish between the type +constructor and the value constructor. When declaring a data type, the +part before the = is the type constructor and the +constructors after it (possibly separated by |’s) are value +constructors. Giving a function a type of +Vector t t t -> Vector t t t -> t would be wrong, +because we have to put types in type declaration and the vector +type constructor takes only one parameter, whereas the +value constructor takes three. Let’s play around with our vectors.

        +
        ghci> Vector 3 5 8 `vplus` Vector 9 2 8
         Vector 12 7 16
         ghci> Vector 3 5 8 `vplus` Vector 9 2 8 `vplus` Vector 0 2 3
         Vector 12 9 19
        @@ -321,30 +500,68 @@ 

        Type parameters

        ghci> Vector 4 9 5 `scalarMult` Vector 9.0 2.0 4.0 74.0 ghci> Vector 2 9 3 `vectMult` (Vector 4 9 5 `scalarMult` Vector 9 2 4) -Vector 148 666 222 -
        +Vector 148 666 222

        Derived instances

        -gob -

        In the Typeclasses 101 section, we explained the basics of typeclasses. We explained that a typeclass is a sort of an interface that defines some behavior. A type can be made an instance of a typeclass if it supports that behavior. Example: the Int type is an instance of the Eq typeclass because the Eq typeclass defines behavior for stuff that can be equated. And because integers can be equated, Int is a part of the Eq typeclass. The real usefulness comes with the functions that act as the interface for Eq, namely == and /=. If a type is a part of the Eq typeclass, we can use the == functions with values of that type. That’s why expressions like 4 == 4 and "foo" /= "bar" typecheck.

        -

        We also mentioned that they’re often confused with classes in languages like Java, Python, C++ and the like, which then baffles a lot of people. In those languages, classes are a blueprint from which we then create objects that contain state and can do some actions. Typeclasses are more like interfaces. We don’t make data from typeclasses. Instead, we first make our data type and then we think about what it can act like. If it can act like something that can be equated, we make it an instance of the Eq typeclass. If it can act like something that can be ordered, we make it an instance of the Ord typeclass.

        -

        In the next section, we’ll take a look at how we can manually make our types instances of typeclasses by implementing the functions defined by the typeclasses. But right now, let’s see how Haskell can automatically make our type an instance of any of the following typeclasses: Eq, Ord, Enum, Bounded, Show, Read. Haskell can derive the behavior of our types in these contexts if we use the deriving keyword when making our data type.

        +gob +

        In the Typeclasses 101 +section, we explained the basics of typeclasses. We explained that a +typeclass is a sort of an interface that defines some behavior. A type +can be made an instance of a typeclass if it supports +that behavior. Example: the Int type is an instance of the +Eq typeclass because the Eq typeclass defines +behavior for stuff that can be equated. And because integers can be +equated, Int is a part of the Eq typeclass. +The real usefulness comes with the functions that act as the interface +for Eq, namely == and /=. If a +type is a part of the Eq typeclass, we can use the +== functions with values of that type. That’s why +expressions like 4 == 4 and "foo" /= "bar" +typecheck.

        +

        We also mentioned that they’re often confused with classes in +languages like Java, Python, C++ and the like, which then baffles a lot +of people. In those languages, classes are a blueprint from which we +then create objects that contain state and can do some actions. +Typeclasses are more like interfaces. We don’t make data from +typeclasses. Instead, we first make our data type and then we think +about what it can act like. If it can act like something that can be +equated, we make it an instance of the Eq typeclass. If it +can act like something that can be ordered, we make it an instance of +the Ord typeclass.

        +

        In the next section, we’ll take a look at how we can manually make +our types instances of typeclasses by implementing the functions defined +by the typeclasses. But right now, let’s see how Haskell can +automatically make our type an instance of any of the following +typeclasses: Eq, Ord, Enum, +Bounded, Show, Read. Haskell can +derive the behavior of our types in these contexts if we use the +deriving keyword when making our data type.

        Consider this data type:

        -
        
        -data Person = Person { firstName :: String
        +
        data Person = Person { firstName :: String
                              , lastName :: String
                              , age :: Int
        -                     }
        -
        -

        It describes a person. Let’s assume that no two people have the same combination of first name, last name and age. Now, if we have records for two people, does it make sense to see if they represent the same person? Sure it does. We can try to equate them and see if they’re equal or not. That’s why it would make sense for this type to be part of the Eq typeclass. We’ll derive the instance.

        -
        
        -data Person = Person { firstName :: String
        +                     }
        +

        It describes a person. Let’s assume that no two people have the same +combination of first name, last name and age. Now, if we have records +for two people, does it make sense to see if they represent the same +person? Sure it does. We can try to equate them and see if they’re equal +or not. That’s why it would make sense for this type to be part of the +Eq typeclass. We’ll derive the instance.

        +
        data Person = Person { firstName :: String
                              , lastName :: String
                              , age :: Int
        -                     } deriving (Eq)
        -
        -

        When we derive the Eq instance for a type and then try to compare two values of that type with == or /=, Haskell will see if the value constructors match (there’s only one value constructor here though) and then it will check if all the data contained inside matches by testing each pair of fields with ==. There’s only one catch though, the types of all the fields also have to be part of the Eq typeclass. But since both String and Int are, we’re OK. Let’s test our Eq instance.

        -
        
        -ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
        +                     } deriving (Eq)
        +

        When we derive the Eq instance for a type and then try +to compare two values of that type with == or +/=, Haskell will see if the value constructors match +(there’s only one value constructor here though) and then it will check +if all the data contained inside matches by testing each pair of fields +with ==. There’s only one catch though, the types of all +the fields also have to be part of the Eq typeclass. But +since both String and Int are, we’re OK. Let’s +test our Eq instance.

        +
        ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
         ghci> let adRock = Person {firstName = "Adam", lastName = "Horovitz", age = 41}
         ghci> let mca = Person {firstName = "Adam", lastName = "Yauch", age = 44}
         ghci> mca == adRock
        @@ -354,139 +571,162 @@ 

        Derived instances

        ghci> mikeD == mikeD True ghci> mikeD == Person {firstName = "Michael", lastName = "Diamond", age = 43} -True -
        -

        Of course, since Person is now in Eq, we can use it as the a for all functions that have a class constraint of Eq a in their type signature, such as elem.

        -
        
        -ghci> let beastieBoys = [mca, adRock, mikeD]
        +True
        +

        Of course, since Person is now in Eq, we +can use it as the a for all functions that have a class +constraint of Eq a in their type signature, such as +elem.

        +
        ghci> let beastieBoys = [mca, adRock, mikeD]
         ghci> mikeD `elem` beastieBoys
        -True
        -
        -

        The Show and Read typeclasses are for things that can be converted to or from strings, respectively. Like with Eq, if a type’s constructors have fields, their type has to be a part of Show or Read if we want to make our type an instance of them. Let’s make our Person data type a part of Show and Read as well.

        -
        
        -data Person = Person { firstName :: String
        +True
        +

        The Show and Read typeclasses are for +things that can be converted to or from strings, respectively. Like with +Eq, if a type’s constructors have fields, their type has to +be a part of Show or Read if we want to make +our type an instance of them. Let’s make our Person data +type a part of Show and Read as well.

        +
        data Person = Person { firstName :: String
                              , lastName :: String
                              , age :: Int
        -                     } deriving (Eq, Show, Read)
        -
        + } deriving (Eq, Show, Read)

        Now we can print a person out to the terminal.

        -
        
        -ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
        +
        ghci> let mikeD = Person {firstName = "Michael", lastName = "Diamond", age = 43}
         ghci> mikeD
         Person {firstName = "Michael", lastName = "Diamond", age = 43}
         ghci> "mikeD is: " ++ show mikeD
        -"mikeD is: Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}"
        -
        -

        Had we tried to print a person on the terminal before making the Person data type part of Show, Haskell would have complained at us, claiming it doesn’t know how to represent a person as a string. But now that we’ve derived a Show instance for it, it does know.

        -

        Read is pretty much the inverse typeclass of Show. Show is for converting values of our a type to a string, Read is for converting strings to values of our type. Remember though, when we use the read function, we have to use an explicit type annotation to tell Haskell which type we want to get as a result. If we don’t make the type we want as a result explicit, Haskell doesn’t know which type we want.

        -
        
        -ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" :: Person
        -Person {firstName = "Michael", lastName = "Diamond", age = 43}
        -
        -

        If we use the result of our read later on in a way that Haskell can infer that it should read it as a person, we don’t have to use type annotation.

        -
        
        -ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" == mikeD
        -True
        -
        -

        We can also read parameterized types, but we have to fill in the type parameters. So we can’t do read "Just 't'" :: Maybe a, but we can do read "Just 't'" :: Maybe Char.

        -

        We can derive instances for the Ord type class, -which is for types that have values that can be ordered. If we compare two -values of the same type that were made using different constructors, the value -which was made with a constructor that’s defined first is considered smaller. -For instance, consider the Bool -type, which can have a value of either False or -True. For the purpose of seeing how it behaves when -compared, we can think of it as being implemented like -this:

        -
        
        -data Bool = False | True deriving (Ord)
        -
        -

        Because the False value constructor is specified first and the True value constructor is specified after it, we can consider True as greater than False.

        -
        
        -ghci> True `compare` False
        +"mikeD is: Person {firstName = \"Michael\", lastName = \"Diamond\", age = 43}"
        +

        Had we tried to print a person on the terminal before making the +Person data type part of Show, Haskell would +have complained at us, claiming it doesn’t know how to represent a +person as a string. But now that we’ve derived a Show +instance for it, it does know.

        +

        Read is pretty much the inverse typeclass of +Show. Show is for converting values of our a +type to a string, Read is for converting strings to values +of our type. Remember though, when we use the read +function, we have to use an explicit type annotation to tell Haskell +which type we want to get as a result. If we don’t make the type we want +as a result explicit, Haskell doesn’t know which type we want.

        +
        ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" :: Person
        +Person {firstName = "Michael", lastName = "Diamond", age = 43}
        +

        If we use the result of our read later on in a way that +Haskell can infer that it should read it as a person, we don’t have to +use type annotation.

        +
        ghci> read "Person {firstName =\"Michael\", lastName =\"Diamond\", age = 43}" == mikeD
        +True
        +

        We can also read parameterized types, but we have to fill in the type +parameters. So we can’t do read "Just 't'" :: Maybe a, but +we can do read "Just 't'" :: Maybe Char.

        +

        We can derive instances for the Ord type class, which is +for types that have values that can be ordered. If we compare two values +of the same type that were made using different constructors, the value +which was made with a constructor that’s defined first is considered +smaller. For instance, consider the Bool type, which can +have a value of either False or True. For the +purpose of seeing how it behaves when compared, we can think of it as +being implemented like this:

        +
        data Bool = False | True deriving (Ord)
        +

        Because the False value constructor is specified first +and the True value constructor is specified after it, we +can consider True as greater than False.

        +
        ghci> True `compare` False
         GT
         ghci> True > False
         True
         ghci> True < False
        -False
        -
        -

        In the Maybe a data type, the Nothing value constructor is specified before the Just value constructor, so a value of Nothing is always smaller than a value of Just something, even if that something is minus one billion trillion. But if we compare two Just values, then it goes to compare what’s inside them.

        -
        
        -ghci> Nothing < Just 100
        +False
        +

        In the Maybe a data type, the Nothing value +constructor is specified before the Just value constructor, +so a value of Nothing is always smaller than a value of +Just something, even if that something is minus one billion +trillion. But if we compare two Just values, then it goes +to compare what’s inside them.

        +
        ghci> Nothing < Just 100
         True
         ghci> Nothing > Just (-49999)
         False
         ghci> Just 3 `compare` Just 2
         GT
         ghci> Just 100 > Just 50
        -True
        -
        -

        But we can’t do something like Just (*3) > Just (*2), because (*3) and (*2) are functions, which aren’t instances of Ord.

        -

        We can easily use algebraic data types to make enumerations and the Enum and Bounded typeclasses help us with that. Consider the following data type:

        -
        
        -data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
        -
        -

        Because all the value constructors are nullary (take no parameters, i.e. fields), we can make it part of the Enum typeclass. The Enum typeclass is for things that have predecessors and successors. We can also make it part of the Bounded typeclass, which is for things that have a lowest possible value and highest possible value. And while we’re at it, let’s also make it an instance of all the other derivable typeclasses and see what we can do with it.

        -
        
        -data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
        -           deriving (Eq, Ord, Show, Read, Bounded, Enum)
        -
        -

        Because it’s part of the Show and Read typeclasses, we can convert values of this type to and from strings.

        -
        
        -ghci> Wednesday
        +True
        +

        But we can’t do something like Just (*3) > Just (*2), +because (*3) and (*2) are functions, which +aren’t instances of Ord.

        +

        We can easily use algebraic data types to make enumerations and the +Enum and Bounded typeclasses help us with +that. Consider the following data type:

        +
        data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
        +

        Because all the value constructors are nullary (take no parameters, +i.e. fields), we can make it part of the Enum typeclass. +The Enum typeclass is for things that have predecessors and +successors. We can also make it part of the Bounded +typeclass, which is for things that have a lowest possible value and +highest possible value. And while we’re at it, let’s also make it an +instance of all the other derivable typeclasses and see what we can do +with it.

        +
        data Day = Monday | Tuesday | Wednesday | Thursday | Friday | Saturday | Sunday
        +           deriving (Eq, Ord, Show, Read, Bounded, Enum)
        +

        Because it’s part of the Show and Read +typeclasses, we can convert values of this type to and from strings.

        +
        ghci> Wednesday
         Wednesday
         ghci> show Wednesday
         "Wednesday"
         ghci> read "Saturday" :: Day
        -Saturday
        -
        -

        Because it’s part of the Eq and Ord typeclasses, we can compare or equate days.

        -
        
        -ghci> Saturday == Sunday
        +Saturday
        +

        Because it’s part of the Eq and Ord +typeclasses, we can compare or equate days.

        +
        ghci> Saturday == Sunday
         False
         ghci> Saturday == Saturday
         True
         ghci> Saturday > Friday
         True
         ghci> Monday `compare` Wednesday
        -LT
        -
        -

        It’s also part of Bounded, so we can get the lowest and highest day.

        -
        
        -ghci> minBound :: Day
        +LT
        +

        It’s also part of Bounded, so we can get the lowest and +highest day.

        +
        ghci> minBound :: Day
         Monday
         ghci> maxBound :: Day
        -Sunday
        -
        -

        It’s also an instance of Enum. We can get predecessors and successors of days and we can make list ranges from them!

        -
        
        -ghci> succ Monday
        +Sunday
        +

        It’s also an instance of Enum. We can get predecessors +and successors of days and we can make list ranges from them!

        +
        ghci> succ Monday
         Tuesday
         ghci> pred Saturday
         Friday
         ghci> [Thursday .. Sunday]
         [Thursday,Friday,Saturday,Sunday]
         ghci> [minBound .. maxBound] :: [Day]
        -[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]
        -
        +[Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday]

        That’s pretty awesome.

        Type synonyms

        -

        - Previously, we mentioned that when writing types, the [Char] and String types are equivalent and interchangeable. That’s implemented with type synonyms. Type synonyms don’t really do anything per se, they’re just about giving some types different names so that they make more sense to someone reading our code and documentation. Here’s how the standard library defines String as a synonym for [Char]. -

        -
        
        - type String = [Char]
        - 
        - chicken -

        - We’ve introduced the type keyword. The keyword might be misleading to some, because we’re not actually making anything new (we did that with the data keyword), but we’re just making a synonym for an already existing type. -

        -

        If we make a function that converts a string to uppercase and call it toUpperString or something, we can give it a type declaration of toUpperString :: [Char] -> [Char] or toUpperString :: String -> String. Both of these are essentially the same, only the latter is nicer to read.

        -

        - When we were dealing with the Data.Map module, we first represented a phonebook with an association list before converting it into a map. As we’ve already found out, an association list is a list of key-value pairs. Let’s look at a phonebook that we had. -

        -
        
        -phoneBook :: [(String,String)]
        +

        Previously, we mentioned that when writing types, the +[Char] and String types are equivalent and +interchangeable. That’s implemented with type synonyms. +Type synonyms don’t really do anything per se, they’re just about giving +some types different names so that they make more sense to someone +reading our code and documentation. Here’s how the standard library +defines String as a synonym for [Char].

        +
        type String = [Char]
        +chicken +

        We’ve introduced the type keyword. The keyword might be +misleading to some, because we’re not actually making anything new (we +did that with the data keyword), but we’re just making a +synonym for an already existing type.

        +

        If we make a function that converts a string to uppercase and call it +toUpperString or something, we can give it a type +declaration of toUpperString :: [Char] -> [Char] or +toUpperString :: String -> String. Both of these are +essentially the same, only the latter is nicer to read.

        +

        When we were dealing with the Data.Map module, we first +represented a phonebook with an association list before converting it +into a map. As we’ve already found out, an association list is a list of +key-value pairs. Let’s look at a phonebook that we had.

        +
        phoneBook :: [(String,String)]
         phoneBook =
             [("amelia","555-2938")
             ,("freya","452-2928")
        @@ -495,78 +735,168 @@ 

        Type synonyms

        ,("roald","939-8282") ,("tenzing","853-2492") ]
        -

        We see that the type of phoneBook is [(String,String)]. That tells us that it’s an association list that maps from strings to strings, but not much else. Let’s make a type synonym to convey some more information in the type declaration.

        -
        
        -type PhoneBook = [(String,String)]
        -
        -

        Now the type declaration for our phonebook can be phoneBook :: PhoneBook. Let’s make a type synonym for String as well.

        -
        
        -type PhoneNumber = String
        +

        We see that the type of phoneBook is +[(String,String)]. That tells us that it’s an association +list that maps from strings to strings, but not much else. Let’s make a +type synonym to convey some more information in the type +declaration.

        +
        type PhoneBook = [(String,String)]
        +

        Now the type declaration for our phonebook can be +phoneBook :: PhoneBook. Let’s make a type synonym for +String as well.

        +
        type PhoneNumber = String
         type Name = String
        -type PhoneBook = [(Name,PhoneNumber)]
        -
        -

        Giving the String type synonyms is something that Haskell programmers do when they want to convey more information about what strings in their functions should be used as and what they represent.

        -

        So now, when we implement a function that takes a name and a number and sees if that name and number combination is in our phonebook, we can give it a very pretty and descriptive type declaration.

        -
        
        -inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool
        -inPhoneBook name pnumber pbook = (name,pnumber) `elem` pbook
        -
        -

        If we decided not to use type synonyms, our function would have a type of String -> String -> [(String,String)] -> Bool. In this case, the type declaration that took advantage of type synonyms is easier to understand. However, you shouldn’t go overboard with them. We introduce type synonyms either to describe what some existing type represents in our functions (and thus our type declarations become better documentation) or when something has a long-ish type that’s repeated a lot (like [(String,String)]) but represents something more specific in the context of our functions.

        -

        Type synonyms can also be parameterized. If we want a type that represents an association list type but still want it to be general so it can use any type as the keys and values, we can do this:

        -
        
        -type AssocList k v = [(k,v)]
        -
        -

        Now, a function that gets the value by a key in an association list can have a type of (Eq k) => k -> AssocList k v -> Maybe v. AssocList is a type constructor that takes two types and produces a concrete type, like AssocList Int String, for instance.

        -

        Fonzie says: Aaay! When I talk about concrete types I mean like fully applied types like Map Int String or if we’re dealin’ with one of them polymorphic functions, [a] or (Ord a) => Maybe a and stuff. And like, sometimes me and my buddies say that Maybe is a type, but we don’t mean that, cause every idiot knows Maybe is a type constructor. When I apply an extra type to Maybe, like Maybe String, then I have a concrete type. You know, values can only have types that are concrete types! So in conclusion, live fast, love hard and don’t let anybody else use your comb!

        -

        Just like we can partially apply functions to get new functions, we can partially apply type parameters and get new type constructors from them. Just like we call a function with too few parameters to get back a new function, we can specify a type constructor with too few type parameters and get back a partially applied type constructor. If we wanted a type that represents a map (from Data.Map) from integers to something, we could either do this:

        -
        
        -type IntMap v = Map Int v
        -
        +type PhoneBook = [(Name,PhoneNumber)]
        +

        Giving the String type synonyms is something that +Haskell programmers do when they want to convey more information about +what strings in their functions should be used as and what they +represent.

        +

        So now, when we implement a function that takes a name and a number +and sees if that name and number combination is in our phonebook, we can +give it a very pretty and descriptive type declaration.

        +
        inPhoneBook :: Name -> PhoneNumber -> PhoneBook -> Bool
        +inPhoneBook name pnumber pbook = (name,pnumber) `elem` pbook
        +

        If we decided not to use type synonyms, our function would have a +type of +String -> String -> [(String,String)] -> Bool. In +this case, the type declaration that took advantage of type synonyms is +easier to understand. However, you shouldn’t go overboard with them. We +introduce type synonyms either to describe what some existing type +represents in our functions (and thus our type declarations become +better documentation) or when something has a long-ish type that’s +repeated a lot (like [(String,String)]) but represents +something more specific in the context of our functions.

        +

        Type synonyms can also be parameterized. If we want a type that +represents an association list type but still want it to be general so +it can use any type as the keys and values, we can do this:

        +
        type AssocList k v = [(k,v)]
        +

        Now, a function that gets the value by a key in an association list +can have a type of +(Eq k) => k -> AssocList k v -> Maybe v. +AssocList is a type constructor that takes two types and +produces a concrete type, like AssocList Int String, for +instance.

        +
        +

        Fonzie says: Aaay! When I talk about concrete +types I mean like fully applied types like +Map Int String or if we’re dealin’ with one of them +polymorphic functions, [a] or +(Ord a) => Maybe a and stuff. And like, sometimes me and +my buddies say that Maybe is a type, but we don’t mean +that, cause every idiot knows Maybe is a type constructor. +When I apply an extra type to Maybe, like +Maybe String, then I have a concrete type. You know, values +can only have types that are concrete types! So in conclusion, live +fast, love hard and don’t let anybody else use your comb!

        +
        +

        Just like we can partially apply functions to get new functions, we +can partially apply type parameters and get new type constructors from +them. Just like we call a function with too few parameters to get back a +new function, we can specify a type constructor with too few type +parameters and get back a partially applied type constructor. If we +wanted a type that represents a map (from Data.Map) from +integers to something, we could either do this:

        +
        type IntMap v = Map Int v

        Or we could do it like this:

        -
        
        -type IntMap = Map Int
        -
        -

        Either way, the IntMap type constructor takes one parameter and that is the type of what the integers will point to.

        -

        Oh yeah. If you’re going to try and implement this, you’ll probably going to do a qualified import of Data.Map. When you do a qualified import, type constructors also have to be preceded with a module name. So you’d write type IntMap = Map.Map Int.

        -

        Make sure that you really understand the distinction between type constructors and value constructors. Just because we made a type synonym called IntMap or AssocList doesn’t mean that we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it means is that we can refer to its type by using different names. We can do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will make the numbers inside assume a type of Int, but we can still use that list as we would any normal list that has pairs of integers inside. Type synonyms (and types generally) can only be used in the type portion of Haskell. We’re in Haskell’s type portion whenever we’re defining new types (so in data and type declarations) or when we’re located after a ::. The :: is in type declarations or in type annotations.

        -

        Another cool data type that takes two types as its parameters is the Either a b type. This is roughly how it’s defined:

        -
        
        -data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
        -
        -

        It has two value constructors. If the Left is used, then its contents are of type a and if Right is used, then its contents are of type b. So we can use this type to encapsulate a value of one type or another and then when we get a value of type Either a b, we usually pattern match on both Left and Right and we different stuff based on which one of them it was.

        -
        
        -ghci> Right 20
        +
        type IntMap = Map Int
        +

        Either way, the IntMap type constructor takes one +parameter and that is the type of what the integers will point to.

        +
        +

        Oh yeah. If you’re going to try and implement this, +you’ll probably going to do a qualified import of Data.Map. +When you do a qualified import, type constructors also have to be +preceded with a module name. So you’d write +type IntMap = Map.Map Int.

        +
        +

        Make sure that you really understand the distinction between type +constructors and value constructors. Just because we made a type synonym +called IntMap or AssocList doesn’t mean that +we can do stuff like AssocList [(1,2),(4,5),(7,9)]. All it +means is that we can refer to its type by using different names. We can +do [(1,2),(3,5),(8,9)] :: AssocList Int Int, which will +make the numbers inside assume a type of Int, but we can +still use that list as we would any normal list that has pairs of +integers inside. Type synonyms (and types generally) can only be used in +the type portion of Haskell. We’re in Haskell’s type portion whenever +we’re defining new types (so in data and type +declarations) or when we’re located after a ::. The +:: is in type declarations or in type annotations.

        +

        Another cool data type that takes two types as its parameters is the +Either a b type. This is roughly how it’s defined:

        +
        data Either a b = Left a | Right b deriving (Eq, Ord, Read, Show)
        +

        It has two value constructors. If the Left is used, then +its contents are of type a and if Right is +used, then its contents are of type b. So we can use this +type to encapsulate a value of one type or another and then when we get +a value of type Either a b, we usually pattern match on +both Left and Right and we different stuff +based on which one of them it was.

        +
        ghci> Right 20
         Right 20
         ghci> Left "w00t"
         Left "w00t"
         ghci> :t Right 'a'
         Right 'a' :: Either a Char
         ghci> :t Left True
        -Left True :: Either Bool b
        -
        -

        So far, we’ve seen that Maybe a was mostly used to represent the results of computations that could have either failed or not. But sometimes, Maybe a isn’t good enough because Nothing doesn’t really convey much information other than that something has failed. That’s cool for functions that can fail in only one way or if we’re just not interested in how and why they failed. A Data.Map lookup fails only if the key we were looking for wasn’t in the map, so we know exactly what happened. However, when we’re interested in how some function failed or why, we usually use the result type of Either a b, where a is some sort of type that can tell us something about the possible failure and b is the type of a successful computation. Hence, errors use the Left value constructor while results use Right.

        -

        An example: a high-school has lockers so that students have some place to put their Guns’n’Roses posters. Each locker has a code combination. When a student wants a new locker, they tell the locker supervisor which locker number they want and he gives them the code. However, if someone is already using that locker, he can’t tell them the code for the locker and they have to pick a different one. We’ll use a map from Data.Map to represent the lockers. It’ll map from locker numbers to a pair of whether the locker is in use or not and the locker code.

        -
        
        -import qualified Data.Map as Map
        +Left True :: Either Bool b
        +

        So far, we’ve seen that Maybe a was mostly used to +represent the results of computations that could have either failed or +not. But sometimes, Maybe a isn’t good enough because +Nothing doesn’t really convey much information other than +that something has failed. That’s cool for functions that can fail in +only one way or if we’re just not interested in how and why they failed. +A Data.Map lookup fails only if the key we were looking for +wasn’t in the map, so we know exactly what happened. However, when we’re +interested in how some function failed or why, we usually use the result +type of Either a b, where a is some sort of +type that can tell us something about the possible failure and +b is the type of a successful computation. Hence, errors +use the Left value constructor while results use +Right.

        +

        An example: a high-school has lockers so that students have some +place to put their Guns’n’Roses posters. Each locker has a code +combination. When a student wants a new locker, they tell the locker +supervisor which locker number they want and he gives them the code. +However, if someone is already using that locker, he can’t tell them the +code for the locker and they have to pick a different one. We’ll use a +map from Data.Map to represent the lockers. It’ll map from +locker numbers to a pair of whether the locker is in use or not and the +locker code.

        +
        import qualified Data.Map as Map
         
         data LockerState = Taken | Free deriving (Show, Eq)
         
         type Code = String
         
        -type LockerMap = Map.Map Int (LockerState, Code)
        -
        -

        Simple stuff. We introduce a new data type to represent whether a locker is taken or free and we make a type synonym for the locker code. We also make a type synonym for the type that maps from integers to pairs of locker state and code. And now, we’re going to make a function that searches for the code in a locker map. We’re going to use an Either String Code type to represent our result, because our lookup can fail in two ways — the locker can be taken, in which case we can’t tell the code or the locker number might not exist at all. If the lookup fails, we’re just going to use a String to tell what’s happened.

        -
        
        -lockerLookup :: Int -> LockerMap -> Either String Code
        +type LockerMap = Map.Map Int (LockerState, Code)
        +

        Simple stuff. We introduce a new data type to represent whether a +locker is taken or free and we make a type synonym for the locker code. +We also make a type synonym for the type that maps from integers to +pairs of locker state and code. And now, we’re going to make a function +that searches for the code in a locker map. We’re going to use an +Either String Code type to represent our result, because +our lookup can fail in two ways — the locker can be taken, in which case +we can’t tell the code or the locker number might not exist at all. If +the lookup fails, we’re just going to use a String to tell +what’s happened.

        +
        lockerLookup :: Int -> LockerMap -> Either String Code
         lockerLookup lockerNumber map =
             case Map.lookup lockerNumber map of
                 Nothing -> Left $ "Locker number " ++ show lockerNumber ++ " doesn't exist!"
                 Just (state, code) -> if state /= Taken
                                         then Right code
                                         else Left $ "Locker " ++ show lockerNumber ++ " is already taken!"
        -

        We do a normal lookup in the map. If we get a Nothing, we return a value of type Left String, saying that the locker doesn’t exist at all. If we do find it, then we do an additional check to see if the locker is taken. If it is, return a Left saying that it’s already taken. If it isn’t, then return a value of type Right Code, in which we give the student the correct code for the locker. It’s actually a Right String, but we introduced that type synonym to introduce some additional documentation into the type declaration. Here’s an example map:

        -
        
        -lockers :: LockerMap
        +

        We do a normal lookup in the map. If we get a Nothing, +we return a value of type Left String, saying that the +locker doesn’t exist at all. If we do find it, then we do an additional +check to see if the locker is taken. If it is, return a +Left saying that it’s already taken. If it isn’t, then +return a value of type Right Code, in which we give the +student the correct code for the locker. It’s actually a +Right String, but we introduced that type synonym to +introduce some additional documentation into the type declaration. +Here’s an example map:

        +
        lockers :: LockerMap
         lockers = Map.fromList
             [(100,(Taken,"ZD39I"))
             ,(101,(Free,"JAH3I"))
        @@ -574,11 +904,9 @@ 

        Type synonyms

        ,(105,(Free,"QOTSA")) ,(109,(Taken,"893JJ")) ,(110,(Taken,"99292")) - ] -
        + ]

        Now let’s try looking up some locker codes.

        -
        
        -ghci> lockerLookup 101 lockers
        +
        ghci> lockerLookup 101 lockers
         Right "JAH3I"
         ghci> lockerLookup 100 lockers
         Left "Locker 100 is already taken!"
        @@ -587,84 +915,160 @@ 

        Type synonyms

        ghci> lockerLookup 110 lockers Left "Locker 110 is already taken!" ghci> lockerLookup 105 lockers -Right "QOTSA" -
        -

        We could have used a Maybe a to represent the result but then we wouldn’t know why we couldn’t get the code. But now, we have information about the failure in our result type.

        +Right "QOTSA"
        +

        We could have used a Maybe a to represent the result but +then we wouldn’t know why we couldn’t get the code. But now, we have +information about the failure in our result type.

        Recursive data structures

        -the fonz -

        As we’ve seen, a constructor in an algebraic data type can have several (or none at all) fields and each field must be of some concrete type. With that in mind, we can make types whose constructors have fields that are of the same type! Using that, we can create recursive data types, where one value of some type contains values of that type, which in turn contain more values of the same type and so on.

        -

        Think about this list: [5]. That’s just syntactic sugar for 5:[]. On the left side of the :, there’s a value and on the right side, there’s a list. And in this case, it’s an empty list. Now how about the list [4,5]? Well, that desugars to 4:(5:[]). Looking at the first :, we see that it also has an element on its left side and a list (5:[]) on its right side. Same goes for a list like 3:(4:(5:6:[])), which could be written either like that or like 3:4:5:6:[] (because : is right-associative) or [3,4,5,6].

        -

        We could say that a list can be an empty list or it can be an element joined together with a : with another list (that can be either the empty list or not).

        +the fonz +

        As we’ve seen, a constructor in an algebraic data type can have +several (or none at all) fields and each field must be of some concrete +type. With that in mind, we can make types whose constructors have +fields that are of the same type! Using that, we can create recursive +data types, where one value of some type contains values of that type, +which in turn contain more values of the same type and so on.

        +

        Think about this list: [5]. That’s just syntactic sugar +for 5:[]. On the left side of the :, there’s a +value and on the right side, there’s a list. And in this case, it’s an +empty list. Now how about the list [4,5]? Well, that +desugars to 4:(5:[]). Looking at the first :, +we see that it also has an element on its left side and a list +(5:[]) on its right side. Same goes for a list like +3:(4:(5:6:[])), which could be written either like that or +like 3:4:5:6:[] (because : is +right-associative) or [3,4,5,6].

        +

        We could say that a list can be an empty list or it can be an element +joined together with a : with another list (that can be +either the empty list or not).

        Let’s use algebraic data types to implement our own list then!

        -
        
        -data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
        -
        -

        This reads just like our definition of lists from one of the previous paragraphs. It’s either an empty list or a combination of a head with some value and a list. If you’re confused about this, you might find it easier to understand in record syntax.

        -
        
        -data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)
        -
        -

        You might also be confused about the Cons constructor here. cons is another word for :. You see, in lists, : is actually a constructor that takes a value and another list and returns a list. We can already use our new list type! In other words, it has two fields. One field is of the type of a and the other is of the type [a].

        -
        
        -ghci> Empty
        +
        data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
        +

        This reads just like our definition of lists from one of the previous +paragraphs. It’s either an empty list or a combination of a head with +some value and a list. If you’re confused about this, you might find it +easier to understand in record syntax.

        +
        data List a = Empty | Cons { listHead :: a, listTail :: List a} deriving (Show, Read, Eq, Ord)
        +

        You might also be confused about the Cons constructor +here. cons is another word for :. You see, in +lists, : is actually a constructor that takes a value and +another list and returns a list. We can already use our new list type! +In other words, it has two fields. One field is of the type of +a and the other is of the type [a].

        +
        ghci> Empty
         Empty
         ghci> 5 `Cons` Empty
         Cons 5 Empty
         ghci> 4 `Cons` (5 `Cons` Empty)
         Cons 4 (Cons 5 Empty)
         ghci> 3 `Cons` (4 `Cons` (5 `Cons` Empty))
        -Cons 3 (Cons 4 (Cons 5 Empty))
        -
        -

        We called our Cons constructor in an infix manner so you can see how it’s just like :. Empty is like [] and 4 `Cons` (5 `Cons` Empty) is like 4:(5:[]).

        -

        We can define functions to be automatically infix by making them comprised of only special characters. We can also do the same with constructors, since they’re just functions that return a data type. So check this out.

        -
        
        -infixr 5 :-:
        -data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)
        -
        -

        First off, we notice a new syntactic construct, the fixity declarations. When we define functions as operators, we can use that to give them a fixity (but we don’t have to). A fixity states how tightly the operator binds and whether it’s left-associative or right-associative. For instance, *’s fixity is infixl 7 * and +’s fixity is infixl 6. That means that they’re both left-associative (4 * 3 * 2 is (4 * 3) * 2) but * binds tighter than +, because it has a greater fixity, so 5 * 4 + 3 is (5 * 4) + 3.

        -

        Otherwise, we just wrote a :-: (List a) instead of Cons a (List a). Now, we can write out lists in our list type like so:

        -
        
        -ghci> 3 :-: 4 :-: 5 :-: Empty
        +Cons 3 (Cons 4 (Cons 5 Empty))
        +

        We called our Cons constructor in an infix manner so you +can see how it’s just like :. Empty is like +[] and 4 `Cons` (5 `Cons` Empty) is like +4:(5:[]).

        +

        We can define functions to be automatically infix by making them +comprised of only special characters. We can also do the same with +constructors, since they’re just functions that return a data type. So +check this out.

        +
        infixr 5 :-:
        +data List a = Empty | a :-: (List a) deriving (Show, Read, Eq, Ord)
        +

        First off, we notice a new syntactic construct, the fixity +declarations. When we define functions as operators, we can use that to +give them a fixity (but we don’t have to). A fixity states how tightly +the operator binds and whether it’s left-associative or +right-associative. For instance, *’s fixity is +infixl 7 * and +’s fixity is +infixl 6. That means that they’re both left-associative +(4 * 3 * 2 is (4 * 3) * 2) but * +binds tighter than +, because it has a greater fixity, so +5 * 4 + 3 is (5 * 4) + 3.

        +

        Otherwise, we just wrote a :-: (List a) instead of +Cons a (List a). Now, we can write out lists in our list +type like so:

        +
        ghci> 3 :-: 4 :-: 5 :-: Empty
         (:-:) 3 ((:-:) 4 ((:-:) 5 Empty))
         ghci> let a = 3 :-: 4 :-: 5 :-: Empty
         ghci> 100 :-: a
        -(:-:) 100 ((:-:) 3 ((:-:) 4 ((:-:) 5 Empty)))
        -
        -

        When deriving Show for our type, Haskell will still display it as if the constructor was a prefix function, hence the parentheses around the operator (remember, 4 + 3 is (+) 4 3).

        -

        Let’s make a function that adds two of our lists together. This is how ++ is defined for normal lists:

        -
        
        -infixr 5  ++
        +(:-:) 100 ((:-:) 3 ((:-:) 4 ((:-:) 5 Empty)))
        +

        When deriving Show for our type, Haskell will still +display it as if the constructor was a prefix function, hence the +parentheses around the operator (remember, 4 + 3 is +(+) 4 3).

        +

        Let’s make a function that adds two of our lists together. This is +how ++ is defined for normal lists:

        +
        infixr 5  ++
         (++) :: [a] -> [a] -> [a]
         []     ++ ys = ys
        -(x:xs) ++ ys = x : (xs ++ ys)
        -
        -

        So we’ll just steal that for our own list. We’ll name the function .++.

        -
        
        -infixr 5  .++
        +(x:xs) ++ ys = x : (xs ++ ys)
        +

        So we’ll just steal that for our own list. We’ll name the function +.++.

        +
        infixr 5  .++
         (.++) :: List a -> List a -> List a
         Empty .++ ys = ys
        -(x :-: xs) .++ ys = x :-: (xs .++ ys)
        -
        +(x :-: xs) .++ ys = x :-: (xs .++ ys)

        And let’s see if it works …

        -
        
        -ghci> let a = 3 :-: 4 :-: 5 :-: Empty
        +
        ghci> let a = 3 :-: 4 :-: 5 :-: Empty
         ghci> let b = 6 :-: 7 :-: Empty
         ghci> a .++ b
        -(:-:) 3 ((:-:) 4 ((:-:) 5 ((:-:) 6 ((:-:) 7 Empty))))
        -
        -

        Nice. Is nice. If we wanted, we could implement all of the functions that operate on lists on our own list type.

        -

        Notice how we pattern matched on (x :-: xs). That works because pattern matching is actually about matching constructors. We can match on :-: because it is a constructor for our own list type and we can also match on : because it is a constructor for the built-in list type. Same goes for []. Because pattern matching works (only) on constructors, we can match for stuff like that, normal prefix constructors or stuff like 8 or 'a', which are basically constructors for the numeric and character types, respectively.

        -binary search tree -

        Now, we’re going to implement a binary search tree. If you’re not familiar with binary search trees from languages like C, here’s what they are: an element points to two elements, one on its left and one on its right. The element to the left is smaller, the element to the right is bigger. Each of those elements can also point to two elements (or one, or none). In effect, each element has up to two subtrees. And a cool thing about binary search trees is that we know that all the elements at the left subtree of, say, 5 are going to be smaller than 5. Elements in its right subtree are going to be bigger. So if we need to find if 8 is in our tree, we’d start at 5 and then because 8 is greater than 5, we’d go right. We’re now at 7 and because 8 is greater than 7, we go right again. And we’ve found our element in three hops! Now if this were a normal list (or a tree, but really unbalanced), it would take us seven hops instead of three to see if 8 is in there.

        -

        Sets and maps from Data.Set and Data.Map are implemented using trees, only instead of normal binary search trees, they use balanced binary search trees, which are always balanced. But right now, we’ll just be implementing normal binary search trees.

        -

        Here’s what we’re going to say: a tree is either an empty tree or it’s an element that contains some value and two trees. Sounds like a perfect fit for an algebraic data type!

        -
        
        -data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
        -
        -

        Okay, good, this is good. Instead of manually building a tree, we’re going to make a function that takes a tree and an element and inserts an element. We do this by comparing the value we want to insert to the root node and then if it’s smaller, we go left, if it’s larger, we go right. We do the same for every subsequent node until we reach an empty tree. Once we’ve reached an empty tree, we just insert a node with that value instead of the empty tree.

        -

        In languages like C, we’d do this by modifying the pointers and values inside the tree. In Haskell, we can’t really modify our tree, so we have to make a new subtree each time we decide to go left or right and in the end the insertion function returns a completely new tree, because Haskell doesn’t really have a concept of pointer, just values. Hence, the type for our insertion function is going to be something like a -> Tree a -> Tree a. It takes an element and a tree and returns a new tree that has that element inside. This might seem like it’s inefficient but laziness takes care of that problem.

        -

        So, here are two functions. One is a utility function for making a singleton tree (a tree with just one node) and a function to insert an element into a tree.

        -
        
        -singleton :: a -> Tree a
        +(:-:) 3 ((:-:) 4 ((:-:) 5 ((:-:) 6 ((:-:) 7 Empty))))
        +

        Nice. Is nice. If we wanted, we could implement all of the functions +that operate on lists on our own list type.

        +

        Notice how we pattern matched on (x :-: xs). That works +because pattern matching is actually about matching constructors. We can +match on :-: because it is a constructor for our own list +type and we can also match on : because it is a constructor +for the built-in list type. Same goes for []. Because +pattern matching works (only) on constructors, we can match for stuff +like that, normal prefix constructors or stuff like 8 or +'a', which are basically constructors for the numeric and +character types, respectively.

        +binary search tree +

        Now, we’re going to implement a binary search tree. +If you’re not familiar with binary search trees from languages like C, +here’s what they are: an element points to two elements, one on its left +and one on its right. The element to the left is smaller, the element to +the right is bigger. Each of those elements can also point to two +elements (or one, or none). In effect, each element has up to two +subtrees. And a cool thing about binary search trees is that we know +that all the elements at the left subtree of, say, 5 are going to be +smaller than 5. Elements in its right subtree are going to be bigger. So +if we need to find if 8 is in our tree, we’d start at 5 and then because +8 is greater than 5, we’d go right. We’re now at 7 and because 8 is +greater than 7, we go right again. And we’ve found our element in three +hops! Now if this were a normal list (or a tree, but really unbalanced), +it would take us seven hops instead of three to see if 8 is in +there.

        +

        Sets and maps from Data.Set and Data.Map +are implemented using trees, only instead of normal binary search trees, +they use balanced binary search trees, which are always balanced. But +right now, we’ll just be implementing normal binary search trees.

        +

        Here’s what we’re going to say: a tree is either an empty tree or +it’s an element that contains some value and two trees. Sounds like a +perfect fit for an algebraic data type!

        +
        data Tree a = EmptyTree | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
        +

        Okay, good, this is good. Instead of manually building a tree, we’re +going to make a function that takes a tree and an element and inserts an +element. We do this by comparing the value we want to insert to the root +node and then if it’s smaller, we go left, if it’s larger, we go right. +We do the same for every subsequent node until we reach an empty tree. +Once we’ve reached an empty tree, we just insert a node with that value +instead of the empty tree.

        +

        In languages like C, we’d do this by modifying the pointers and +values inside the tree. In Haskell, we can’t really modify our tree, so +we have to make a new subtree each time we decide to go left or right +and in the end the insertion function returns a completely new tree, +because Haskell doesn’t really have a concept of pointer, just values. +Hence, the type for our insertion function is going to be something like +a -> Tree a -> Tree a. It takes an element and a tree +and returns a new tree that has that element inside. This might seem +like it’s inefficient but laziness takes care of that problem.

        +

        So, here are two functions. One is a utility function for making a +singleton tree (a tree with just one node) and a function to insert an +element into a tree.

        +
        singleton :: a -> Tree a
         singleton x = Node x EmptyTree EmptyTree
         
         treeInsert :: (Ord a) => a -> Tree a -> Tree a
        @@ -672,181 +1076,378 @@ 

        Recursive data structures

        treeInsert x (Node a left right) | x == a = Node x left right | x < a = Node a (treeInsert x left) right - | x > a = Node a left (treeInsert x right) -
        -

        The singleton function is just a shortcut for making a node that has something and then two empty subtrees. In the insertion function, we first have the edge condition as a pattern. If we’ve reached an empty subtree, that means we’re where we want and instead of the empty tree, we put a singleton tree with our element. If we’re not inserting into an empty tree, then we have to check some things. First off, if the element we’re inserting is equal to the root element, just return a tree that’s the same. If it’s smaller, return a tree that has the same root value, the same right subtree but instead of its left subtree, put a tree that has our value inserted into it. Same (but the other way around) goes if our value is bigger than the root element.

        -

        Next up, we’re going to make a function that checks if some element is in the tree. First, let’s define the edge condition. If we’re looking for an element in an empty tree, then it’s certainly not there. Okay. Notice how this is the same as the edge condition when searching for elements in lists. If we’re looking for an element in an empty list, it’s not there. Anyway, if we’re not looking for an element in an empty tree, then we check some things. If the element in the root node is what we’re looking for, great! If it’s not, what then? Well, we can take advantage of knowing that all the left elements are smaller than the root node. So if the element we’re looking for is smaller than the root node, check to see if it’s in the left subtree. If it’s bigger, check to see if it’s in the right subtree.

        -
        
        -treeElem :: (Ord a) => a -> Tree a -> Bool
        +    | x > a  = Node a left (treeInsert x right)
        +

        The singleton function is just a shortcut for making a +node that has something and then two empty subtrees. In the insertion +function, we first have the edge condition as a pattern. If we’ve +reached an empty subtree, that means we’re where we want and instead of +the empty tree, we put a singleton tree with our element. If we’re not +inserting into an empty tree, then we have to check some things. First +off, if the element we’re inserting is equal to the root element, just +return a tree that’s the same. If it’s smaller, return a tree that has +the same root value, the same right subtree but instead of its left +subtree, put a tree that has our value inserted into it. Same (but the +other way around) goes if our value is bigger than the root element.

        +

        Next up, we’re going to make a function that checks if some element +is in the tree. First, let’s define the edge condition. If we’re looking +for an element in an empty tree, then it’s certainly not there. Okay. +Notice how this is the same as the edge condition when searching for +elements in lists. If we’re looking for an element in an empty list, +it’s not there. Anyway, if we’re not looking for an element in an empty +tree, then we check some things. If the element in the root node is what +we’re looking for, great! If it’s not, what then? Well, we can take +advantage of knowing that all the left elements are smaller than the +root node. So if the element we’re looking for is smaller than the root +node, check to see if it’s in the left subtree. If it’s bigger, check to +see if it’s in the right subtree.

        +
        treeElem :: (Ord a) => a -> Tree a -> Bool
         treeElem x EmptyTree = False
         treeElem x (Node a left right)
             | x == a = True
             | x < a  = treeElem x left
        -    | x > a  = treeElem x right
        -
        -

        All we had to do was write up the previous paragraph in code. Let’s have some fun with our trees! Instead of manually building one (although we could), we’ll use a fold to build up a tree from a list. Remember, pretty much everything that traverses a list one by one and then returns some sort of value can be implemented with a fold! We’re going to start with the empty tree and then approach a list from the right and just insert element after element into our accumulator tree.

        -
        
        -ghci> let nums = [8,6,4,1,7,3,5]
        +    | x > a  = treeElem x right
        +

        All we had to do was write up the previous paragraph in code. Let’s +have some fun with our trees! Instead of manually building one (although +we could), we’ll use a fold to build up a tree from a list. Remember, +pretty much everything that traverses a list one by one and then returns +some sort of value can be implemented with a fold! We’re going to start +with the empty tree and then approach a list from the right and just +insert element after element into our accumulator tree.

        +
        ghci> let nums = [8,6,4,1,7,3,5]
         ghci> let numsTree = foldr treeInsert EmptyTree nums
         ghci> numsTree
         Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 7 (Node 6 EmptyTree EmptyTree) (Node 8 EmptyTree EmptyTree))
        -

        In that foldr, treeInsert was the folding function (it takes a tree and a list element and produces a new tree) and EmptyTree was the starting accumulator. nums, of course, was the list we were folding over.

        -

        When we print our tree to the console, it’s not very readable, but if we try, we can make out its structure. We see that the root node is 5 and then it has two subtrees, one of which has the root node of 3 and the other a 7, etc.

        -
        
        -ghci> 8 `treeElem` numsTree
        +

        In that foldr, treeInsert was the folding +function (it takes a tree and a list element and produces a new tree) +and EmptyTree was the starting accumulator. +nums, of course, was the list we were folding over.

        +

        When we print our tree to the console, it’s not very readable, but if +we try, we can make out its structure. We see that the root node is 5 +and then it has two subtrees, one of which has the root node of 3 and +the other a 7, etc.

        +
        ghci> 8 `treeElem` numsTree
         True
         ghci> 100 `treeElem` numsTree
         False
         ghci> 1 `treeElem` numsTree
         True
         ghci> 10 `treeElem` numsTree
        -False
        -
        +False

        Checking for membership also works nicely. Cool.

        -

        So as you can see, algebraic data structures are a really cool and powerful concept in Haskell. We can use them to make anything from boolean values and weekday enumerations to binary search trees and more!

        +

        So as you can see, algebraic data structures are a really cool and +powerful concept in Haskell. We can use them to make anything from +boolean values and weekday enumerations to binary search trees and +more!

        Typeclasses 102

        -tweet -

        So far, we’ve learned about some of the standard Haskell typeclasses and we’ve seen which types are in them. We’ve also learned how to automatically make our own types instances of the standard typeclasses by asking Haskell to derive the instances for us. In this section, we’re going to learn how to make our own typeclasses and how to make types instances of them by hand.

        -

        A quick recap on typeclasses: typeclasses are like interfaces. A typeclass defines some behavior (like comparing for equality, comparing for ordering, enumeration) and then types that can behave in that way are made instances of that typeclass. The behavior of typeclasses is achieved by defining functions or just type declarations that we then implement. So when we say that a type is an instance of a typeclass, we mean that we can use the functions that the typeclass defines with that type.

        -

        Typeclasses have pretty much nothing to do with classes in languages like Java or Python. This confuses many people, so I want you to forget everything you know about classes in imperative languages right now.

        -

        For example, the Eq typeclass is for stuff that can be equated. It defines the functions == and /=. If we have a type (say, Car) and comparing two cars with the equality function == makes sense, then it makes sense for Car to be an instance of Eq.

        -

        This is how the Eq class is defined in the standard prelude:

        -
        
        -class Eq a where
        +tweet
        +

        So far, we’ve learned about some of the standard Haskell typeclasses +and we’ve seen which types are in them. We’ve also learned how to +automatically make our own types instances of the standard typeclasses +by asking Haskell to derive the instances for us. In this section, we’re +going to learn how to make our own typeclasses and how to make types +instances of them by hand.

        +

        A quick recap on typeclasses: typeclasses are like interfaces. A +typeclass defines some behavior (like comparing for equality, comparing +for ordering, enumeration) and then types that can behave in that way +are made instances of that typeclass. The behavior of typeclasses is +achieved by defining functions or just type declarations that we then +implement. So when we say that a type is an instance of a typeclass, we +mean that we can use the functions that the typeclass defines with that +type.

        +

        Typeclasses have pretty much nothing to do with classes in languages +like Java or Python. This confuses many people, so I want you to forget +everything you know about classes in imperative languages right now.

        +

        For example, the Eq typeclass is for stuff that can be +equated. It defines the functions == and /=. +If we have a type (say, Car) and comparing two cars with +the equality function == makes sense, then it makes sense +for Car to be an instance of Eq.

        +

        This is how the Eq class is defined in the standard +prelude:

        +
        class Eq a where
             (==) :: a -> a -> Bool
             (/=) :: a -> a -> Bool
             x == y = not (x /= y)
        -    x /= y = not (x == y)
        -
        -

        Woah, woah, woah! Some new strange syntax and keywords there! Don’t worry, this will all be clear in a second. First off, when we write class Eq a where, this means that we’re defining a new typeclass and that’s called Eq. The a is the type variable and it means that a will play the role of the type that we will soon be making an instance of Eq. It doesn’t have to be called a, it doesn’t even have to be one letter, it just has to be a lowercase word. Then, we define several functions. It’s not mandatory to implement the function bodies themselves, we just have to specify the type declarations for the functions.

        -

        Some people might understand this better if we wrote class Eq equatable where and then specified the type declarations like (==) :: equatable -> equatable -> Bool.

        -

        Anyway, we did implement the function bodies for the functions that Eq defines, only we defined them in terms of mutual recursion. We said that two instances of Eq are equal if they are not different and they are different if they are not equal. We didn’t have to do this, really, but we did and we’ll see how this helps us soon.

        -

        If we have say class Eq a where and then define a type declaration within that class like (==) :: a -> a -> Bool, then when we examine the type of that function later on, it will have the type of (Eq a) => a -> a -> Bool.

        -

        So once we have a class, what can we do with it? Well, not much, really. But once we start making types instances of that class, we start getting some nice functionality. So check out this type:

        -
        
        -data TrafficLight = Red | Yellow | Green
        -
        -

        It defines the states of a traffic light. Notice how we didn’t derive any class instances for it. That’s because we’re going to write up some instances by hand, even though we could derive them for types like Eq and Show. Here’s how we make it an instance of Eq.

        -
        
        -instance Eq TrafficLight where
        +    x /= y = not (x == y)
        +

        Woah, woah, woah! Some new strange syntax and keywords there! Don’t +worry, this will all be clear in a second. First off, when we write +class Eq a where, this means that we’re defining a new +typeclass and that’s called Eq. The a is the +type variable and it means that a will play the role of the +type that we will soon be making an instance of Eq. It +doesn’t have to be called a, it doesn’t even have to be one +letter, it just has to be a lowercase word. Then, we define several +functions. It’s not mandatory to implement the function bodies +themselves, we just have to specify the type declarations for the +functions.

        +
        +

        Some people might understand this better if we wrote +class Eq equatable where and then specified the type +declarations like +(==) :: equatable -> equatable -> Bool.

        +
        +

        Anyway, we did implement the function bodies for the +functions that Eq defines, only we defined them in terms of +mutual recursion. We said that two instances of Eq are +equal if they are not different and they are different if they are not +equal. We didn’t have to do this, really, but we did and we’ll see how +this helps us soon.

        +
        +

        If we have say class Eq a where and then define a type +declaration within that class like +(==) :: a -> a -> Bool, then when we examine the type +of that function later on, it will have the type of +(Eq a) => a -> a -> Bool.

        +
        +

        So once we have a class, what can we do with it? Well, not much, +really. But once we start making types instances of that class, we start +getting some nice functionality. So check out this type:

        +
        data TrafficLight = Red | Yellow | Green
        +

        It defines the states of a traffic light. Notice how we didn’t derive +any class instances for it. That’s because we’re going to write up some +instances by hand, even though we could derive them for types like +Eq and Show. Here’s how we make it an instance +of Eq.

        +
        instance Eq TrafficLight where
             Red == Red = True
             Green == Green = True
             Yellow == Yellow = True
        -    _ == _ = False
        -
        -

        We did it by using the instance keyword. So class is for defining new typeclasses and instance is for making our types instances of typeclasses. When we were defining Eq, we wrote class Eq a where and we said that a plays the role of whichever type will be made an instance later on. We can see that clearly here, because when we’re making an instance, we write instance Eq TrafficLight where. We replace the a with the actual type.

        -

        Because == was defined in terms of /= and vice versa in the class declaration, we only had to overwrite one of them in the instance declaration. That’s called the minimal complete definition for the typeclass — the minimum of functions that we have to implement so that our type can behave like the class advertises. To fulfill the minimal complete definition for Eq, we have to overwrite either one of == or /=. If Eq was defined simply like this:

        -
        
        -class Eq a where
        +    _ == _ = False
        +

        We did it by using the instance keyword. So class +is for defining new typeclasses and instance is for making our +types instances of typeclasses. When we were defining Eq, +we wrote class Eq a where and we said that a +plays the role of whichever type will be made an instance later on. We +can see that clearly here, because when we’re making an instance, we +write instance Eq TrafficLight where. We replace the +a with the actual type.

        +

        Because == was defined in terms of /= and +vice versa in the class declaration, we only had to overwrite +one of them in the instance declaration. That’s called the minimal +complete definition for the typeclass — the minimum of functions that we +have to implement so that our type can behave like the class advertises. +To fulfill the minimal complete definition for Eq, we have +to overwrite either one of == or /=. If +Eq was defined simply like this:

        +
        class Eq a where
             (==) :: a -> a -> Bool
        -    (/=) :: a -> a -> Bool
        -
        -

        we’d have to implement both of these functions when making a type an instance of it, because Haskell wouldn’t know how these two functions are related. The minimal complete definition would then be: both == and /=.

        -

        You can see that we implemented == simply by doing pattern matching. Since there are many more cases where two lights aren’t equal, we specified the ones that are equal and then just did a catch-all pattern saying that if it’s none of the previous combinations, then two lights aren’t equal.

        -

        Let’s make this an instance of Show by hand, too. To satisfy the minimal complete definition for Show, we just have to implement its show function, which takes a value and turns it into a string.

        -
        
        -instance Show TrafficLight where
        +    (/=) :: a -> a -> Bool
        +

        we’d have to implement both of these functions when making a type an +instance of it, because Haskell wouldn’t know how these two functions +are related. The minimal complete definition would then be: both +== and /=.

        +

        You can see that we implemented == simply by doing +pattern matching. Since there are many more cases where two lights +aren’t equal, we specified the ones that are equal and then just did a +catch-all pattern saying that if it’s none of the previous combinations, +then two lights aren’t equal.

        +

        Let’s make this an instance of Show by hand, too. To +satisfy the minimal complete definition for Show, we just +have to implement its show function, which takes a value +and turns it into a string.

        +
        instance Show TrafficLight where
             show Red = "Red light"
             show Yellow = "Yellow light"
        -    show Green = "Green light"
        -
        -

        Once again, we used pattern matching to achieve our goals. Let’s see how it works in action:

        -
        
        -ghci> Red == Red
        +    show Green = "Green light"
        +

        Once again, we used pattern matching to achieve our goals. Let’s see +how it works in action:

        +
        ghci> Red == Red
         True
         ghci> Red == Yellow
         False
         ghci> Red `elem` [Red, Yellow, Green]
         True
         ghci> [Red, Yellow, Green]
        -[Red light,Yellow light,Green light]
        -
        -

        Nice. We could have just derived Eq and it would have had the same effect (but we didn’t for educational purposes). However, deriving Show would have just directly translated the value constructors to strings. But if we want lights to appear like "Red light", then we have to make the instance declaration by hand.

        -

        You can also make typeclasses that are subclasses of other typeclasses. The class declaration for Num is a bit long, but here’s the first part:

        -
        
        -class (Eq a) => Num a where
        -   ...  
        -

        As we mentioned previously, there are a lot of places where we can cram in class constraints. So this is just like writing class Num a where, only we state that our type a must be an instance of Eq. We’re essentially saying that we have to make a type an instance of Eq before we can make it an instance of Num. Before some type can be considered a number, it makes sense that we can determine whether values of that type can be equated or not. That’s all there is to subclassing really, it’s just a class constraint on a class declaration! When defining function bodies in the class declaration or when defining them in instance declarations, we can assume that a is a part of Eq and so we can use == on values of that type.

        -

        But how are the Maybe or list types made as instances of typeclasses? What makes Maybe different from, say, TrafficLight is that Maybe in itself isn’t a concrete type, it’s a type constructor that takes one type parameter (like Char or something) to produce a concrete type (like Maybe Char). Let’s take a look at the Eq typeclass again:

        -
        
        -class Eq a where
        +[Red light,Yellow light,Green light]
        +

        Nice. We could have just derived Eq and it would have +had the same effect (but we didn’t for educational purposes). However, +deriving Show would have just directly translated the value +constructors to strings. But if we want lights to appear like +"Red light", then we have to make the instance declaration +by hand.

        +

        You can also make typeclasses that are subclasses of other +typeclasses. The class declaration for Num is a +bit long, but here’s the first part:

        +
        class (Eq a) => Num a where
        +   ...
        +

        As we mentioned previously, there are a lot of places where we can +cram in class constraints. So this is just like writing +class Num a where, only we state that our type +a must be an instance of Eq. We’re essentially +saying that we have to make a type an instance of Eq before +we can make it an instance of Num. Before some type can be +considered a number, it makes sense that we can determine whether values +of that type can be equated or not. That’s all there is to subclassing +really, it’s just a class constraint on a class declaration! +When defining function bodies in the class declaration or when +defining them in instance declarations, we can assume that +a is a part of Eq and so we can use +== on values of that type.

        +

        But how are the Maybe or list types made as instances of +typeclasses? What makes Maybe different from, say, +TrafficLight is that Maybe in itself isn’t a +concrete type, it’s a type constructor that takes one type parameter +(like Char or something) to produce a concrete type (like +Maybe Char). Let’s take a look at the Eq +typeclass again:

        +
        class Eq a where
             (==) :: a -> a -> Bool
             (/=) :: a -> a -> Bool
             x == y = not (x /= y)
        -    x /= y = not (x == y) 
        -

        From the type declarations, we see that the a is used as a concrete type because all the types in functions have to be concrete (remember, you can’t have a function of the type a -> Maybe but you can have a function of a -> Maybe a or Maybe Int -> Maybe String). That’s why we can’t do something like

        -
        
        -instance Eq Maybe where
        -    ...  
        -

        Because like we’ve seen, the a has to be a concrete type but Maybe isn’t a concrete type. It’s a type constructor that takes one parameter and then produces a concrete type. It would also be tedious to write instance Eq (Maybe Int) where, instance Eq (Maybe Char) where, etc. for every type ever. So we could write it out like so:

        -
        
        -instance Eq (Maybe m) where
        +    x /= y = not (x == y)
        +

        From the type declarations, we see that the a is used as +a concrete type because all the types in functions have to be concrete +(remember, you can’t have a function of the type +a -> Maybe but you can have a function of +a -> Maybe a or +Maybe Int -> Maybe String). That’s why we can’t do +something like

        +
        instance Eq Maybe where
        +    ...
        +

        Because like we’ve seen, the a has to be a concrete type +but Maybe isn’t a concrete type. It’s a type constructor +that takes one parameter and then produces a concrete type. It would +also be tedious to write instance Eq (Maybe Int) where, +instance Eq (Maybe Char) where, etc. for every type ever. +So we could write it out like so:

        +
        instance Eq (Maybe m) where
             Just x == Just y = x == y
             Nothing == Nothing = True
        -    _ == _ = False
        -      
        -

        This is like saying that we want to make all types of the form Maybe something an instance of Eq. We actually could have written (Maybe something), but we usually opt for single letters to be true to the Haskell style. The (Maybe m) here plays the role of the a from class Eq a where. While Maybe isn’t a concrete type, Maybe m is. By specifying a type parameter (m, which is in lowercase), we said that we want all types that are in the form of Maybe m, where m is any type, to be an instance of Eq.

        -

        There’s one problem with this though. Can you spot it? We use == on the contents of the Maybe but we have no assurance that what the Maybe contains can be used with Eq! That’s why we have to modify our instance declaration like this:

        -
        
        -instance (Eq m) => Eq (Maybe m) where
        +    _ == _ = False
        +

        This is like saying that we want to make all types of the form +Maybe something an instance of Eq. We actually +could have written (Maybe something), but we usually opt +for single letters to be true to the Haskell style. The +(Maybe m) here plays the role of the a from +class Eq a where. While Maybe isn’t a concrete +type, Maybe m is. By specifying a type parameter +(m, which is in lowercase), we said that we want all types +that are in the form of Maybe m, where m is +any type, to be an instance of Eq.

        +

        There’s one problem with this though. Can you spot it? We use +== on the contents of the Maybe but we have no +assurance that what the Maybe contains can be used with +Eq! That’s why we have to modify our instance +declaration like this:

        +
        instance (Eq m) => Eq (Maybe m) where
             Just x == Just y = x == y
             Nothing == Nothing = True
        -    _ == _ = False
        -      
        -

        We had to add a class constraint! With this instance declaration, we say this: we want all types of the form Maybe m to be part of the Eq typeclass, but only those types where the m (so what’s contained inside the Maybe) is also a part of Eq. This is actually how Haskell would derive the instance too.

        -

        Most of the times, class constraints in class declarations are used for making a typeclass a subclass of another typeclass and class constraints in instance declarations are used to express requirements about the contents of some type. For instance, here we required the contents of the Maybe to also be part of the Eq typeclass.

        -

        When making instances, if you see that a type is used as a concrete type in the type declarations (like the a in a -> a -> Bool), you have to supply type parameters and add parentheses so that you end up with a concrete type.

        -

        Take into account that the type you’re trying to make an instance of will replace the parameter in the class declaration. The a from class Eq a where will be replaced with a real type when you make an instance, so try mentally putting your type into the function type declarations as well. (==) :: Maybe -> Maybe -> Bool doesn’t make much sense but (==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. But this is just something to think about, because == will always have a type of (==) :: (Eq a) => a -> a -> Bool, no matter what instances we make.

        -

        Ooh, one more thing, check this out! If you want to see what the instances of a typeclass are, just do :info YourTypeClass in GHCI. So typing :info Num will show which functions the typeclass defines and it will give you a list of the types in the typeclass. :info works for types and type constructors too. If you do :info Maybe, it will show you all the typeclasses that Maybe is an instance of. Also :info can show you the type declaration of a function. I think that’s pretty cool.

        + _ == _ = False
        +

        We had to add a class constraint! With this instance +declaration, we say this: we want all types of the form +Maybe m to be part of the Eq typeclass, but +only those types where the m (so what’s contained inside +the Maybe) is also a part of Eq. This is +actually how Haskell would derive the instance too.

        +

        Most of the times, class constraints in class declarations +are used for making a typeclass a subclass of another typeclass and +class constraints in instance declarations are used to express +requirements about the contents of some type. For instance, here we +required the contents of the Maybe to also be part of the +Eq typeclass.

        +

        When making instances, if you see that a type is used as a concrete +type in the type declarations (like the a in +a -> a -> Bool), you have to supply type parameters +and add parentheses so that you end up with a concrete type.

        +
        +

        Take into account that the type you’re trying to make an instance of +will replace the parameter in the class declaration. The +a from class Eq a where will be replaced with +a real type when you make an instance, so try mentally putting your type +into the function type declarations as well. +(==) :: Maybe -> Maybe -> Bool doesn’t make much +sense but +(==) :: (Eq m) => Maybe m -> Maybe m -> Bool does. +But this is just something to think about, because == will +always have a type of +(==) :: (Eq a) => a -> a -> Bool, no matter what +instances we make.

        +
        +

        Ooh, one more thing, check this out! If you want to see what the +instances of a typeclass are, just do :info YourTypeClass +in GHCI. So typing :info Num will show which functions the +typeclass defines and it will give you a list of the types in the +typeclass. :info works for types and type constructors too. +If you do :info Maybe, it will show you all the typeclasses +that Maybe is an instance of. Also :info can +show you the type declaration of a function. I think that’s pretty +cool.

        A yes-no typeclass

        -yesno -

        In JavaScript and some other weakly typed languages, you can put almost anything inside an if expression. For example, you can do all of the following: if (0) alert("YEAH!") else alert("NO!"), if ("") alert ("YEAH!") else alert("NO!"), if (false) alert("YEAH") else alert("NO!), etc. and all of these will throw an alert of NO!. If you do if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert a "YEAH!" because JavaScript considers non-empty strings to be a sort of true-ish value.

        -

        Even though strictly using Bool for boolean semantics works better in Haskell, let’s try and implement that JavaScript-ish behavior anyway. For fun! Let’s start out with a class declaration.

        -
        
        -class YesNo a where
        -    yesno :: a -> Bool
        -
        -

        Pretty simple. The YesNo typeclass defines one function. That function takes one value of a type that can be considered to hold some concept of true-ness and tells us for sure if it’s true or not. Notice that from the way we use the a in the function, a has to be a concrete type.

        -

        Next up, let’s define some instances. For numbers, we’ll assume that (like in JavaScript) any number that isn’t 0 is true-ish and 0 is false-ish.

        -
        
        -instance YesNo Int where
        +yesno
        +

        In JavaScript and some other weakly typed languages, you can put +almost anything inside an if expression. For example, you can do all of +the following: if (0) alert("YEAH!") else alert("NO!"), +if ("") alert ("YEAH!") else alert("NO!"), +if (false) alert("YEAH") else alert("NO!), etc. and all of +these will throw an alert of NO!. If you do +if ("WHAT") alert ("YEAH") else alert("NO!"), it will alert +a "YEAH!" because JavaScript considers non-empty strings to +be a sort of true-ish value.

        +

        Even though strictly using Bool for boolean semantics +works better in Haskell, let’s try and implement that JavaScript-ish +behavior anyway. For fun! Let’s start out with a class +declaration.

        +
        class YesNo a where
        +    yesno :: a -> Bool
        +

        Pretty simple. The YesNo typeclass defines one function. +That function takes one value of a type that can be considered to hold +some concept of true-ness and tells us for sure if it’s true or not. +Notice that from the way we use the a in the function, +a has to be a concrete type.

        +

        Next up, let’s define some instances. For numbers, we’ll assume that +(like in JavaScript) any number that isn’t 0 is true-ish and 0 is +false-ish.

        +
        instance YesNo Int where
             yesno 0 = False
        -    yesno _ = True
        -
        -

        Empty lists (and by extensions, strings) are a no-ish value, while non-empty lists are a yes-ish value.

        -
        
        -instance YesNo [a] where
        +    yesno _ = True
        +

        Empty lists (and by extensions, strings) are a no-ish value, while +non-empty lists are a yes-ish value.

        +
        instance YesNo [a] where
             yesno [] = False
        -    yesno _ = True
        -
        -

        Notice how we just put in a type parameter a in there to make the list a concrete type, even though we don’t make any assumptions about the type that’s contained in the list. What else, hmm … I know, Bool itself also holds true-ness and false-ness and it’s pretty obvious which is which.

        -
        
        -instance YesNo Bool where
        -    yesno = id
        -
        -

        Huh? What’s id? It’s just a standard library function that takes a parameter and returns the same thing, which is what we would be writing here anyway.

        + yesno _ = True
        +

        Notice how we just put in a type parameter a in there to +make the list a concrete type, even though we don’t make any assumptions +about the type that’s contained in the list. What else, hmm … I know, +Bool itself also holds true-ness and false-ness and it’s +pretty obvious which is which.

        +
        instance YesNo Bool where
        +    yesno = id
        +

        Huh? What’s id? It’s just a standard library function +that takes a parameter and returns the same thing, which is what we +would be writing here anyway.

        Let’s make Maybe a an instance too.

        -
        
        -instance YesNo (Maybe a) where
        +
        instance YesNo (Maybe a) where
             yesno (Just _) = True
        -    yesno Nothing = False
        -
        -

        We didn’t need a class constraint because we made no assumptions about the contents of the Maybe. We just said that it’s true-ish if it’s a Just value and false-ish if it’s a Nothing. We still had to write out (Maybe a) instead of just Maybe because if you think about it, a Maybe -> Bool function can’t exist (because Maybe isn’t a concrete type), whereas a Maybe a -> Bool is fine and dandy. Still, this is really cool because now, any type of the form Maybe something is part of YesNo and it doesn’t matter what that something is.

        -

        Previously, we defined a Tree a type, that represented a binary search tree. We can say an empty tree is false-ish and anything that’s not an empty tree is true-ish.

        -
        
        -instance YesNo (Tree a) where
        +    yesno Nothing = False
        +

        We didn’t need a class constraint because we made no assumptions +about the contents of the Maybe. We just said that it’s +true-ish if it’s a Just value and false-ish if it’s a +Nothing. We still had to write out (Maybe a) +instead of just Maybe because if you think about it, a +Maybe -> Bool function can’t exist (because +Maybe isn’t a concrete type), whereas a +Maybe a -> Bool is fine and dandy. Still, this is really +cool because now, any type of the form Maybe something is +part of YesNo and it doesn’t matter what that +something is.

        +

        Previously, we defined a Tree a type, that represented a +binary search tree. We can say an empty tree is false-ish and anything +that’s not an empty tree is true-ish.

        +
        instance YesNo (Tree a) where
             yesno EmptyTree = False
        -    yesno _ = True
        -
        -

        Can a traffic light be a yes or no value? Sure. If it’s red, you stop. If it’s green, you go. If it’s yellow? Eh, I usually run the yellows because I live for adrenaline.

        -
        
        -instance YesNo TrafficLight where
        +    yesno _ = True
        +

        Can a traffic light be a yes or no value? Sure. If it’s red, you +stop. If it’s green, you go. If it’s yellow? Eh, I usually run the +yellows because I live for adrenaline.

        +
        instance YesNo TrafficLight where
             yesno Red = False
        -    yesno _ = True
        -
        + yesno _ = True

        Cool, now that we have some instances, let’s go play!

        -
        
        -ghci> yesno $ length []
        +
        ghci> yesno $ length []
         False
         ghci> yesno "haha"
         True
        @@ -863,16 +1464,15 @@ 

        A yes-no typeclass

        ghci> yesno [0,0,0] True ghci> :t yesno -yesno :: (YesNo a) => a -> Bool -
        -

        Right, it works! Let’s make a function that mimics the if statement, but it works with YesNo values.

        -
        
        -yesnoIf :: (YesNo y) => y -> a -> a -> a
        -yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult
        -
        -

        Pretty straightforward. It takes a yes-no-ish value and two things. If the yes-no-ish value is more of a yes, it returns the first of the two things, otherwise it returns the second of them.

        -
        
        -ghci> yesnoIf [] "YEAH!" "NO!"
        +yesno :: (YesNo a) => a -> Bool
        +

        Right, it works! Let’s make a function that mimics the if statement, +but it works with YesNo values.

        +
        yesnoIf :: (YesNo y) => y -> a -> a -> a
        +yesnoIf yesnoVal yesResult noResult = if yesno yesnoVal then yesResult else noResult
        +

        Pretty straightforward. It takes a yes-no-ish value and two things. +If the yes-no-ish value is more of a yes, it returns the first of the +two things, otherwise it returns the second of them.

        +
        ghci> yesnoIf [] "YEAH!" "NO!"
         "NO!"
         ghci> yesnoIf [2,3,4] "YEAH!" "NO!"
         "YEAH!"
        @@ -881,166 +1481,397 @@ 

        A yes-no typeclass

        ghci> yesnoIf (Just 500) "YEAH!" "NO!" "YEAH!" ghci> yesnoIf Nothing "YEAH!" "NO!" -"NO!" -
        +"NO!"

        The Functor typeclass

        -

        So far, we’ve encountered a lot of the typeclasses in the standard library. We’ve played with Ord, which is for stuff that can be ordered. We’ve palled around with Eq, which is for things that can be equated. We’ve seen Show, which presents an interface for types whose values can be displayed as strings. Our good friend Read is there whenever we need to convert a string to a value of some type. And now, we’re going to take a look at the Functor typeclass, which is basically for things that can be mapped over. You’re probably thinking about lists now, since mapping over lists is such a dominant idiom in Haskell. And you’re right, the list type is part of the Functor typeclass.

        -

        What better way to get to know the Functor typeclass than to see how it’s implemented? Let’s take a peek.

        -
        
        -class Functor f where
        -    fmap :: (a -> b) -> f a -> f b
        -
        -I AM FUNCTOOOOR!!! -

        Alright. We see that it defines one function, fmap, and doesn’t provide any default implementation for it. The type of fmap is interesting. In the definitions of typeclasses so far, the type variable that played the role of the type in the typeclass was a concrete type, like the a in (==) :: (Eq a) => a -> a -> Bool. But now, the f is not a concrete type (a type that a value can hold, like Int, Bool or Maybe String), but a type constructor that takes one type parameter. A quick refresher example: Maybe Int is a concrete type, but Maybe is a type constructor that takes one type as the parameter. Anyway, we see that fmap takes a function from one type to another and a functor applied with one type and returns a functor applied with another type.

        -

        If this sounds a bit confusing, don’t worry. All will be revealed soon when we check out a few examples. Hmm, this type declaration for fmap reminds me of something. If you don’t know what the type signature of map is, it’s: map :: (a -> b) -> [a] -> [b].

        -

        Ah, interesting! It takes a function from one type to another and a list of one type and returns a list of another type. My friends, I think we have ourselves a functor! In fact, map is just a fmap that works only on lists. Here’s how the list is an instance of the Functor typeclass.

        -
        
        -instance Functor [] where
        -    fmap = map
        -
        -

        That’s it! Notice how we didn’t write instance Functor [a] where, because from fmap :: (a -> b) -> f a -> f b, we see that the f has to be a type constructor that takes one type. [a] is already a concrete type (of a list with any type inside it), while [] is a type constructor that takes one type and can produce types such as [Int], [String] or even [[String]].

        -

        Since for lists, fmap is just map, we get the same results when using them on lists.

        -
        
        -map :: (a -> b) -> [a] -> [b]
        +

        So far, we’ve encountered a lot of the typeclasses in the standard +library. We’ve played with Ord, which is for stuff that can +be ordered. We’ve palled around with Eq, which is for +things that can be equated. We’ve seen Show, which presents +an interface for types whose values can be displayed as strings. Our +good friend Read is there whenever we need to convert a +string to a value of some type. And now, we’re going to take a look at +the Functor typeclass, which is +basically for things that can be mapped over. You’re probably thinking +about lists now, since mapping over lists is such a dominant idiom in +Haskell. And you’re right, the list type is part of the +Functor typeclass.

        +

        What better way to get to know the Functor typeclass +than to see how it’s implemented? Let’s take a peek.

        +
        class Functor f where
        +    fmap :: (a -> b) -> f a -> f b
        +I AM FUNCTOOOOR!!! +

        Alright. We see that it defines one function, fmap, and +doesn’t provide any default implementation for it. The type of +fmap is interesting. In the definitions of typeclasses so +far, the type variable that played the role of the type in the typeclass +was a concrete type, like the a in +(==) :: (Eq a) => a -> a -> Bool. But now, the +f is not a concrete type (a type that a value can hold, +like Int, Bool or Maybe String), +but a type constructor that takes one type parameter. A quick refresher +example: Maybe Int is a concrete type, but +Maybe is a type constructor that takes one type as the +parameter. Anyway, we see that fmap takes a function from +one type to another and a functor applied with one type and returns a +functor applied with another type.

        +

        If this sounds a bit confusing, don’t worry. All will be revealed +soon when we check out a few examples. Hmm, this type declaration for +fmap reminds me of something. If you don’t know what the +type signature of map is, it’s: +map :: (a -> b) -> [a] -> [b].

        +

        Ah, interesting! It takes a function from one type to another and a +list of one type and returns a list of another type. My friends, I think +we have ourselves a functor! In fact, map is just a +fmap that works only on lists. Here’s how the list is an +instance of the Functor typeclass.

        +
        instance Functor [] where
        +    fmap = map
        +

        That’s it! Notice how we didn’t write +instance Functor [a] where, because from +fmap :: (a -> b) -> f a -> f b, we see that the +f has to be a type constructor that takes one type. +[a] is already a concrete type (of a list with any type +inside it), while [] is a type constructor that takes one +type and can produce types such as [Int], +[String] or even [[String]].

        +

        Since for lists, fmap is just map, we get +the same results when using them on lists.

        +
        map :: (a -> b) -> [a] -> [b]
         ghci> fmap (*2) [1..3]
         [2,4,6]
         ghci> map (*2) [1..3]
        -[2,4,6]
        -
        -

        What happens when we map or fmap over an empty list? Well, of course, we get an empty list. It just turns an empty list of type [a] into an empty list of type [b].

        -

        Types that can act like a box can be functors. You can think of a list as a box that has an infinite amount of little compartments and they can all be empty, one can be full and the others empty or a number of them can be full. So, what else has the properties of being like a box? For one, the Maybe a type. In a way, it’s like a box that can either hold nothing, in which case it has the value of Nothing, or it can hold one item, like "HAHA", in which case it has a value of Just "HAHA". Here’s how Maybe is a functor.

        -
        
        -instance Functor Maybe where
        +[2,4,6]
        +

        What happens when we map or fmap over an +empty list? Well, of course, we get an empty list. It just turns an +empty list of type [a] into an empty list of type +[b].

        +

        Types that can act like a box can be functors. You can think of a +list as a box that has an infinite amount of little compartments and +they can all be empty, one can be full and the others empty or a number +of them can be full. So, what else has the properties of being like a +box? For one, the Maybe a type. In a way, it’s like a box +that can either hold nothing, in which case it has the value of +Nothing, or it can hold one item, like "HAHA", +in which case it has a value of Just "HAHA". Here’s how +Maybe is a functor.

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

        Again, notice how we wrote instance Functor Maybe where instead of instance Functor (Maybe m) where, like we did when we were dealing with Maybe and YesNo. Functor wants a type constructor that takes one type and not a concrete type. If you mentally replace the fs with Maybes, fmap acts like a (a -> b) -> Maybe a -> Maybe b for this particular type, which looks OK. But if you replace f with (Maybe m), then it would seem to act like a (a -> b) -> Maybe m a -> Maybe m b, which doesn’t make any damn sense because Maybe takes just one type parameter.

        -

        Anyway, the fmap implementation is pretty simple. If it’s an empty value of Nothing, then just return a Nothing. If we map over an empty box, we get an empty box. It makes sense. Just like if we map over an empty list, we get back an empty list. If it’s not an empty value, but rather a single value packed up in a Just, then we apply the function on the contents of the Just.

        -
        
        -ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something serious.")
        +    fmap f Nothing = Nothing
        +

        Again, notice how we wrote instance Functor Maybe where +instead of instance Functor (Maybe m) where, like we did +when we were dealing with Maybe and YesNo. +Functor wants a type constructor that takes one type and +not a concrete type. If you mentally replace the fs with +Maybes, fmap acts like a +(a -> b) -> Maybe a -> Maybe b for this particular +type, which looks OK. But if you replace f with +(Maybe m), then it would seem to act like a +(a -> b) -> Maybe m a -> Maybe m b, which doesn’t +make any damn sense because Maybe takes just one type +parameter.

        +

        Anyway, the fmap implementation is pretty simple. If +it’s an empty value of Nothing, then just return a +Nothing. If we map over an empty box, we get an empty box. +It makes sense. Just like if we map over an empty list, we get back an +empty list. If it’s not an empty value, but rather a single value packed +up in a Just, then we apply the function on the contents of +the Just.

        +
        ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") (Just "Something serious.")
         Just "Something serious. HEY GUYS IM INSIDE THE JUST"
         ghci> fmap (++ " HEY GUYS IM INSIDE THE JUST") Nothing
         Nothing
         ghci> fmap (*2) (Just 200)
         Just 400
         ghci> fmap (*2) Nothing
        -Nothing
        -
        -

        Another thing that can be mapped over and made an instance of Functor is our Tree a type. It can be thought of as a box in a way (holds several or no values) and the Tree type constructor takes exactly one type parameter. If you look at fmap as if it were a function made only for Tree, its type signature would look like (a -> b) -> Tree a -> Tree b. We’re going to use recursion on this one. Mapping over an empty tree will produce an empty tree. Mapping over a non-empty tree will be a tree consisting of our function applied to the root value and its left and right subtrees will be the previous subtrees, only our function will be mapped over them.

        -
        
        -instance Functor Tree where
        +Nothing
        +

        Another thing that can be mapped over and made an instance of +Functor is our Tree a type. It can be thought +of as a box in a way (holds several or no values) and the +Tree type constructor takes exactly one type parameter. If +you look at fmap as if it were a function made only for +Tree, its type signature would look like +(a -> b) -> Tree a -> Tree b. We’re going to use +recursion on this one. Mapping over an empty tree will produce an empty +tree. Mapping over a non-empty tree will be a tree consisting of our +function applied to the root value and its left and right subtrees will +be the previous subtrees, only our function will be mapped over +them.

        +
        instance Functor Tree where
             fmap f EmptyTree = EmptyTree
        -    fmap f (Node x leftsub rightsub) = Node (f x) (fmap f leftsub) (fmap f rightsub)
        -
        -
        
        -ghci> fmap (*2) EmptyTree
        +    fmap f (Node x leftsub rightsub) = Node (f x) (fmap f leftsub) (fmap f rightsub)
        +
        ghci> fmap (*2) EmptyTree
         EmptyTree
         ghci> fmap (*4) (foldr treeInsert EmptyTree [5,7,3,2,1,7])
        -Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree
        -
        -

        Nice! Now how about Either a b? Can this be made a functor? The Functor typeclass wants a type constructor that takes only one type parameter but Either takes two. Hmmm! I know, we’ll partially apply Either by feeding it only one parameter so that it has one free parameter. Here’s how Either a is a functor in the standard libraries:

        -
        
        -instance Functor (Either a) where
        +Node 28 (Node 4 EmptyTree (Node 8 EmptyTree (Node 12 EmptyTree (Node 20 EmptyTree EmptyTree)))) EmptyTree
        +

        Nice! Now how about Either a b? Can this be made a +functor? The Functor typeclass wants a type constructor +that takes only one type parameter but Either takes two. +Hmmm! I know, we’ll partially apply Either by feeding it +only one parameter so that it has one free parameter. Here’s how +Either a is a functor in the standard libraries:

        +
        instance Functor (Either a) where
             fmap f (Right x) = Right (f x)
        -    fmap f (Left x) = Left x
        -
        -

        Well well, what did we do here? You can see how we made Either a an instance instead of just Either. That’s because Either a is a type constructor that takes one parameter, whereas Either takes two. If fmap was specifically for Either a, the type signature would then be (b -> c) -> Either a b -> Either a c because that’s the same as (b -> c) -> (Either a) b -> (Either a) c. In the implementation, we mapped in the case of a Right value constructor, but we didn’t in the case of a Left. Why is that? Well, if we look back at how the Either a b type is defined, it’s kind of like:

        -
        
        -data Either a b = Left a | Right b
        -
        -

        Well, if we wanted to map one function over both of them, a and b would have to be the same type. I mean, if we tried to map a function that takes a string and returns a string and the b was a string but the a was a number, that wouldn’t really work out. Also, from seeing what fmap’s type would be if it operated only on Either values, we see that the first parameter has to remain the same while the second one can change and the first parameter is actualized by the Left value constructor.

        -

        This also goes nicely with our box analogy if we think of the Left part as sort of an empty box with an error message written on the side telling us why it’s empty.

        -

        Maps from Data.Map can also be made a functor because they hold values (or not!). In the case of Map k v, fmap will map a function v -> v' over a map of type Map k v and return a map of type Map k v'.

        Note, the ' has no special meaning in types just like it doesn’t have special meaning when naming values. It’s used to denote things that are similar, only slightly changed.

        -

        Try figuring out how Map k is made an instance of Functor by yourself!

        -

        With the Functor typeclass, we’ve seen how typeclasses can represent pretty cool higher-order concepts. We’ve also had some more practice with partially applying types and making instances. In one of the next chapters, we’ll also take a look at some laws that apply for functors.

        -

        Just one more thing! Functors should obey some laws so that they may have some properties that we can depend on and not think about too much. If we use fmap (+1) over the list [1,2,3,4], we expect the result to be [2,3,4,5] and not its reverse, [5,4,3,2]. If we use fmap (\a -> a) (the identity function, which just returns its parameter) over some list, we expect to get back the same list as a result. For example, if we gave the wrong functor instance to our Tree type, using fmap over a tree where the left subtree of a node only has elements that are smaller than the node and the right subtree only has nodes that are larger than the node might produce a tree where that’s not the case. We’ll go over the functor laws in more detail in one of the next chapters.

        + fmap f (Left x) = Left x
        +

        Well well, what did we do here? You can see how we made +Either a an instance instead of just Either. +That’s because Either a is a type constructor that takes +one parameter, whereas Either takes two. If +fmap was specifically for Either a, the type +signature would then be +(b -> c) -> Either a b -> Either a c because +that’s the same as +(b -> c) -> (Either a) b -> (Either a) c. In the +implementation, we mapped in the case of a Right value +constructor, but we didn’t in the case of a Left. Why is +that? Well, if we look back at how the Either a b type is +defined, it’s kind of like:

        +
        data Either a b = Left a | Right b
        +

        Well, if we wanted to map one function over both of them, +a and b would have to be the same type. I +mean, if we tried to map a function that takes a string and returns a +string and the b was a string but the a was a +number, that wouldn’t really work out. Also, from seeing what +fmap’s type would be if it operated only on +Either values, we see that the first parameter has to +remain the same while the second one can change and the first parameter +is actualized by the Left value constructor.

        +

        This also goes nicely with our box analogy if we think of the +Left part as sort of an empty box with an error message +written on the side telling us why it’s empty.

        +

        Maps from Data.Map can also be made a functor because +they hold values (or not!). In the case of Map k v, +fmap will map a function v -> v' over a map +of type Map k v and return a map of type +Map k v'.

        +
        +

        Note, the ' has no special meaning in types just like it +doesn’t have special meaning when naming values. It’s used to denote +things that are similar, only slightly changed.

        +
        +

        Try figuring out how Map k is made an instance of +Functor by yourself!

        +

        With the Functor typeclass, we’ve seen how typeclasses +can represent pretty cool higher-order concepts. We’ve also had some +more practice with partially applying types and making instances. In one +of the next chapters, we’ll also take a look at some laws that apply for +functors.

        +
        +

        Just one more thing! Functors should obey some laws +so that they may have some properties that we can depend on and not +think about too much. If we use fmap (+1) over the list +[1,2,3,4], we expect the result to be +[2,3,4,5] and not its reverse, [5,4,3,2]. If +we use fmap (\a -> a) (the identity function, which just +returns its parameter) over some list, we expect to get back the same +list as a result. For example, if we gave the wrong functor instance to +our Tree type, using fmap over a tree where +the left subtree of a node only has elements that are smaller than the +node and the right subtree only has nodes that are larger than the node +might produce a tree where that’s not the case. We’ll go over the +functor laws in more detail in one of the next chapters.

        +

        Kinds and some type-foo

        -TYPE FOO MASTER -

        Type constructors take other types as parameters to eventually produce concrete types. That kind of reminds me of functions, which take values as parameters to produce values. We’ve seen that type constructors can be partially applied (Either String is a type that takes one type and produces a concrete type, like Either String Int), just like functions can. This is all very interesting indeed. In this section, we’ll take a look at formally defining how types are applied to type constructors, just like we took a look at formally defining how values are applied to functions by using type declarations. You don’t really have to read this section to continue on your magical Haskell quest and if you don’t understand it, don’t worry about it. However, getting this will give you a very thorough understanding of the type system.

        -

        So, values like 3, "YEAH" or takeWhile (functions are also values, because we can pass them around and such) each have their own type. Types are little labels that values carry so that we can reason about the values. But types have their own little labels, called kinds. A kind is more or less the type of a type. This may sound a bit weird and confusing, but it’s actually a really cool concept.

        -

        What are kinds and what are they good for? Well, let’s examine the kind of a type by using the :k command in GHCI.

        -
        
        -ghci> :k Int
        -Int :: *
        -
        -

        A star? How quaint. What does that mean? A * means that the type is a concrete type. A concrete type is a type that doesn’t take any type parameters and values can only have types that are concrete types. If I had to read * out loud (I haven’t had to do that so far), I’d say star or just type.

        +TYPE FOO MASTER +

        Type constructors take other types as parameters to eventually +produce concrete types. That kind of reminds me of functions, which take +values as parameters to produce values. We’ve seen that type +constructors can be partially applied (Either String is a +type that takes one type and produces a concrete type, like +Either String Int), just like functions can. This is all +very interesting indeed. In this section, we’ll take a look at formally +defining how types are applied to type constructors, just like we took a +look at formally defining how values are applied to functions by using +type declarations. You don’t really have to read this section to +continue on your magical Haskell quest and if you don’t +understand it, don’t worry about it. However, getting this will give you +a very thorough understanding of the type system.

        +

        So, values like 3, "YEAH" or +takeWhile (functions are also values, because we can pass +them around and such) each have their own type. Types are little labels +that values carry so that we can reason about the values. But types have +their own little labels, called kinds. A kind is more +or less the type of a type. This may sound a bit weird and confusing, +but it’s actually a really cool concept.

        +

        What are kinds and what are they good for? Well, let’s examine the +kind of a type by using the :k command in GHCI.

        +
        ghci> :k Int
        +Int :: *
        +

        A star? How quaint. What does that mean? A * means that +the type is a concrete type. A concrete type is a type that doesn’t take +any type parameters and values can only have types that are concrete +types. If I had to read * out loud (I haven’t had to do +that so far), I’d say star or just type.

        Okay, now let’s see what the kind of Maybe is.

        -
        
        -ghci> :k Maybe
        -Maybe :: * -> *
        -
        -

        The Maybe type constructor takes one concrete type (like Int) and then returns a concrete type like Maybe Int. And that’s what this kind tells us. Just like Int -> Int means that a function takes an Int and returns an Int, * -> * means that the type constructor takes one concrete type and returns a concrete type. Let’s apply the type parameter to Maybe and see what the kind of that type is.

        -
        
        -ghci> :k Maybe Int
        -Maybe Int :: *
        -
        -

        Just like I expected! We applied the type parameter to Maybe and got back a concrete type (that’s what * -> * means). A parallel (although not equivalent, types and kinds are two different things) to this is if we do :t isUpper and :t isUpper 'A'. isUpper has a type of Char -> Bool and isUpper 'A' has a type of Bool, because its value is basically True. Both those types, however, have a kind of *.

        -

        We used :k on a type to get its kind, just like we can use :t on a value to get its type. Like we said, types are the labels of values and kinds are the labels of types and there are parallels between the two.

        +
        ghci> :k Maybe
        +Maybe :: * -> *
        +

        The Maybe type constructor takes one concrete type (like +Int) and then returns a concrete type like +Maybe Int. And that’s what this kind tells us. Just like +Int -> Int means that a function takes an +Int and returns an Int, * -> * +means that the type constructor takes one concrete type and returns a +concrete type. Let’s apply the type parameter to Maybe and +see what the kind of that type is.

        +
        ghci> :k Maybe Int
        +Maybe Int :: *
        +

        Just like I expected! We applied the type parameter to +Maybe and got back a concrete type (that’s what +* -> * means). A parallel (although not equivalent, +types and kinds are two different things) to this is if we do +:t isUpper and :t isUpper 'A'. +isUpper has a type of Char -> Bool and +isUpper 'A' has a type of Bool, because its +value is basically True. Both those types, however, have a +kind of *.

        +

        We used :k on a type to get its kind, just like we can +use :t on a value to get its type. Like we said, types are +the labels of values and kinds are the labels of types and there are +parallels between the two.

        Let’s look at another kind.

        -
        
        -ghci> :k Either
        -Either :: * -> * -> *
        -
        -

        Aha, this tells us that Either takes two concrete types as type parameters to produce a concrete type. It also looks kind of like a type declaration of a function that takes two values and returns something. Type constructors are curried (just like functions), so we can partially apply them.

        -
        
        -ghci> :k Either String
        +
        ghci> :k Either
        +Either :: * -> * -> *
        +

        Aha, this tells us that Either takes two concrete types +as type parameters to produce a concrete type. It also looks kind of +like a type declaration of a function that takes two values and returns +something. Type constructors are curried (just like functions), so we +can partially apply them.

        +
        ghci> :k Either String
         Either String :: * -> *
         ghci> :k Either String Int
        -Either String Int :: *
        -
        -

        When we wanted to make Either a part of the Functor typeclass, we had to partially apply it because Functor wants types that take only one parameter while Either takes two. In other words, Functor wants types of kind * -> * and so we had to partially apply Either to get a type of kind * -> * instead of its original kind * -> * -> *. If we look at the definition of Functor again

        -
        
        -class Functor f where
        -    fmap :: (a -> b) -> f a -> f b
        -
        -

        we see that the f type variable is used as a type that takes one concrete type to produce a concrete type. We know it has to produce a concrete type because it’s used as the type of a value in a function. And from that, we can deduce that types that want to be friends with Functor have to be of kind * -> *.

        -

        Now, let’s do some type-foo. Take a look at this typeclass that I’m just going to make up right now:

        -
        
        -class Tofu t where
        -    tofu :: j a -> t a j
        -
        -

        Man, that looks weird. How would we make a type that could be an instance of that strange typeclass? Well, let’s look at what its kind would have to be. Because j a is used as the type of a value that the tofu function takes as its parameter, j a has to have a kind of *. We assume * for a and so we can infer that j has to have a kind of * -> *. We see that t has to produce a concrete value too and that it takes two types. And knowing that a has a kind of * and j has a kind of * -> *, we infer that t has to have a kind of * -> (* -> *) -> *. So it takes a concrete type (a), a type constructor that takes one concrete type (j) and produces a concrete type. Wow.

        -

        OK, so let’s make a type with a kind of * -> (* -> *) -> *. Here’s one way of going about it.

        -
        
        -data Frank a b  = Frank {frankField :: b a} deriving (Show)
        -
        -

        How do we know this type has a kind of * -> (* -> *) - > *? Well, fields in ADTs are made to hold values, so they must be of kind *, obviously. We assume * for a, which means that b takes one type parameter and so its kind is * -> *. Now we know the kinds of both a and b and because they’re parameters for Frank, we see that Frank has a kind of * -> (* -> *) -> * The first * represents a and the (* -> *) represents b. Let’s make some Frank values and check out their types.

        -
        
        -ghci> :t Frank {frankField = Just "HAHA"}
        +Either String Int :: *
        +

        When we wanted to make Either a part of the +Functor typeclass, we had to partially apply it because +Functor wants types that take only one parameter while +Either takes two. In other words, Functor +wants types of kind * -> * and so we had to partially +apply Either to get a type of kind * -> * +instead of its original kind * -> * -> *. If we look +at the definition of Functor again

        +
        class Functor f where
        +    fmap :: (a -> b) -> f a -> f b
        +

        we see that the f type variable is used as a type that +takes one concrete type to produce a concrete type. We know it has to +produce a concrete type because it’s used as the type of a value in a +function. And from that, we can deduce that types that want to be +friends with Functor have to be of kind +* -> *.

        +

        Now, let’s do some type-foo. Take a look at this typeclass that I’m +just going to make up right now:

        +
        class Tofu t where
        +    tofu :: j a -> t a j
        +

        Man, that looks weird. How would we make a type that could be an +instance of that strange typeclass? Well, let’s look at what its kind +would have to be. Because j a is used as the type of a +value that the tofu function takes as its parameter, +j a has to have a kind of *. We assume +* for a and so we can infer that +j has to have a kind of * -> *. We see that +t has to produce a concrete value too and that it takes two +types. And knowing that a has a kind of * and +j has a kind of * -> *, we infer that +t has to have a kind of +* -> (* -> *) -> *. So it takes a concrete type +(a), a type constructor that takes one concrete type +(j) and produces a concrete type. Wow.

        +

        OK, so let’s make a type with a kind of +* -> (* -> *) -> *. Here’s one way of going about +it.

        +
        data Frank a b  = Frank {frankField :: b a} deriving (Show)
        +

        How do we know this type has a kind of +* -> (* -> *) - > *? Well, fields in ADTs are made +to hold values, so they must be of kind *, obviously. We +assume * for a, which means that +b takes one type parameter and so its kind is +* -> *. Now we know the kinds of both a and +b and because they’re parameters for Frank, we +see that Frank has a kind of +* -> (* -> *) -> * The first * +represents a and the (* -> *) represents +b. Let’s make some Frank values and check out +their types.

        +
        ghci> :t Frank {frankField = Just "HAHA"}
         Frank {frankField = Just "HAHA"} :: Frank [Char] Maybe
         ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
         Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
         ghci> :t Frank {frankField = "YES"}
        -Frank {frankField = "YES"} :: Frank Char []
        -
        -

        Hmm. Because frankField has a type of form a b, its values must have types that are of a similar form as well. So they can be Just "HAHA", which has a type of Maybe [Char] or it can have a value of ['Y','E','S'], which has a type of [Char] (if we used our own list type for this, it would have a type of List Char). And we see that the types of the Frank values correspond with the kind for Frank. [Char] has a kind of * and Maybe has a kind of * -> *. Because in order to have a value, it has to be a concrete type and thus has to be fully applied, every value of Frank blah blaah has a kind of *.

        -

        Making Frank an instance of Tofu is pretty simple. We see that tofu takes a j a (so an example type of that form would be Maybe Int) and returns a t a j. So if we replace Frank with j, the result type would be Frank Int Maybe.

        -
        
        -instance Tofu Frank where
        -    tofu x = Frank x
        -
        -
        
        -ghci> tofu (Just 'a') :: Frank Char Maybe
        +Frank {frankField = "YES"} :: Frank Char []
        +

        Hmm. Because frankField has a type of form +a b, its values must have types that are of a similar form +as well. So they can be Just "HAHA", which has a type of +Maybe [Char] or it can have a value of +['Y','E','S'], which has a type of [Char] (if +we used our own list type for this, it would have a type of +List Char). And we see that the types of the +Frank values correspond with the kind for +Frank. [Char] has a kind of * and +Maybe has a kind of * -> *. Because in +order to have a value, it has to be a concrete type and thus has to be +fully applied, every value of Frank blah blaah has a kind +of *.

        +

        Making Frank an instance of Tofu is pretty +simple. We see that tofu takes a j a (so an +example type of that form would be Maybe Int) and returns a +t a j. So if we replace Frank with +j, the result type would be +Frank Int Maybe.

        +
        instance Tofu Frank where
        +    tofu x = Frank x
        +
        ghci> tofu (Just 'a') :: Frank Char Maybe
         Frank {frankField = Just 'a'}
         ghci> tofu ["HELLO"] :: Frank [Char] []
        -Frank {frankField = ["HELLO"]}
        -
        -

        Not very useful, but we did flex our type muscles. Let’s do some more type-foo. We have this data type:

        -
        
        -data Barry t k p = Barry { yabba :: p, dabba :: t k }
        -
        -

        And now we want to make it an instance of Functor. Functor wants types of kind * -> * but Barry doesn’t look like it has that kind. What is the kind of Barry? Well, we see it takes three type parameters, so it’s going to be something -> something -> something -> *. It’s safe to say that p is a concrete type and thus has a kind of *. For k, we assume * and so by extension, t has a kind of * -> *. Now let’s just replace those kinds with the somethings that we used as placeholders and we see it has a kind of (* -> *) -> * -> * -> *. Let’s check that with GHCI.

        -
        
        -ghci> :k Barry
        -Barry :: (* -> *) -> * -> * -> *
        -
        -

        Ah, we were right. How satisfying. Now, to make this type a part of Functor we have to partially apply the first two type parameters so that we’re left with * -> *. That means that the start of the instance declaration will be: instance Functor (Barry a b) where. If we look at fmap as if it was made specifically for Barry, it would have a type of fmap :: (a -> b) -> Barry c d a -> Barry c d b, because we just replace the Functor’s f with Barry c d. The third type parameter from Barry will have to change and we see that it’s conveniently in its own field.

        -
        
        -instance Functor (Barry a b) where
        -    fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}
        -
        -

        There we go! We just mapped the f over the first field.

        -

        In this section, we took a good look at how type parameters work and kind of formalized them with kinds, just like we formalized function parameters with type declarations. We saw that there are interesting parallels between functions and type constructors. They are, however, two completely different things. When working on real Haskell, you usually won’t have to mess with kinds and do kind inference by hand like we did now. Usually, you just have to partially apply your own type to * -> * or * when making it an instance of one of the standard typeclasses, but it’s good to know how and why that actually works. It’s also interesting to see that types have little types of their own. Again, you don’t really have to understand everything we did here to read on, but if you understand how kinds work, chances are that you have a very solid grasp of Haskell’s type system.

        +Frank {frankField = ["HELLO"]}
        +

        Not very useful, but we did flex our type muscles. Let’s do some more +type-foo. We have this data type:

        +
        data Barry t k p = Barry { yabba :: p, dabba :: t k }
        +

        And now we want to make it an instance of Functor. +Functor wants types of kind * -> * but +Barry doesn’t look like it has that kind. What is the kind +of Barry? Well, we see it takes three type parameters, so +it’s going to be +something -> something -> something -> *. It’s +safe to say that p is a concrete type and thus has a kind +of *. For k, we assume * and so +by extension, t has a kind of * -> *. Now +let’s just replace those kinds with the somethings that we used +as placeholders and we see it has a kind of +(* -> *) -> * -> * -> *. Let’s check that with +GHCI.

        +
        ghci> :k Barry
        +Barry :: (* -> *) -> * -> * -> *
        +

        Ah, we were right. How satisfying. Now, to make this type a part of +Functor we have to partially apply the first two type +parameters so that we’re left with * -> *. That means +that the start of the instance declaration will be: +instance Functor (Barry a b) where. If we look at +fmap as if it was made specifically for Barry, +it would have a type of +fmap :: (a -> b) -> Barry c d a -> Barry c d b, +because we just replace the Functor’s f with +Barry c d. The third type parameter from Barry +will have to change and we see that it’s conveniently in its own +field.

        +
        instance Functor (Barry a b) where
        +    fmap f (Barry {yabba = x, dabba = y}) = Barry {yabba = f x, dabba = y}
        +

        There we go! We just mapped the f over the first +field.

        +

        In this section, we took a good look at how type parameters work and +kind of formalized them with kinds, just like we formalized function +parameters with type declarations. We saw that there are interesting +parallels between functions and type constructors. They are, however, +two completely different things. When working on real Haskell, you +usually won’t have to mess with kinds and do kind inference by hand like +we did now. Usually, you just have to partially apply your own type to +* -> * or * when making it an instance of +one of the standard typeclasses, but it’s good to know how and why that +actually works. It’s also interesting to see that types have little +types of their own. Again, you don’t really have to understand +everything we did here to read on, but if you understand how kinds work, +chances are that you have a very solid grasp of Haskell’s type +system.

        • diff --git a/docs/modules.html b/docs/modules.html index 8bf1076..163da8c 100644 --- a/docs/modules.html +++ b/docs/modules.html @@ -31,432 +31,705 @@
        -

        Modules

        +

        Modules

        Loading modules

        -modules -

        A Haskell module is a collection of related functions, types and typeclasses. A Haskell program is a collection of modules where the main module loads up the other modules and then uses the functions defined in them to do something. Having code split up into several modules has quite a lot of advantages. If a module is generic enough, the functions it exports can be used in a multitude of different programs. If your own code is separated into self-contained modules which don’t rely on each other too much (we also say they are loosely coupled), you can reuse them later on. It makes the whole deal of writing code more manageable by having it split into several parts, each of which has some sort of purpose.

        -

        The Haskell standard library is split into modules, each of them contains functions and types that are somehow related and serve some common purpose. There’s a module for manipulating lists, a module for concurrent programming, a module for dealing with complex numbers, etc. All the functions, types and typeclasses that we’ve dealt with so far were part of the Prelude module, which is imported by default. In this chapter, we’re going to examine a few useful modules and the functions that they have. But first, we’re going to see how to import modules.

        -

        The syntax for importing modules in a Haskell script is import <module name>. This must be done before defining any functions, so imports are usually done at the top of the file. One script can, of course, import several modules. Just put each import statement into a separate line. Let’s import the Data.List module, which has a bunch of useful functions for working with lists and use a function that it exports to create a function that tells us how many unique elements a list has.

        -
        
        -import Data.List
        +modules
        +

        A Haskell module is a collection of related functions, types and +typeclasses. A Haskell program is a collection of modules where the main +module loads up the other modules and then uses the functions defined in +them to do something. Having code split up into several modules has +quite a lot of advantages. If a module is generic enough, the functions +it exports can be used in a multitude of different programs. If your own +code is separated into self-contained modules which don’t rely on each +other too much (we also say they are loosely coupled), you can reuse +them later on. It makes the whole deal of writing code more manageable +by having it split into several parts, each of which has some sort of +purpose.

        +

        The Haskell standard library is split into modules, each of them +contains functions and types that are somehow related and serve some +common purpose. There’s a module for manipulating lists, a module for +concurrent programming, a module for dealing with complex numbers, etc. +All the functions, types and typeclasses that we’ve dealt with so far +were part of the Prelude module, which is imported by +default. In this chapter, we’re going to examine a few useful modules +and the functions that they have. But first, we’re going to see how to +import modules.

        +

        The syntax for importing modules in a Haskell script is +import <module name>. This must be done before +defining any functions, so imports are usually done at the top of the +file. One script can, of course, import several modules. Just put each +import statement into a separate line. Let’s import the +Data.List module, which has a bunch of useful functions for +working with lists and use a function that it exports to create a +function that tells us how many unique elements a list has.

        +
        import Data.List
         
         numUniques :: (Eq a) => [a] -> Int
        -numUniques = length . nub
        -
        -

        When you do import Data.List, all the functions that Data.List exports become available in the global namespace, meaning that you can call them from wherever in the script. nub is a function defined in Data.List that takes a list and weeds out duplicate elements. Composing length and nub by doing length . nub produces a function that’s the equivalent of \xs -> length (nub xs).

        -

        You can also put the functions of modules into the global namespace when using GHCI. If you’re in GHCI and you want to be able to call the functions exported by Data.List, do this:

        -
        
        -ghci> :m + Data.List
        -
        -

        If we want to load up the names from several modules inside GHCI, we don’t have to do :m + several times, we can just load up several modules at once.

        -
        
        -ghci> :m + Data.List Data.Map Data.Set
        -
        -

        However, if you’ve loaded a script that already imports a module, you don’t need to use :m + to get access to it.

        -

        If you just need a couple of functions from a module, you can selectively import just those functions. If we wanted to import only the nub and sort functions from Data.List, we’d do this:

        -
        
        -import Data.List (nub, sort)
        -
        -

        You can also choose to import all of the functions of a module except a few select ones. That’s often useful when several modules export functions with the same name and you want to get rid of the offending ones. Say we already have our own function that’s called nub and we want to import all the functions from Data.List except the nub function:

        -
        
        -import Data.List hiding (nub)
        -
        -

        Another way of dealing with name clashes is to do qualified imports. The Data.Map module, which offers a data structure for looking up values by key, exports a bunch of functions with the same name as Prelude functions, like filter or null. So when we import Data.Map and then call filter, Haskell won’t know which function to use. Here’s how we solve this:

        -
        
        -import qualified Data.Map
        -
        -

        This makes it so that if we want to reference Data.Map’s filter function, we have to do Data.Map.filter, whereas just filter still refers to the normal filter we all know and love. But typing out Data.Map in front of every function from that module is kind of tedious. That’s why we can rename the qualified import to something shorter:

        -
        
        -import qualified Data.Map as M
        -
        -

        Now, to reference Data.Map’s filter function, we just use M.filter.

        -

        Use this handy reference to see which modules are in the standard library. A great way to pick up new Haskell knowledge is to just click through the standard library reference and explore the modules and their functions. You can also view the Haskell source code for each module. Reading the source code of some modules is a really good way to learn Haskell and get a solid feel for it.

        -

        To search for functions or to find out where they’re located, use Hoogle. It’s a really awesome Haskell search engine, you can search by name, module name or even type signature.

        +numUniques = length . nub
        +

        When you do import Data.List, all the functions that +Data.List exports become available in the global namespace, +meaning that you can call them from wherever in the script. +nub is a function defined in Data.List that +takes a list and weeds out duplicate elements. Composing +length and nub by doing +length . nub produces a function that’s the equivalent of +\xs -> length (nub xs).

        +

        You can also put the functions of modules into the global namespace +when using GHCI. If you’re in GHCI and you want to be able to call the +functions exported by Data.List, do this:

        +
        ghci> :m + Data.List
        +

        If we want to load up the names from several modules inside GHCI, we +don’t have to do :m + several times, we can just load up +several modules at once.

        +
        ghci> :m + Data.List Data.Map Data.Set
        +

        However, if you’ve loaded a script that already imports a module, you +don’t need to use :m + to get access to it.

        +

        If you just need a couple of functions from a module, you can +selectively import just those functions. If we wanted to import only the +nub and sort functions from +Data.List, we’d do this:

        +
        import Data.List (nub, sort)
        +

        You can also choose to import all of the functions of a module except +a few select ones. That’s often useful when several modules export +functions with the same name and you want to get rid of the offending +ones. Say we already have our own function that’s called +nub and we want to import all the functions from +Data.List except the nub function:

        +
        import Data.List hiding (nub)
        +

        Another way of dealing with name clashes is to do qualified imports. +The Data.Map module, which offers a data structure for +looking up values by key, exports a bunch of functions with the same +name as Prelude functions, like filter or +null. So when we import Data.Map and then call +filter, Haskell won’t know which function to use. Here’s +how we solve this:

        +
        import qualified Data.Map
        +

        This makes it so that if we want to reference Data.Map’s +filter function, we have to do +Data.Map.filter, whereas just filter still +refers to the normal filter we all know and love. But +typing out Data.Map in front of every function from that +module is kind of tedious. That’s why we can rename the qualified import +to something shorter:

        +
        import qualified Data.Map as M
        +

        Now, to reference Data.Map’s filter +function, we just use M.filter.

        +

        Use this +handy reference to see which modules are in the standard library. A +great way to pick up new Haskell knowledge is to just click through the +standard library reference and explore the modules and their functions. +You can also view the Haskell source code for each module. Reading the +source code of some modules is a really good way to learn Haskell and +get a solid feel for it.

        +

        To search for functions or to find out where they’re located, use Hoogle. It’s a really awesome +Haskell search engine, you can search by name, module name or even type +signature.

        Data.List

        -

        The Data.List module is all about lists, obviously. It provides some very useful functions for dealing with them. We’ve already met some of its functions (like map and filter) because the Prelude module exports some functions from Data.List for convenience. You don’t have to import Data.List via a qualified import because it doesn’t clash with any Prelude names except for those that Prelude already steals from Data.List. Let’s take a look at some of the functions that we haven’t met before.

        -

        intersperse takes an element and a list and then puts that element in between each pair of elements in the list. Here’s a demonstration:

        -
        
        -ghci> intersperse '.' "MONKEY"
        +

        The Data.List module is all about lists, obviously. It +provides some very useful functions for dealing with them. We’ve already +met some of its functions (like map and +filter) because the Prelude module exports +some functions from Data.List for convenience. You don’t +have to import Data.List via a qualified import because it +doesn’t clash with any Prelude names except for those that +Prelude already steals from Data.List. Let’s +take a look at some of the functions that we haven’t met before.

        +

        intersperse takes an element and +a list and then puts that element in between each pair of elements in +the list. Here’s a demonstration:

        +
        ghci> intersperse '.' "MONKEY"
         "M.O.N.K.E.Y"
         ghci> intersperse 0 [1,2,3,4,5,6]
        -[1,0,2,0,3,0,4,0,5,0,6]
        -
        -

        intercalate takes a list and a list of lists. It then inserts that list in between all those lists and then flattens the result.

        -
        
        -ghci> intercalate " " ["hey","there","folks"]
        +[1,0,2,0,3,0,4,0,5,0,6]
        +

        intercalate takes a list and a +list of lists. It then inserts that list in between all those lists and +then flattens the result.

        +
        ghci> intercalate " " ["hey","there","folks"]
         "hey there folks"
         ghci> intercalate [0,0,0] [[1,2,3],[4,5,6],[7,8,9]]
        -[1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]
        -
        -

        transpose transposes a list of lists. If you look at a list of lists as a 2D matrix, the columns become the rows and vice versa.

        -
        
        -ghci> transpose [[1,2,3],[4,5,6],[7,8,9]]
        +[1,2,3,0,0,0,4,5,6,0,0,0,7,8,9]
        +

        transpose transposes a list of +lists. If you look at a list of lists as a 2D matrix, the columns become +the rows and vice versa.

        +
        ghci> transpose [[1,2,3],[4,5,6],[7,8,9]]
         [[1,4,7],[2,5,8],[3,6,9]]
         ghci> transpose ["hey","there","folks"]
        -["htg","ehu","yey","rs","e"]
        -
        -

        Say we have the polynomials 3x2 + 5x + 9, 10x3 + 9 and 8x3 + 5x2 + x - 1 and we want to add them together. We can use the lists [0,3,5,9], [10,0,0,9] and [8,5,1,-1] to represent them in Haskell. Now, to add them, all we have to do is this:

        -
        
        -ghci> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]
        -[18,8,6,17]
        -
        -

        When we transpose these three lists, the third powers are then in the first row, the second powers in the second one and so on. Mapping sum to that produces our desired result.

        -shopping lists -

        foldl' and foldl1' are stricter versions of their respective lazy incarnations. When using lazy folds on really big lists, you might often get a stack overflow error. The culprit for that is that due to the lazy nature of the folds, the accumulator value isn’t actually updated as the folding happens. What actually happens is that the accumulator kind of makes a promise that it will compute its value when asked to actually produce the result (also called a thunk). That happens for every intermediate accumulator and all those thunks overflow your stack. The strict folds aren’t lazy buggers and actually compute the intermediate values as they go along instead of filling up your stack with thunks. So if you ever get stack overflow errors when doing lazy folds, try switching to their strict versions.

        -

        concat flattens a list of lists into just a list of elements.

        -
        
        -ghci> concat ["foo","bar","car"]
        +["htg","ehu","yey","rs","e"]
        +

        Say we have the polynomials 3x2 + 5x + 9, +10x3 + 9 and 8x3 + 5x2 + x +- 1 and we want to add them together. We can use the lists +[0,3,5,9], [10,0,0,9] and +[8,5,1,-1] to represent them in Haskell. Now, to add them, +all we have to do is this:

        +
        ghci> map sum $ transpose [[0,3,5,9],[10,0,0,9],[8,5,1,-1]]
        +[18,8,6,17]
        +

        When we transpose these three lists, the third powers are then in the +first row, the second powers in the second one and so on. Mapping +sum to that produces our desired result.

        +shopping lists +

        foldl' and foldl1' are stricter versions of their +respective lazy incarnations. When using lazy folds on really big lists, +you might often get a stack overflow error. The culprit for that is that +due to the lazy nature of the folds, the accumulator value isn’t +actually updated as the folding happens. What actually happens is that +the accumulator kind of makes a promise that it will compute its value +when asked to actually produce the result (also called a thunk). That +happens for every intermediate accumulator and all those thunks overflow +your stack. The strict folds aren’t lazy buggers and actually compute +the intermediate values as they go along instead of filling up your +stack with thunks. So if you ever get stack overflow errors when doing +lazy folds, try switching to their strict versions.

        +

        concat flattens a list of lists +into just a list of elements.

        +
        ghci> concat ["foo","bar","car"]
         "foobarcar"
         ghci> concat [[3,4,5],[2,3,4],[2,1,1]]
        -[3,4,5,2,3,4,2,1,1]
        -
        -

        It will just remove one level of nesting. So if you want to completely flatten [[[2,3],[3,4,5],[2]],[[2,3],[3,4]]], which is a list of lists of lists, you have to concatenate it twice.

        -

        Doing concatMap is the same as first mapping a function to a list and then concatenating the list with concat.

        -
        
        -ghci> concatMap (replicate 4) [1..3]
        -[1,1,1,1,2,2,2,2,3,3,3,3]
        -
        -

        and takes a list of boolean values and returns True only if all the values in the list are True.

        -
        
        -ghci> and $ map (>4) [5,6,7,8]
        +[3,4,5,2,3,4,2,1,1]
        +

        It will just remove one level of nesting. So if you want to +completely flatten [[[2,3],[3,4,5],[2]],[[2,3],[3,4]]], +which is a list of lists of lists, you have to concatenate it twice.

        +

        Doing concatMap is the same as +first mapping a function to a list and then concatenating the list with +concat.

        +
        ghci> concatMap (replicate 4) [1..3]
        +[1,1,1,1,2,2,2,2,3,3,3,3]
        +

        and takes a list of boolean +values and returns True only if all the values in the list +are True.

        +
        ghci> and $ map (>4) [5,6,7,8]
         True
         ghci> and $ map (==4) [4,4,4,3,4]
        -False
        -
        -

        or is like and, only it returns True if any of the boolean values in a list is True.

        -
        
        -ghci> or $ map (==4) [2,3,4,5,6,1]
        +False
        +

        or is like and, only +it returns True if any of the boolean values in a list is +True.

        +
        ghci> or $ map (==4) [2,3,4,5,6,1]
         True
         ghci> or $ map (>4) [1,2,3]
        -False
        -
        -

        any and all take a predicate and then check if any or all the elements in a list satisfy the predicate, respectively. Usually we use these two functions instead of mapping over a list and then doing and or or.

        -
        
        -ghci> any (==4) [2,3,5,6,1,4]
        +False
        +

        any and all take a predicate and then check if any +or all the elements in a list satisfy the predicate, respectively. +Usually we use these two functions instead of mapping over a list and +then doing and or or.

        +
        ghci> any (==4) [2,3,5,6,1,4]
         True
         ghci> all (>4) [6,9,10]
         True
         ghci> all (`elem` ['A'..'Z']) "HEYGUYSwhatsup"
         False
         ghci> any (`elem` ['A'..'Z']) "HEYGUYSwhatsup"
        -True
        -
        -

        iterate takes a function and a starting value. It applies the function to the starting value, then it applies that function to the result, then it applies the function to that result again, etc. It returns all the results in the form of an infinite list.

        -
        
        -ghci> take 10 $ iterate (*2) 1
        +True
        +

        iterate takes a function and a +starting value. It applies the function to the starting value, then it +applies that function to the result, then it applies the function to +that result again, etc. It returns all the results in the form of an +infinite list.

        +
        ghci> take 10 $ iterate (*2) 1
         [1,2,4,8,16,32,64,128,256,512]
         ghci> take 3 $ iterate (++ "haha") "haha"
        -["haha","hahahaha","hahahahahaha"]
        -
        -

        splitAt takes a number and a list. It then splits the list at that many elements, returning the resulting two lists in a tuple.

        -
        
        -ghci> splitAt 3 "heyman"
        +["haha","hahahaha","hahahahahaha"]
        +

        splitAt takes a number and a +list. It then splits the list at that many elements, returning the +resulting two lists in a tuple.

        +
        ghci> splitAt 3 "heyman"
         ("hey","man")
         ghci> splitAt 100 "heyman"
         ("heyman","")
         ghci> splitAt (-3) "heyman"
         ("","heyman")
         ghci> let (a,b) = splitAt 3 "foobar" in b ++ a
        -"barfoo"
        -
        -

        takeWhile is a really useful little function. It takes elements from a list while the predicate holds and then when an element is encountered that doesn’t satisfy the predicate, it’s cut off. It turns out this is very useful.

        -
        
        -ghci> takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1]
        +"barfoo"
        +

        takeWhile is a really useful +little function. It takes elements from a list while the predicate holds +and then when an element is encountered that doesn’t satisfy the +predicate, it’s cut off. It turns out this is very useful.

        +
        ghci> takeWhile (>3) [6,5,4,3,2,1,2,3,4,5,4,3,2,1]
         [6,5,4]
         ghci> takeWhile (/=' ') "This is a sentence"
        -"This"
        -
        -

        Say we wanted to know the sum of all third powers that are under 10,000. We can’t map (^3) to [1..], apply a filter and then try to sum that up because filtering an infinite list never finishes. You may know that all the elements here are ascending but Haskell doesn’t. That’s why we can do this:

        -
        
        -ghci> sum $ takeWhile (<10000) $ map (^3) [1..]
        -53361
        -
        -

        We apply (^3) to an infinite list and then once an element that’s over 10,000 is encountered, the list is cut off. Now we can sum it up easily.

        -

        dropWhile is similar, only it drops all the elements while the predicate is true. Once predicate equates to False, it returns the rest of the list. An extremely useful and lovely function!

        -
        
        -ghci> dropWhile (/=' ') "This is a sentence"
        +"This"
        +

        Say we wanted to know the sum of all third powers that are under +10,000. We can’t map (^3) to [1..], apply a +filter and then try to sum that up because filtering an infinite list +never finishes. You may know that all the elements here are ascending +but Haskell doesn’t. That’s why we can do this:

        +
        ghci> sum $ takeWhile (<10000) $ map (^3) [1..]
        +53361
        +

        We apply (^3) to an infinite list and then once an +element that’s over 10,000 is encountered, the list is cut off. Now we +can sum it up easily.

        +

        dropWhile is similar, only it +drops all the elements while the predicate is true. Once predicate +equates to False, it returns the rest of the list. An +extremely useful and lovely function!

        +
        ghci> dropWhile (/=' ') "This is a sentence"
         " is a sentence"
         ghci> dropWhile (<3) [1,2,2,2,3,4,5,4,3,2,1]
        -[3,4,5,4,3,2,1]
        -
        -

        We’re given a list that represents the value of a stock by date. The list is made of tuples whose first component is the stock value, the second is the year, the third is the month and the fourth is the date. We want to know when the stock value first exceeded one thousand dollars!

        -
        
        -ghci> let stock = [(994.4,2008,9,1),(995.2,2008,9,2),(999.2,2008,9,3),(1001.4,2008,9,4),(998.3,2008,9,5)]
        +[3,4,5,4,3,2,1]
        +

        We’re given a list that represents the value of a stock by date. The +list is made of tuples whose first component is the stock value, the +second is the year, the third is the month and the fourth is the date. +We want to know when the stock value first exceeded one thousand +dollars!

        +
        ghci> let stock = [(994.4,2008,9,1),(995.2,2008,9,2),(999.2,2008,9,3),(1001.4,2008,9,4),(998.3,2008,9,5)]
         ghci> head (dropWhile (\(val,y,m,d) -> val < 1000) stock)
        -(1001.4,2008,9,4)
        -
        -

        span is kind of like takeWhile, only it returns a pair of lists. The first list contains everything the resulting list from takeWhile would contain if it were called with the same predicate and the same list. The second list contains the part of the list that would have been dropped.

        -
        
        -ghci> let (fw, rest) = span (/=' ') "This is a sentence" in "First word:" ++ fw ++ ", the rest:" ++ rest
        -"First word: This, the rest: is a sentence"
        -
        -

        Whereas span spans the list while the predicate is true, break breaks it when the predicate is first true. Doing break p is the equivalent of doing span (not . p).

        -
        
        -ghci> break (==4) [1,2,3,4,5,6,7]
        +(1001.4,2008,9,4)
        +

        span is kind of like +takeWhile, only it returns a pair of lists. The first list +contains everything the resulting list from takeWhile would +contain if it were called with the same predicate and the same list. The +second list contains the part of the list that would have been +dropped.

        +
        ghci> let (fw, rest) = span (/=' ') "This is a sentence" in "First word:" ++ fw ++ ", the rest:" ++ rest
        +"First word: This, the rest: is a sentence"
        +

        Whereas span spans the list while the predicate is true, +break breaks it when the predicate +is first true. Doing break p is the equivalent of doing +span (not . p).

        +
        ghci> break (==4) [1,2,3,4,5,6,7]
         ([1,2,3],[4,5,6,7])
         ghci> span (/=4) [1,2,3,4,5,6,7]
        -([1,2,3],[4,5,6,7])
        -
        -

        When using break, the second list in the result will start with the first element that satisfies the predicate.

        -

        sort simply sorts a list. The type of the elements in the list has to be part of the Ord typeclass, because if the elements of a list can’t be put in some kind of order, then the list can’t be sorted.

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

        When using break, the second list in the result will +start with the first element that satisfies the predicate.

        +

        sort simply sorts a list. The +type of the elements in the list has to be part of the Ord +typeclass, because if the elements of a list can’t be put in some kind +of order, then the list can’t be sorted.

        +
        ghci> sort [8,5,3,2,1,6,4,2]
         [1,2,2,3,4,5,6,8]
         ghci> sort "This will be sorted soon"
        -"    Tbdeehiillnooorssstw"
        -
        -

        group takes a list and groups adjacent elements into sublists if they are equal.

        -
        
        -ghci> group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
        -[[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]
        -
        -

        If we sort a list before grouping it, we can find out how many times each element appears in the list.

        -
        
        -ghci> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
        -[(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]
        -
        -

        inits and tails are like init and tail, only they recursively apply that to a list until there’s nothing left. Observe.

        -
        
        -ghci> inits "w00t"
        +"    Tbdeehiillnooorssstw"
        +

        group takes a list and groups +adjacent elements into sublists if they are equal.

        +
        ghci> group [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
        +[[1,1,1,1],[2,2,2,2],[3,3],[2,2,2],[5],[6],[7]]
        +

        If we sort a list before grouping it, we can find out how many times +each element appears in the list.

        +
        ghci> map (\l@(x:xs) -> (x,length l)) . group . sort $ [1,1,1,1,2,2,2,2,3,3,2,2,2,5,6,7]
        +[(1,4),(2,7),(3,2),(5,1),(6,1),(7,1)]
        +

        inits and tails are like init and +tail, only they recursively apply that to a list until +there’s nothing left. Observe.

        +
        ghci> inits "w00t"
         ["","w","w0","w00","w00t"]
         ghci> tails "w00t"
         ["w00t","00t","0t","t",""]
         ghci> let w = "w00t" in zip (inits w) (tails w)
        -[("","w00t"),("w","00t"),("w0","0t"),("w00","t"),("w00t","")]
        -
        +[("","w00t"),("w","00t"),("w0","0t"),("w00","t"),("w00t","")]

        Let’s use a fold to implement searching a list for a sublist.

        -
        
        -search :: (Eq a) => [a] -> [a] -> Bool
        +
        search :: (Eq a) => [a] -> [a] -> Bool
         search needle haystack =
             let nlen = length needle
        -    in  foldl (\acc x -> if take nlen x == needle then True else acc) False (tails haystack)
        -
        -

        First we call tails with the list in which we’re searching. Then we go over each tail and see if it starts with what we’re looking for.

        -

        With that, we actually just made a function that behaves like isInfixOf. isInfixOf searches for a sublist within a list and returns True if the sublist we’re looking for is somewhere inside the target list.

        -
        
        -ghci> "cat" `isInfixOf` "im a cat burglar"
        +    in  foldl (\acc x -> if take nlen x == needle then True else acc) False (tails haystack)
        +

        First we call tails with the list in which we’re +searching. Then we go over each tail and see if it starts with what +we’re looking for.

        +

        With that, we actually just made a function that behaves like isInfixOf. isInfixOf searches +for a sublist within a list and returns True if the sublist +we’re looking for is somewhere inside the target list.

        +
        ghci> "cat" `isInfixOf` "im a cat burglar"
         True
         ghci> "Cat" `isInfixOf` "im a cat burglar"
         False
         ghci> "cats" `isInfixOf` "im a cat burglar"
        -False
        -
        -

        isPrefixOf and isSuffixOf search for a sublist at the beginning and at the end of a list, respectively.

        -
        
        -ghci> "hey" `isPrefixOf` "hey there!"
        +False
        +

        isPrefixOf and isSuffixOf search for a sublist at the +beginning and at the end of a list, respectively.

        +
        ghci> "hey" `isPrefixOf` "hey there!"
         True
         ghci> "hey" `isPrefixOf` "oh hey there!"
         False
         ghci> "there!" `isSuffixOf` "oh hey there!"
         True
         ghci> "there!" `isSuffixOf` "oh hey there"
        -False
        -
        -

        elem and notElem check if an element is or isn’t inside a list.

        -

        partition takes a list and a predicate and returns a pair of lists. The first list in the result contains all the elements that satisfy the predicate, the second contains all the ones that don’t.

        -
        
        -ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
        +False
        +

        elem and notElem check if an element is or isn’t +inside a list.

        +

        partition takes a list and a +predicate and returns a pair of lists. The first list in the result +contains all the elements that satisfy the predicate, the second +contains all the ones that don’t.

        +
        ghci> partition (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
         ("BOBMORGAN","sidneyeddy")
         ghci> partition (>3) [1,3,5,6,3,2,1,0,3,7]
        -([5,6,7],[1,3,3,2,1,0,3])
        -
        -

        It’s important to understand how this is different from span and break:

        -
        
        -ghci> span (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
        -("BOB","sidneyMORGANeddy")
        -
        -

        While span and break are done once they encounter the first element that doesn’t and does satisfy the predicate, partition goes through the whole list and splits it up according to the predicate.

        -

        find takes a list and a predicate and returns the first element that satisfies the predicate. But it returns that element wrapped in a Maybe value. We’ll be covering algebraic data types more in depth in the next chapter but for now, this is what you need to know: a Maybe value can either be Just something or Nothing. Much like a list can be either an empty list or a list with some elements, a Maybe value can be either no elements or a single element. And like the type of a list of, say, integers is [Int], the type of maybe having an integer is Maybe Int. Anyway, let’s take our find function for a spin.

        -
        
        -ghci> find (>4) [1,2,3,4,5,6]
        +([5,6,7],[1,3,3,2,1,0,3])
        +

        It’s important to understand how this is different from +span and break:

        +
        ghci> span (`elem` ['A'..'Z']) "BOBsidneyMORGANeddy"
        +("BOB","sidneyMORGANeddy")
        +

        While span and break are done once they +encounter the first element that doesn’t and does satisfy the predicate, +partition goes through the whole list and splits it up +according to the predicate.

        +

        find takes a list and a predicate +and returns the first element that satisfies the predicate. But it +returns that element wrapped in a Maybe value. We’ll be +covering algebraic data types more in depth in the next chapter but for +now, this is what you need to know: a Maybe value can +either be Just something or Nothing. Much like +a list can be either an empty list or a list with some elements, a +Maybe value can be either no elements or a single element. +And like the type of a list of, say, integers is [Int], the +type of maybe having an integer is Maybe Int. Anyway, let’s +take our find function for a spin.

        +
        ghci> find (>4) [1,2,3,4,5,6]
         Just 5
         ghci> find (>9) [1,2,3,4,5,6]
         Nothing
         ghci> :t find
        -find :: (a -> Bool) -> [a] -> Maybe a
        -
        -

        Notice the type of find. Its result is Maybe a. That’s kind of like having the type of [a], only a value of the type Maybe can contain either no elements or one element, whereas a list can contain no elements, one element or several elements.

        -

        Remember when we were searching for the first time our stock went over $1000. We did head (dropWhile (\(val,y,m,d) -> val < 1000) stock). Remember that head is not really safe. What would happen if our stock never went over $1000? Our application of dropWhile would return an empty list and getting the head of an empty list would result in an error. However, if we rewrote that as find (\(val,y,m,d) -> val > 1000) stock, we’d be much safer. If our stock never went over $1000 (so if no element satisfied the predicate), we’d get back a Nothing. But if there was a valid answer in that list, we’d get, say, Just (1001.4,2008,9,4).

        -

        elemIndex is kind of like elem, only it doesn’t return a boolean value. It maybe returns the index of the element we’re looking for. If that element isn’t in our list, it returns a Nothing.

        -
        
        -ghci> :t elemIndex
        +find :: (a -> Bool) -> [a] -> Maybe a
        +

        Notice the type of find. Its result is +Maybe a. That’s kind of like having the type of +[a], only a value of the type Maybe can +contain either no elements or one element, whereas a list can contain no +elements, one element or several elements.

        +

        Remember when we were searching for the first time our stock went +over $1000. We did +head (dropWhile (\(val,y,m,d) -> val < 1000) stock). +Remember that head is not really safe. What would happen if +our stock never went over $1000? Our application of +dropWhile would return an empty list and getting the head +of an empty list would result in an error. However, if we rewrote that +as find (\(val,y,m,d) -> val > 1000) stock, we’d be +much safer. If our stock never went over $1000 (so if no element +satisfied the predicate), we’d get back a Nothing. But if +there was a valid answer in that list, we’d get, say, +Just (1001.4,2008,9,4).

        +

        elemIndex is kind of like +elem, only it doesn’t return a boolean value. It maybe +returns the index of the element we’re looking for. If that element +isn’t in our list, it returns a Nothing.

        +
        ghci> :t elemIndex
         elemIndex :: (Eq a) => a -> [a] -> Maybe Int
         ghci> 4 `elemIndex` [1,2,3,4,5,6]
         Just 3
         ghci> 10 `elemIndex` [1,2,3,4,5,6]
        -Nothing
        -
        -

        elemIndices is like elemIndex, only it returns a list of indices, in case the element we’re looking for crops up in our list several times. Because we’re using a list to represent the indices, we don’t need a Maybe type, because failure can be represented as the empty list, which is very much synonymous to Nothing.

        -
        
        -ghci> ' ' `elemIndices` "Where are the spaces?"
        -[5,9,13]
        -
        -

        findIndex is like find, but it maybe returns the index of the first element that satisfies the predicate. findIndices returns the indices of all elements that satisfy the predicate in the form of a list.

        -
        
        -ghci> findIndex (==4) [5,3,2,1,6,4]
        +Nothing
        +

        elemIndices is like +elemIndex, only it returns a list of indices, in case the +element we’re looking for crops up in our list several times. Because +we’re using a list to represent the indices, we don’t need a +Maybe type, because failure can be represented as the empty +list, which is very much synonymous to Nothing.

        +
        ghci> ' ' `elemIndices` "Where are the spaces?"
        +[5,9,13]
        +

        findIndex is like find, but it +maybe returns the index of the first element that satisfies the +predicate. findIndices returns the +indices of all elements that satisfy the predicate in the form of a +list.

        +
        ghci> findIndex (==4) [5,3,2,1,6,4]
         Just 5
         ghci> findIndex (==7) [5,3,2,1,6,4]
         Nothing
         ghci> findIndices (`elem` ['A'..'Z']) "Where Are The Caps?"
        -[0,6,10,14]
        -
        -

        We already covered zip and zipWith. We noted that they zip together two lists, either in a tuple or with a binary function (meaning such a function that takes two parameters). But what if we want to zip together three lists? Or zip three lists with a function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. While this may look like a hack, it works out pretty fine, because there aren’t many times when you want to zip 8 lists together. There’s also a very clever way for zipping infinite numbers of lists, but we’re not advanced enough to cover that just yet.

        -
        
        -ghci> zipWith3 (\x y z -> x + y + z) [1,2,3] [4,5,2,2] [2,2,3]
        +[0,6,10,14]
        +

        We already covered zip and zipWith. We +noted that they zip together two lists, either in a tuple or with a +binary function (meaning such a function that takes two parameters). But +what if we want to zip together three lists? Or zip three lists with a +function that takes three parameters? Well, for that, we have zip3, zip4, etc. and zipWith3, zipWith4, etc. These variants go up to 7. +While this may look like a hack, it works out pretty fine, because there +aren’t many times when you want to zip 8 lists together. There’s also a +very clever way for zipping infinite numbers of lists, but we’re not +advanced enough to cover that just yet.

        +
        ghci> zipWith3 (\x y z -> x + y + z) [1,2,3] [4,5,2,2] [2,2,3]
         [7,9,8]
         ghci> zip4 [2,3,3] [2,2,2] [5,5,3] [2,2,2]
        -[(2,2,5,2),(3,2,5,2),(3,2,3,2)]
        -
        -

        Just like with normal zipping, lists that are longer than the shortest list that’s being zipped are cut down to size.

        -

        lines is a useful function when dealing with files or input from somewhere. It takes a string and returns every line of that string as separate element of a list.

        -
        
        -ghci> lines "first line\nsecond line\nthird line"
        -["first line","second line","third line"]
        -
        -

        '\n' is the character for a unix newline. Backslashes have special meaning in Haskell strings and characters.

        -

        unlines is the inverse function of lines. It takes a list of strings and joins them together using a '\n'.

        -
        
        -ghci> unlines ["first line", "second line", "third line"]
        -"first line\nsecond line\nthird line\n"
        -
        -

        words and unwords are for splitting a line of text into words or joining a list of words into a text. Very useful.

        -
        
        -ghci> words "hey these are the words in this sentence"
        +[(2,2,5,2),(3,2,5,2),(3,2,3,2)]
        +

        Just like with normal zipping, lists that are longer than the +shortest list that’s being zipped are cut down to size.

        +

        lines is a useful function when +dealing with files or input from somewhere. It takes a string and +returns every line of that string as separate element of a list.

        +
        ghci> lines "first line\nsecond line\nthird line"
        +["first line","second line","third line"]
        +

        '\n' is the character for a unix newline. Backslashes +have special meaning in Haskell strings and characters.

        +

        unlines is the inverse function +of lines. It takes a list of strings and joins them +together using a '\n'.

        +
        ghci> unlines ["first line", "second line", "third line"]
        +"first line\nsecond line\nthird line\n"
        +

        words and unwords are for splitting a line of text +into words or joining a list of words into a text. Very useful.

        +
        ghci> words "hey these are the words in this sentence"
         ["hey","these","are","the","words","in","this","sentence"]
         ghci> words "hey these           are    the words in this\nsentence"
         ["hey","these","are","the","words","in","this","sentence"]
         ghci> unwords ["hey","there","mate"]
        -"hey there mate"
        -
        -

        We’ve already mentioned nub. It takes a list and weeds out the duplicate elements, returning a list whose every element is a unique snowflake! The function does have a kind of strange name. It turns out that “nub” means a small lump or essential part of something. In my opinion, they should use real words for function names instead of old-people words.

        -
        
        -ghci> nub [1,2,3,4,3,2,1,2,3,4,3,2,1]
        +"hey there mate"
        +

        We’ve already mentioned nub. It +takes a list and weeds out the duplicate elements, returning a list +whose every element is a unique snowflake! The function does have a kind +of strange name. It turns out that “nub” means a small lump or essential +part of something. In my opinion, they should use real words for +function names instead of old-people words.

        +
        ghci> nub [1,2,3,4,3,2,1,2,3,4,3,2,1]
         [1,2,3,4]
         ghci> nub "Lots of words and stuff"
        -"Lots fwrdanu"
        -
        -

        delete takes an element and a list and deletes the first occurrence of that element in the list.

        -
        
        -ghci> delete 'h' "hey there ghang!"
        +"Lots fwrdanu"
        +

        delete takes an element and a +list and deletes the first occurrence of that element in the list.

        +
        ghci> delete 'h' "hey there ghang!"
         "ey there ghang!"
         ghci> delete 'h' . delete 'h' $ "hey there ghang!"
         "ey tere ghang!"
         ghci> delete 'h' . delete 'h' . delete 'h' $ "hey there ghang!"
        -"ey tere gang!"
        -
        -

        \\ is the list difference function. It acts like a set difference, basically. For every element in the right-hand list, it removes a matching element in the left one.

        -
        
        -ghci> [1..10] \\ [2,5,9]
        +"ey tere gang!"
        +

        \\ is the list difference +function. It acts like a set difference, basically. For every element in +the right-hand list, it removes a matching element in the left one.

        +
        ghci> [1..10] \\ [2,5,9]
         [1,3,4,6,7,8,10]
         ghci> "Im a big baby" \\ "big"
        -"Im a  baby"
        -
        -

        Doing [1..10] \\ [2,5,9] is like doing delete 2 . delete 5 . delete 9 $ [1..10].

        -

        union also acts like a function on sets. It returns the union of two lists. It pretty much goes over every element in the second list and appends it to the first one if it isn’t already in yet. Watch out though, duplicates are removed from the second list!

        -
        
        -ghci> "hey man" `union` "man what's up"
        +"Im a  baby"
        +

        Doing [1..10] \\ [2,5,9] is like doing +delete 2 . delete 5 . delete 9 $ [1..10].

        +

        union also acts like a function +on sets. It returns the union of two lists. It pretty much goes over +every element in the second list and appends it to the first one if it +isn’t already in yet. Watch out though, duplicates are removed from the +second list!

        +
        ghci> "hey man" `union` "man what's up"
         "hey manwt'sup"
         ghci> [1..7] `union` [5..10]
        -[1,2,3,4,5,6,7,8,9,10]
        -
        -

        intersect works like set intersection. It returns only the elements that are found in both lists.

        -
        
        -ghci> [1..7] `intersect` [5..10]
        -[5,6,7]
        -
        -

        insert takes an element and a list of elements that can be sorted and inserts it into the last position where it’s still less than or equal to the next element. In other words, insert will start at the beginning of the list and then keep going until it finds an element that’s equal to or greater than the element that we’re inserting and it will insert it just before the element.

        -
        
        -ghci> insert 4 [3,5,1,2,8,2]
        +[1,2,3,4,5,6,7,8,9,10]
        +

        intersect works like set +intersection. It returns only the elements that are found in both +lists.

        +
        ghci> [1..7] `intersect` [5..10]
        +[5,6,7]
        +

        insert takes an element and a +list of elements that can be sorted and inserts it into the last +position where it’s still less than or equal to the next element. In +other words, insert will start at the beginning of the list +and then keep going until it finds an element that’s equal to or greater +than the element that we’re inserting and it will insert it just before +the element.

        +
        ghci> insert 4 [3,5,1,2,8,2]
         [3,4,5,1,2,8,2]
         ghci> insert 4 [1,3,4,4,1]
        -[1,3,4,4,4,1]
        -
        -

        The 4 is inserted right after the 3 and before the 5 in the first example and in between the 3 and 4 in the second example.

        -

        If we use insert to insert into a sorted list, the resulting list will be kept sorted.

        -
        
        -ghci> insert 4 [1,2,3,5,6,7]
        +[1,3,4,4,4,1]
        +

        The 4 is inserted right after the 3 and +before the 5 in the first example and in between the +3 and 4 in the second example.

        +

        If we use insert to insert into a sorted list, the +resulting list will be kept sorted.

        +
        ghci> insert 4 [1,2,3,5,6,7]
         [1,2,3,4,5,6,7]
         ghci> insert 'g' $ ['a'..'f'] ++ ['h'..'z']
         "abcdefghijklmnopqrstuvwxyz"
         ghci> insert 3 [1,2,4,3,2,1]
        -[1,2,3,4,3,2,1]
        -
        -

        What length, take, drop, splitAt, !! and replicate have in common is that they take an Int as one of their parameters (or return an Int), even though they could be more generic and usable if they just took any type that’s part of the Integral or Num typeclasses (depending on the functions). They do that for historical reasons. However, fixing that would probably break a lot of existing code. That’s why Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, length has a type signature of length :: [a] -> Int. If we try to get the average of a list of numbers by doing let xs = [1..6] in sum xs / length xs, we get a type error, because you can’t use / with an Int. genericLength, on the other hand, has a type signature of genericLength :: (Num a) => [b] -> a. Because a Num can act like a floating point number, getting the average by doing let xs = [1..6] in sum xs / genericLength xs works out just fine.

        -

        The nub, delete, union, intersect and group functions all have their more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is that the first set of functions use == to test for equality, whereas the By ones also take an equality function and then compare them by using that equality function. group is the same as groupBy (==).

        -

        For instance, say we have a list that describes the value of a function for every second. We want to segment it into sublists based on when the value was below zero and when it went above. If we just did a normal group, it would just group the equal adjacent values together. But what we want is to group them by whether they are negative or not. That’s where groupBy comes in! The equality function supplied to the By functions should take two elements of the same type and return True if it considers them equal by its standards.

        -
        
        -ghci> let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
        +[1,2,3,4,3,2,1]
        +

        What length, take, drop, +splitAt, !! and replicate have in +common is that they take an Int as one of their parameters +(or return an Int), even though they could be more generic +and usable if they just took any type that’s part of the +Integral or Num typeclasses (depending on the +functions). They do that for historical reasons. However, fixing that +would probably break a lot of existing code. That’s why +Data.List has their more generic equivalents, named genericLength, genericTake, genericDrop, genericSplitAt, genericIndex and genericReplicate. For instance, +length has a type signature of +length :: [a] -> Int. If we try to get the average of a +list of numbers by doing +let xs = [1..6] in sum xs / length xs, we get a type error, +because you can’t use / with an Int. +genericLength, on the other hand, has a type signature of +genericLength :: (Num a) => [b] -> a. Because a +Num can act like a floating point number, getting the +average by doing +let xs = [1..6] in sum xs / genericLength xs works out just +fine.

        +

        The nub, delete, union, +intersect and group functions all have their +more general counterparts called nubBy, deleteBy, unionBy, intersectBy and groupBy. The difference between them is +that the first set of functions use == to test for +equality, whereas the By ones also take an equality function +and then compare them by using that equality function. +group is the same as groupBy (==).

        +

        For instance, say we have a list that describes the value of a +function for every second. We want to segment it into sublists based on +when the value was below zero and when it went above. If we just did a +normal group, it would just group the equal adjacent values +together. But what we want is to group them by whether they are negative +or not. That’s where groupBy comes in! The equality +function supplied to the By functions should take two elements +of the same type and return True if it considers them equal +by its standards.

        +
        ghci> let values = [-4.3, -2.4, -1.2, 0.4, 2.3, 5.9, 10.5, 29.1, 5.3, -2.4, -14.5, 2.9, 2.3]
         ghci> groupBy (\x y -> (x > 0) == (y > 0)) values
        -[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
        -
        -

        From this, we clearly see which sections are positive and which are negative. The equality function supplied takes two elements and then returns True only if they’re both negative or if they’re both positive. This equality function can also be written as \x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), although I think the first way is more readable. An even clearer way to write equality functions for the By functions is if you import the on function from Data.Function. on is defined like this:

        -
        
        -on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
        -f `on` g = \x y -> f (g x) (g y)
        -
        -

        So doing (==) `on` (> 0) returns an equality function that looks like \x y -> (x > 0) == (y > 0). on is used a lot with the By functions because with it, we can do:

        -
        
        -ghci> groupBy ((==) `on` (> 0)) values
        -[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
        -
        -

        Very readable indeed! You can read it out loud: Group this by equality on whether the elements are greater than zero.

        -

        Similarly, the sort, insert, maximum and minimum also have their more general equivalents. Functions like groupBy take a function that determines when two elements are equal. sortBy, insertBy, maximumBy and minimumBy take a function that determine if one element is greater, smaller or equal to the other. The type signature of sortBy is sortBy :: (a -> a -> Ordering) -> [a] -> [a]. If you remember from before, the Ordering type can have a value of LT, EQ or GT. sort is the equivalent of sortBy compare, because compare just takes two elements whose type is in the Ord typeclass and returns their ordering relationship.

        -

        Lists can be compared, but when they are, they are compared lexicographically. What if we have a list of lists and we want to sort it not based on the inner lists’ contents but on their lengths? Well, as you’ve probably guessed, we’ll use the sortBy function.

        -
        
        -ghci> let xs = [[5,4,5,4,4],[1,2,3],[3,5,4,3],[],[2],[2,2]]
        +[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
        +

        From this, we clearly see which sections are positive and which are +negative. The equality function supplied takes two elements and then +returns True only if they’re both negative or if they’re +both positive. This equality function can also be written as +\x y -> (x > 0) && (y > 0) || (x <= 0) && (y <= 0), +although I think the first way is more readable. An even clearer way to +write equality functions for the By functions is if you import +the on function from +Data.Function. on is defined like this:

        +
        on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
        +f `on` g = \x y -> f (g x) (g y)
        +

        So doing (==) `on` (> 0) returns an equality function +that looks like \x y -> (x > 0) == (y > 0). +on is used a lot with the By functions because +with it, we can do:

        +
        ghci> groupBy ((==) `on` (> 0)) values
        +[[-4.3,-2.4,-1.2],[0.4,2.3,5.9,10.5,29.1,5.3],[-2.4,-14.5],[2.9,2.3]]
        +

        Very readable indeed! You can read it out loud: Group this by +equality on whether the elements are greater than zero.

        +

        Similarly, the sort, insert, +maximum and minimum also have their more +general equivalents. Functions like groupBy take a function +that determines when two elements are equal. sortBy, insertBy, maximumBy and minimumBy take a function that determine +if one element is greater, smaller or equal to the other. The type +signature of sortBy is +sortBy :: (a -> a -> Ordering) -> [a] -> [a]. +If you remember from before, the Ordering type can have a +value of LT, EQ or GT. +sort is the equivalent of sortBy compare, +because compare just takes two elements whose type is in the +Ord typeclass and returns their ordering relationship.

        +

        Lists can be compared, but when they are, they are compared +lexicographically. What if we have a list of lists and we want to sort +it not based on the inner lists’ contents but on their lengths? Well, as +you’ve probably guessed, we’ll use the sortBy function.

        +
        ghci> let xs = [[5,4,5,4,4],[1,2,3],[3,5,4,3],[],[2],[2,2]]
         ghci> sortBy (compare `on` length) xs
        -[[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]
        -
        -

        Awesome! compare `on` length … man, that reads almost like real English! If you’re not sure how exactly the on works here, compare `on` length is the equivalent of \x y -> length x `compare` length y. When you’re dealing with By functions that take an equality function, you usually do (==) `on` something and when you’re dealing with By functions that take an ordering function, you usually do compare `on` something.

        +[[],[2],[2,2],[1,2,3],[3,5,4,3],[5,4,5,4,4]]
        +

        Awesome! compare `on` length … man, that reads almost +like real English! If you’re not sure how exactly the on +works here, compare `on` length is the equivalent of +\x y -> length x `compare` length y. When you’re dealing +with By functions that take an equality function, you usually +do (==) `on` something and when you’re dealing with +By functions that take an ordering function, you usually do +compare `on` something.

        Data.Char

        -lego char -

        The Data.Char module does what its name suggests. It exports functions that deal with characters. It’s also helpful when filtering and mapping over strings because they’re just lists of characters.

        -

        Data.Char exports a bunch of predicates over characters. That is, functions that take a character and tell us whether some assumption about it is true or false. Here’s what they are:

        -

        isControl checks whether a character is a control character.

        -

        isSpace checks whether a character is a white-space characters. That includes spaces, tab characters, newlines, etc.

        -

        isLower checks whether a character is lower-cased.

        -

        isUpper checks whether a character is upper-cased.

        -

        isAlpha checks whether a character is a letter.

        -

        isAlphaNum checks whether a character is a letter or a number.

        -

        isPrint checks whether a character is printable. Control characters, for instance, are not printable.

        -

        isDigit checks whether a character is a digit.

        -

        isOctDigit checks whether a character is an octal digit.

        -

        isHexDigit checks whether a character is a hex digit.

        -

        isLetter checks whether a character is a letter.

        -

        isMark checks for Unicode mark characters. Those are characters that combine with preceding letters to form letters with accents. Use this if you are French.

        -

        isNumber checks whether a character is numeric.

        -

        isPunctuation checks whether a character is punctuation.

        -

        isSymbol checks whether a character is a fancy mathematical or currency symbol.

        -

        isSeparator checks for Unicode spaces and separators.

        -

        isAscii checks whether a character falls into the first 128 characters of the Unicode character set.

        -

        isLatin1 checks whether a character falls into the first 256 characters of Unicode.

        -

        isAsciiUpper checks whether a character is ASCII and upper-case.

        -

        isAsciiLower checks whether a character is ASCII and lower-case.

        -

        All these predicates have a type signature of Char -> Bool. Most of the time you’ll use this to filter out strings or something like that. For instance, let’s say we’re making a program that takes a username and the username can only be comprised of alphanumeric characters. We can use the Data.List function all in combination with the Data.Char predicates to determine if the username is alright.

        -
        
        -ghci> all isAlphaNum "bobby283"
        +lego char
        +

        The Data.Char module does what its name suggests. It +exports functions that deal with characters. It’s also helpful when +filtering and mapping over strings because they’re just lists of +characters.

        +

        Data.Char exports a bunch of predicates over characters. +That is, functions that take a character and tell us whether some +assumption about it is true or false. Here’s what they are:

        +

        isControl checks whether a +character is a control character.

        +

        isSpace checks whether a +character is a white-space characters. That includes spaces, tab +characters, newlines, etc.

        +

        isLower checks whether a +character is lower-cased.

        +

        isUpper checks whether a +character is upper-cased.

        +

        isAlpha checks whether a +character is a letter.

        +

        isAlphaNum checks whether a +character is a letter or a number.

        +

        isPrint checks whether a +character is printable. Control characters, for instance, are not +printable.

        +

        isDigit checks whether a +character is a digit.

        +

        isOctDigit checks whether a +character is an octal digit.

        +

        isHexDigit checks whether a +character is a hex digit.

        +

        isLetter checks whether a +character is a letter.

        +

        isMark checks for Unicode mark +characters. Those are characters that combine with preceding letters to +form letters with accents. Use this if you are French.

        +

        isNumber checks whether a +character is numeric.

        +

        isPunctuation checks whether a +character is punctuation.

        +

        isSymbol checks whether a +character is a fancy mathematical or currency symbol.

        +

        isSeparator checks for Unicode +spaces and separators.

        +

        isAscii checks whether a +character falls into the first 128 characters of the Unicode character +set.

        +

        isLatin1 checks whether a +character falls into the first 256 characters of Unicode.

        +

        isAsciiUpper checks whether a +character is ASCII and upper-case.

        +

        isAsciiLower checks whether a +character is ASCII and lower-case.

        +

        All these predicates have a type signature of +Char -> Bool. Most of the time you’ll use this to filter +out strings or something like that. For instance, let’s say we’re making +a program that takes a username and the username can only be comprised +of alphanumeric characters. We can use the Data.List +function all in combination with the Data.Char +predicates to determine if the username is alright.

        +
        ghci> all isAlphaNum "bobby283"
         True
         ghci> all isAlphaNum "eddy the fish!"
        -False
        -
        -

        Kewl. In case you don’t remember, all takes a predicate and a list and returns True only if that predicate holds for every element in the list.

        -

        We can also use isSpace to simulate the Data.List function words.

        -
        
        -ghci> words "hey folks its me"
        +False
        +

        Kewl. In case you don’t remember, all takes a predicate +and a list and returns True only if that predicate holds +for every element in the list.

        +

        We can also use isSpace to simulate the +Data.List function words.

        +
        ghci> words "hey folks its me"
         ["hey","folks","its","me"]
         ghci> groupBy ((==) `on` isSpace) "hey folks its me"
         ["hey"," ","folks"," ","its"," ","me"]
        -ghci>
        -
        -

        Hmmm, well, it kind of does what words does but we’re left with elements of only spaces. Hmm, whatever shall we do? I know, let’s filter that sucker.

        -
        
        -ghci> filter (not . any isSpace) . groupBy ((==) `on` isSpace) $ "hey folks its me"
        -["hey","folks","its","me"]
        -
        +ghci>
        +

        Hmmm, well, it kind of does what words does but we’re +left with elements of only spaces. Hmm, whatever shall we do? I know, +let’s filter that sucker.

        +
        ghci> filter (not . any isSpace) . groupBy ((==) `on` isSpace) $ "hey folks its me"
        +["hey","folks","its","me"]

        Ah.

        -

        The Data.Char also exports a datatype that’s kind of like Ordering. The Ordering type can have a value of LT, EQ or GT. It’s a sort of enumeration. It describes a few possible results that can arise from comparing two elements. The GeneralCategory type is also an enumeration. It presents us with a few possible categories that a character can fall into. The main function for getting the general category of a character is generalCategory. It has a type of generalCategory :: Char -> GeneralCategory. There are about 31 categories so we won’t list them all here, but let’s play around with the function.

        -
        
        -ghci> generalCategory ' '
        +

        The Data.Char also exports a datatype that’s kind of +like Ordering. The Ordering type can have a +value of LT, EQ or GT. It’s a +sort of enumeration. It describes a few possible results that can arise +from comparing two elements. The GeneralCategory type is +also an enumeration. It presents us with a few possible categories that +a character can fall into. The main function for getting the general +category of a character is generalCategory. It has a type +of generalCategory :: Char -> GeneralCategory. There are +about 31 categories so we won’t list them all here, but let’s play +around with the function.

        +
        ghci> generalCategory ' '
         Space
         ghci> generalCategory 'A'
         UppercaseLetter
        @@ -467,197 +740,253 @@ 

        Data.Char

        ghci> generalCategory '9' DecimalNumber ghci> map generalCategory " \t\nA9?|" -[Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol] -
        -

        Since the GeneralCategory type is part of the Eq typeclass, we can also test for stuff like generalCategory c == Space.

        -

        toUpper converts a character to upper-case. Spaces, numbers, and the like remain unchanged.

        -

        toLower converts a character to lower-case.

        -

        toTitle converts a character to title-case. For most characters, title-case is the same as upper-case.

        -

        digitToInt converts a character to an Int. To succeed, the character must be in the ranges '0'..'9', 'a'..'f' or 'A'..'F'.

        -
        
        -ghci> map digitToInt "34538"
        +[Space,Control,Control,UppercaseLetter,DecimalNumber,OtherPunctuation,MathSymbol]
        +

        Since the GeneralCategory type is part of the +Eq typeclass, we can also test for stuff like +generalCategory c == Space.

        +

        toUpper converts a character to +upper-case. Spaces, numbers, and the like remain unchanged.

        +

        toLower converts a character to +lower-case.

        +

        toTitle converts a character to +title-case. For most characters, title-case is the same as +upper-case.

        +

        digitToInt converts a character +to an Int. To succeed, the character must be in the ranges +'0'..'9', 'a'..'f' or +'A'..'F'.

        +
        ghci> map digitToInt "34538"
         [3,4,5,3,8]
         ghci> map digitToInt "FF85AB"
        -[15,15,8,5,10,11]
        -
        -

        intToDigit is the inverse function of digitToInt. It takes an Int in the range of 0..15 and converts it to a lower-case character.

        -
        
        -ghci> intToDigit 15
        +[15,15,8,5,10,11]
        +

        intToDigit is the inverse +function of digitToInt. It takes an Int in the +range of 0..15 and converts it to a lower-case +character.

        +
        ghci> intToDigit 15
         'f'
         ghci> intToDigit 5
        -'5'
        -
        -

        The ord and chr functions convert characters to their corresponding numbers and vice versa:

        -
        
        -ghci> ord 'a'
        +'5'
        +

        The ord and chr +functions convert characters to their corresponding numbers and vice +versa:

        +
        ghci> ord 'a'
         97
         ghci> chr 97
         'a'
         ghci> map ord "abcdefgh"
        -[97,98,99,100,101,102,103,104]
        -
        -

        The difference between the ord values of two characters is equal to how far apart they are in the Unicode table.

        The Caesar cipher is a primitive method of encoding messages by shifting each character in them by a fixed number of positions in the alphabet. We can easily create a sort of Caesar cipher of our own, only we won’t constrict ourselves to the alphabet.

        -
        
        -encode :: Int -> String -> String
        +[97,98,99,100,101,102,103,104]
        +

        The difference between the ord values of two characters +is equal to how far apart they are in the Unicode table.

        +

        The Caesar cipher is a primitive method of encoding messages by +shifting each character in them by a fixed number of positions in the +alphabet. We can easily create a sort of Caesar cipher of our own, only +we won’t constrict ourselves to the alphabet.

        +
        encode :: Int -> String -> String
         encode shift msg =
             let ords = map ord msg
                 shifted = map (+ shift) ords
        -    in  map chr shifted
        -
        -

        Here, we first convert the string to a list of numbers. Then we add the shift amount to each number before converting the list of numbers back to characters. If you’re a composition cowboy, you could write the body of this function as map (chr . (+ shift) . ord) msg. Let’s try encoding a few messages.

        -
        
        -ghci> encode 3 "Heeeeey"
        +    in  map chr shifted
        +

        Here, we first convert the string to a list of numbers. Then we add +the shift amount to each number before converting the list of numbers +back to characters. If you’re a composition cowboy, you could write the +body of this function as map (chr . (+ shift) . ord) msg. +Let’s try encoding a few messages.

        +
        ghci> encode 3 "Heeeeey"
         "Khhhhh|"
         ghci> encode 4 "Heeeeey"
         "Liiiii}"
         ghci> encode 1 "abcd"
         "bcde"
         ghci> encode 5 "Marry Christmas! Ho ho ho!"
        -"Rfww~%Hmwnxyrfx&%Mt%mt%mt&"
        -
        -

        That’s encoded alright. Decoding a message is basically just shifting it back by the number of places it was shifted by in the first place.

        -
        
        -decode :: Int -> String -> String
        -decode shift msg = encode (negate shift) msg
        -
        -
        
        -ghci> encode 3 "Im a little teapot"
        +"Rfww~%Hmwnxyrfx&%Mt%mt%mt&"
        +

        That’s encoded alright. Decoding a message is basically just shifting +it back by the number of places it was shifted by in the first +place.

        +
        decode :: Int -> String -> String
        +decode shift msg = encode (negate shift) msg
        +
        ghci> encode 3 "Im a little teapot"
         "Lp#d#olwwoh#whdsrw"
         ghci> decode 3 "Lp#d#olwwoh#whdsrw"
         "Im a little teapot"
         ghci> decode 5 . encode 5 $ "This is a sentence"
        -"This is a sentence"
        -
        +"This is a sentence"

        Data.Map

        -

        Association lists (also called dictionaries) are lists that are used to store key-value pairs where ordering doesn’t matter. For instance, we might use an association list to store phone numbers, where phone numbers would be the values and people’s names would be the keys. We don’t care in which order they’re stored, we just want to get the right phone number for the right person.

        -

        The most obvious way to represent association lists in Haskell would be by having a list of pairs. The first component in the pair would be the key, the second component the value. Here’s an example of an association list with phone numbers:

        -
        
        -phoneBook =
        +

        Association lists (also called dictionaries) are lists that are used +to store key-value pairs where ordering doesn’t matter. For instance, we +might use an association list to store phone numbers, where phone +numbers would be the values and people’s names would be the keys. We +don’t care in which order they’re stored, we just want to get the right +phone number for the right person.

        +

        The most obvious way to represent association lists in Haskell would +be by having a list of pairs. The first component in the pair would be +the key, the second component the value. Here’s an example of an +association list with phone numbers:

        +
        phoneBook =
             [("amelia","555-2938")
             ,("freya","452-2928")
             ,("isabella","493-2928")
             ,("neil","205-2928")
             ,("roald","939-8282")
             ,("tenzing","853-2492")
        -    ]
        -
        -

        Despite this seemingly odd indentation, this is just a list of pairs of strings. The most common task when dealing with association lists is looking up some value by key. Let’s make a function that looks up some value given a key.

        -
        
        -findKey :: (Eq k) => k -> [(k,v)] -> v
        -findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs
        -
        -

        Pretty simple. The function that takes a key and a list, filters the list so that only matching keys remain, gets the first key-value that matches and returns the value. But what happens if the key we’re looking for isn’t in the association list? Hmm. Here, if a key isn’t in the association list, we’ll end up trying to get the head of an empty list, which throws a runtime error. However, we should avoid making our programs so easy to crash, so let’s use the Maybe data type. If we don’t find the key, we’ll return a Nothing. If we find it, we’ll return Just something, where something is the value corresponding to that key.

        -
        
        -findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
        +    ]
        +

        Despite this seemingly odd indentation, this is just a list of pairs +of strings. The most common task when dealing with association lists is +looking up some value by key. Let’s make a function that looks up some +value given a key.

        +
        findKey :: (Eq k) => k -> [(k,v)] -> v
        +findKey key xs = snd . head . filter (\(k,v) -> key == k) $ xs
        +

        Pretty simple. The function that takes a key and a list, filters the +list so that only matching keys remain, gets the first key-value that +matches and returns the value. But what happens if the key we’re looking +for isn’t in the association list? Hmm. Here, if a key isn’t in the +association list, we’ll end up trying to get the head of an empty list, +which throws a runtime error. However, we should avoid making our +programs so easy to crash, so let’s use the Maybe data +type. If we don’t find the key, we’ll return a Nothing. If +we find it, we’ll return Just something, where something is +the value corresponding to that key.

        +
        findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
         findKey key [] = Nothing
         findKey key ((k,v):xs) = if key == k
                                     then Just v
        -                            else findKey key xs
        -
        -

        Look at the type declaration. It takes a key that can be equated, an association list and then it maybe produces a value. Sounds about right.

        -

        This is a textbook recursive function that operates on a list. Edge case, splitting a list into a head and a tail, recursive calls, they’re all there. This is the classic fold pattern, so let’s see how this would be implemented as a fold.

        -
        
        -findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
        -findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
        -
        -

        Note: It’s usually better to use folds for this standard list recursion pattern instead of explicitly writing the recursion because they’re easier to read and identify. Everyone knows it’s a fold when they see the foldr call, but it takes some more thinking to read explicit recursion.

        -
        
        -ghci> findKey "tenzing" phoneBook
        +                            else findKey key xs
        +

        Look at the type declaration. It takes a key that can be equated, an +association list and then it maybe produces a value. Sounds about +right.

        +

        This is a textbook recursive function that operates on a list. Edge +case, splitting a list into a head and a tail, recursive calls, they’re +all there. This is the classic fold pattern, so let’s see how this would +be implemented as a fold.

        +
        findKey :: (Eq k) => k -> [(k,v)] -> Maybe v
        +findKey key = foldr (\(k,v) acc -> if key == k then Just v else acc) Nothing
        +
        +

        Note: It’s usually better to use folds for this +standard list recursion pattern instead of explicitly writing the +recursion because they’re easier to read and identify. Everyone knows +it’s a fold when they see the foldr call, but it takes some +more thinking to read explicit recursion.

        +
        +
        ghci> findKey "tenzing" phoneBook
         Just "853-2492"
         ghci> findKey "amelia" phoneBook
         Just "555-2938"
         ghci> findKey "christopher" phoneBook
        -Nothing
        -
        -legomap -

        Works like a charm! If we have the friend’s phone number, we Just get the number, otherwise we get Nothing.

        -

        We just implemented the lookup function from Data.List. If we want to find the corresponding value to a key, we have to traverse all the elements of the list until we find it. The Data.Map module offers association lists that are much faster (because they’re internally implemented with trees) and also it provides a lot of utility functions. From now on, we’ll say we’re working with maps instead of association lists.

        -

        Because Data.Map exports functions that clash with the Prelude and Data.List ones, we’ll do a qualified import.

        -
        
        -import qualified Data.Map as Map
        -
        -

        Put this import statement into a script and then load the script via GHCI.

        -

        Let’s go ahead and see what Data.Map has in store for us! Here’s the basic rundown of its functions.

        -

        The fromList function takes an association list (in the form of a list) and returns a map with the same associations.

        -
        
        -ghci> Map.fromList [("amelia","555-2938"),("freya","452-2928"),("neil","205-2928")]
        +Nothing
        +legomap +

        Works like a charm! If we have the friend’s phone number, we +Just get the number, otherwise we get +Nothing.

        +

        We just implemented the lookup function from +Data.List. If we want to find the corresponding value to a +key, we have to traverse all the elements of the list until we find it. +The Data.Map module offers association lists that are much +faster (because they’re internally implemented with trees) and also it +provides a lot of utility functions. From now on, we’ll say we’re +working with maps instead of association lists.

        +

        Because Data.Map exports functions that clash with the +Prelude and Data.List ones, we’ll do a +qualified import.

        +
        import qualified Data.Map as Map
        +

        Put this import statement into a script and then load the script via +GHCI.

        +

        Let’s go ahead and see what Data.Map has in store for +us! Here’s the basic rundown of its functions.

        +

        The fromList function takes an +association list (in the form of a list) and returns a map with the same +associations.

        +
        ghci> Map.fromList [("amelia","555-2938"),("freya","452-2928"),("neil","205-2928")]
         fromList [("amelia","555-2938"),("freya","452-2928"),("neil","205-2928")]
         ghci> Map.fromList [(1,2),(3,4),(3,2),(5,5)]
        -fromList [(1,2),(3,2),(5,5)]
        -
        -

        If there are duplicate keys in the original association list, the duplicates are just discarded. This is the type signature of fromList

        -
        
        -Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v
        -
        -

        It says that it takes a list of pairs of type k and v and returns a map that maps from keys of type k to type v. Notice that when we were doing association lists with normal lists, the keys only had to be equatable (their type belonging to the Eq typeclass) but now they have to be orderable. That’s an essential constraint in the Data.Map module. It needs the keys to be orderable so it can arrange them in a tree.

        -

        You should always use Data.Map for key-value associations unless you have keys that aren’t part of the Ord typeclass.

        -

        empty represents an empty map. It takes no arguments, it just returns an empty map.

        -
        
        -ghci> Map.empty
        -fromList []
        -
        -

        insert takes a key, a value and a map and returns a new map that’s just like the old one, only with the key and value inserted.

        -
        
        -ghci> Map.empty
        +fromList [(1,2),(3,2),(5,5)]
        +

        If there are duplicate keys in the original association list, the +duplicates are just discarded. This is the type signature of +fromList

        +
        Map.fromList :: (Ord k) => [(k, v)] -> Map.Map k v
        +

        It says that it takes a list of pairs of type k and +v and returns a map that maps from keys of type +k to type v. Notice that when we were doing +association lists with normal lists, the keys only had to be equatable +(their type belonging to the Eq typeclass) but now they +have to be orderable. That’s an essential constraint in the +Data.Map module. It needs the keys to be orderable so it +can arrange them in a tree.

        +

        You should always use Data.Map for key-value +associations unless you have keys that aren’t part of the +Ord typeclass.

        +

        empty represents an empty map. It +takes no arguments, it just returns an empty map.

        +
        ghci> Map.empty
        +fromList []
        +

        insert takes a key, a value and a +map and returns a new map that’s just like the old one, only with the +key and value inserted.

        +
        ghci> Map.empty
         fromList []
         ghci> Map.insert 3 100 Map.empty
         fromList [(3,100)]
         ghci> Map.insert 5 600 (Map.insert 4 200 ( Map.insert 3 100  Map.empty))
         fromList [(3,100),(4,200),(5,600)]
         ghci> Map.insert 5 600 . Map.insert 4 200 . Map.insert 3 100 $ Map.empty
        -fromList [(3,100),(4,200),(5,600)]
        -
        -

        We can implement our own fromList by using the empty map, insert and a fold. Watch:

        -
        
        -fromList' :: (Ord k) => [(k,v)] -> Map.Map k v
        -fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
        -
        -

        It’s a pretty straightforward fold. We start of with an empty map and we fold it up from the right, inserting the key value pairs into the accumulator as we go along.

        -

        null checks if a map is empty.

        -
        
        -ghci> Map.null Map.empty
        +fromList [(3,100),(4,200),(5,600)]
        +

        We can implement our own fromList by using the empty +map, insert and a fold. Watch:

        +
        fromList' :: (Ord k) => [(k,v)] -> Map.Map k v
        +fromList' = foldr (\(k,v) acc -> Map.insert k v acc) Map.empty
        +

        It’s a pretty straightforward fold. We start of with an empty map and +we fold it up from the right, inserting the key value pairs into the +accumulator as we go along.

        +

        null checks if a map is +empty.

        +
        ghci> Map.null Map.empty
         True
         ghci> Map.null $ Map.fromList [(2,3),(5,5)]
        -False
        -
        -

        size reports the size of a map.

        -
        
        -ghci> Map.size Map.empty
        +False
        +

        size reports the size of a +map.

        +
        ghci> Map.size Map.empty
         0
         ghci> Map.size $ Map.fromList [(2,4),(3,3),(4,2),(5,4),(6,4)]
        -5
        -
        -

        singleton takes a key and a value and creates a map that has exactly one mapping.

        -
        
        -ghci> Map.singleton 3 9
        +5
        +

        singleton takes a key and a value +and creates a map that has exactly one mapping.

        +
        ghci> Map.singleton 3 9
         fromList [(3,9)]
         ghci> Map.insert 5 9 $ Map.singleton 3 9
        -fromList [(3,9),(5,9)]
        -
        -

        lookup works like the Data.List lookup, only it operates on maps. It returns Just something if it finds something for the key and Nothing if it doesn’t.

        -

        member is a predicate that takes a key and a map and reports whether the key is in the map or not.

        -
        
        -ghci> Map.member 3 $ Map.fromList [(3,6),(4,3),(6,9)]
        +fromList [(3,9),(5,9)]
        +

        lookup works like the +Data.List lookup, only it operates on maps. It +returns Just something if it finds something for the key +and Nothing if it doesn’t.

        +

        member is a predicate that takes +a key and a map and reports whether the key is in the map or not.

        +
        ghci> Map.member 3 $ Map.fromList [(3,6),(4,3),(6,9)]
         True
         ghci> Map.member 3 $ Map.fromList [(2,5),(4,5)]
        -False
        -
        -

        map and filter work much like their list equivalents.

        -
        
        -ghci> Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]
        +False
        +

        map and filter work much like their list +equivalents.

        +
        ghci> Map.map (*100) $ Map.fromList [(1,1),(2,4),(3,9)]
         fromList [(1,100),(2,400),(3,900)]
         ghci> Map.filter isUpper $ Map.fromList [(1,'a'),(2,'A'),(3,'b'),(4,'B')]
        -fromList [(2,'A'),(4,'B')]
        -
        -

        toList is the inverse of fromList.

        -
        
        -ghci> Map.toList . Map.insert 9 2 $ Map.singleton 4 3
        -[(4,3),(9,2)]
        -
        -

        keys and elems return lists of keys and values respectively. keys is the equivalent of map fst . Map.toList and elems is the equivalent of map snd . Map.toList.

        -

        fromListWith is a cool little function. It acts like fromList, only it doesn’t discard duplicate keys but it uses a function supplied to it to decide what to do with them. Let’s say that a friend can have several numbers and we have an association list set up like this.

        -
        
        -phoneBook =
        +fromList [(2,'A'),(4,'B')]
        +

        toList is the inverse of +fromList.

        +
        ghci> Map.toList . Map.insert 9 2 $ Map.singleton 4 3
        +[(4,3),(9,2)]
        +

        keys and elems return lists of keys and values +respectively. keys is the equivalent of +map fst . Map.toList and elems is the +equivalent of map snd . Map.toList.

        +

        fromListWith is a cool little +function. It acts like fromList, only it doesn’t discard +duplicate keys but it uses a function supplied to it to decide what to +do with them. Let’s say that a friend can have several numbers and we +have an association list set up like this.

        +
        phoneBook =
             [("amelia","555-2938")
             ,("amelia","342-2492")
             ,("freya","452-2928")
        @@ -668,90 +997,97 @@ 

        Data.Map

        ,("roald","939-8282") ,("tenzing","853-2492") ,("tenzing","555-2111") - ] -
        -

        Now if we just use fromList to put that into a map, we’ll lose a few numbers! So here’s what we’ll do:

        -
        
        -phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String
        -phoneBookToMap xs = Map.fromListWith (\number1 number2 -> number1 ++ ", " ++ number2) xs
        -
        -
        
        -ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook
        +    ]
        +

        Now if we just use fromList to put that into a map, +we’ll lose a few numbers! So here’s what we’ll do:

        +
        phoneBookToMap :: (Ord k) => [(k, String)] -> Map.Map k String
        +phoneBookToMap xs = Map.fromListWith (\number1 number2 -> number1 ++ ", " ++ number2) xs
        +
        ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook
         "827-9162, 943-2929, 493-2928"
         ghci> Map.lookup "roald" $ phoneBookToMap phoneBook
         "939-8282"
         ghci> Map.lookup "amelia" $ phoneBookToMap phoneBook
        -"342-2492, 555-2938"
        -
        -

        If a duplicate key is found, the function we pass is used to combine the values of those keys into some other value. We could also first make all the values in the association list singleton lists and then we can use ++ to combine the numbers.

        -
        
        -phoneBookToMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
        -phoneBookToMap xs = Map.fromListWith (++) $ map (\(k,v) -> (k,[v])) xs
        -
        -
        
        -ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook
        -["827-9162","943-2929","493-2928"]
        -
        -

        Pretty neat! Another use case is if we’re making a map from an association list of numbers and when a duplicate key is found, we want the biggest value for the key to be kept.

        -
        
        -ghci> Map.fromListWith max [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
        -fromList [(2,100),(3,29),(4,22)]
        -
        +"342-2492, 555-2938"
        +

        If a duplicate key is found, the function we pass is used to combine +the values of those keys into some other value. We could also first make +all the values in the association list singleton lists and then we can +use ++ to combine the numbers.

        +
        phoneBookToMap :: (Ord k) => [(k, a)] -> Map.Map k [a]
        +phoneBookToMap xs = Map.fromListWith (++) $ map (\(k,v) -> (k,[v])) xs
        +
        ghci> Map.lookup "isabella" $ phoneBookToMap phoneBook
        +["827-9162","943-2929","493-2928"]
        +

        Pretty neat! Another use case is if we’re making a map from an +association list of numbers and when a duplicate key is found, we want +the biggest value for the key to be kept.

        +
        ghci> Map.fromListWith max [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
        +fromList [(2,100),(3,29),(4,22)]

        Or we could choose to add together values on the same keys.

        -
        
        -ghci> Map.fromListWith (+) [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
        -fromList [(2,108),(3,62),(4,37)]
        -
        -

        insertWith is to insert what fromListWith is to fromList. It inserts a key-value pair into a map, but if that map already contains the key, it uses the function passed to it to determine what to do.

        -
        
        -ghci> Map.insertWith (+) 3 100 $ Map.fromList [(3,4),(5,103),(6,339)]
        -fromList [(3,104),(5,103),(6,339)]
        -
        -

        These were just a few functions from Data.Map. You can see a complete list of functions in the documentation.

        +
        ghci> Map.fromListWith (+) [(2,3),(2,5),(2,100),(3,29),(3,22),(3,11),(4,22),(4,15)]
        +fromList [(2,108),(3,62),(4,37)]
        +

        insertWith is to +insert what fromListWith is to +fromList. It inserts a key-value pair into a map, but if +that map already contains the key, it uses the function passed to it to +determine what to do.

        +
        ghci> Map.insertWith (+) 3 100 $ Map.fromList [(3,4),(5,103),(6,339)]
        +fromList [(3,104),(5,103),(6,339)]
        +

        These were just a few functions from Data.Map. You can +see a complete list of functions in the documentation.

        Data.Set

        -legosets -

        The Data.Set module offers us, well, sets. Like sets from mathematics. Sets are kind of like a cross between lists and maps. All the elements in a set are unique. And because they’re internally implemented with trees (much like maps in Data.Map), they’re ordered. Checking for membership, inserting, deleting, etc. is much faster than doing the same thing with lists. The most common operation when dealing with sets are inserting into a set, checking for membership and converting a set to a list.

        -

        Because the names in Data.Set clash with a lot of Prelude and Data.List names, we do a qualified import.

        +legosets +

        The Data.Set module offers us, well, sets. Like sets +from mathematics. Sets are kind of like a cross between lists and maps. +All the elements in a set are unique. And because they’re internally +implemented with trees (much like maps in Data.Map), +they’re ordered. Checking for membership, inserting, deleting, etc. is +much faster than doing the same thing with lists. The most common +operation when dealing with sets are inserting into a set, checking for +membership and converting a set to a list.

        +

        Because the names in Data.Set clash with a lot of +Prelude and Data.List names, we do a qualified +import.

        Put this import statement in a script:

        -
        
        -import qualified Data.Set as Set
        -
        +
        import qualified Data.Set as Set

        And then load the script via GHCI.

        -

        Let’s say we have two pieces of text. We want to find out which characters were used in both of them.

        -
        
        -text1 = "I just had an anime dream. Anime... Reality... Are they so different?"
        -text2 = "The old man left his garbage can out and now his trash is all over my lawn!"
        -
        -

        -The fromList function works much like you would expect. It takes a list and converts it into a set.

        -
        
        -ghci> let set1 = Set.fromList text1
        +

        Let’s say we have two pieces of text. We want to find out which +characters were used in both of them.

        +
        text1 = "I just had an anime dream. Anime... Reality... Are they so different?"
        +text2 = "The old man left his garbage can out and now his trash is all over my lawn!"
        +

        The fromList function works much +like you would expect. It takes a list and converts it into a set.

        +
        ghci> let set1 = Set.fromList text1
         ghci> let set2 = Set.fromList text2
         ghci> set1
         fromList " .?AIRadefhijlmnorstuy"
         ghci> set2
        -fromList " !Tabcdefghilmnorstuvwy"
        -
        -

        As you can see, the items are ordered and each element is unique. Now let’s use the intersection function to see which elements they both share.

        -
        
        -ghci> Set.intersection set1 set2
        -fromList " adefhilmnorstuy"
        -
        -

        We can use the difference function to see which letters are in the first set but aren’t in the second one and vice versa.

        -
        
        -ghci> Set.difference set1 set2
        +fromList " !Tabcdefghilmnorstuvwy"
        +

        As you can see, the items are ordered and each element is unique. Now +let’s use the intersection function +to see which elements they both share.

        +
        ghci> Set.intersection set1 set2
        +fromList " adefhilmnorstuy"
        +

        We can use the difference +function to see which letters are in the first set but aren’t in the +second one and vice versa.

        +
        ghci> Set.difference set1 set2
         fromList ".?AIRj"
         ghci> Set.difference set2 set1
        -fromList "!Tbcgvw"
        -
        -

        Or we can see all the unique letters used in both sentences by using union.

        -
        
        -ghci> Set.union set1 set2
        -fromList " !.?AIRTabcdefghijlmnorstuvwy"
        -
        -

        The null, size, member, empty, singleton, insert and delete functions all work like you’d expect them to.

        -
        
        -ghci> Set.null Set.empty
        +fromList "!Tbcgvw"
        +

        Or we can see all the unique letters used in both sentences by using +union.

        +
        ghci> Set.union set1 set2
        +fromList " !.?AIRTabcdefghijlmnorstuvwy"
        +

        The null, size, member, empty, singleton, insert and delete functions all work like you’d +expect them to.

        +
        ghci> Set.null Set.empty
         True
         ghci> Set.null $ Set.fromList [3,4,5,5,4,3]
         False
        @@ -764,54 +1100,76 @@ 

        Data.Set

        ghci> Set.insert 8 $ Set.fromList [5..10] fromList [5,6,7,8,9,10] ghci> Set.delete 4 $ Set.fromList [3,4,5,4,3,4,5] -fromList [3,5] -
        -

        We can also check for subsets or proper subset. Set A is a subset of set B if B contains all the elements that A does. Set A is a proper subset of set B if B contains all the elements that A does but has more elements.

        -
        
        -ghci> Set.fromList [2,3,4] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]
        +fromList [3,5]
        +

        We can also check for subsets or proper subset. Set A is a subset of +set B if B contains all the elements that A does. Set A is a proper +subset of set B if B contains all the elements that A does but has more +elements.

        +
        ghci> Set.fromList [2,3,4] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]
         True
         ghci> Set.fromList [1,2,3,4,5] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]
         True
         ghci> Set.fromList [1,2,3,4,5] `Set.isProperSubsetOf` Set.fromList [1,2,3,4,5]
         False
         ghci> Set.fromList [2,3,4,8] `Set.isSubsetOf` Set.fromList [1,2,3,4,5]
        -False
        -
        -

        We can also map over sets and filter them.

        -
        
        -ghci> Set.filter odd $ Set.fromList [3,4,5,6,7,2,3,4]
        +False
        +

        We can also map over sets and +filter them.

        +
        ghci> Set.filter odd $ Set.fromList [3,4,5,6,7,2,3,4]
         fromList [3,5,7]
         ghci> Set.map (+1) $ Set.fromList [3,4,5,6,7,2,3,4]
        -fromList [3,4,5,6,7,8]
        -
        -

        Sets are often used to weed a list of duplicates from a list by first making it into a set with fromList and then converting it back to a list with toList. The Data.List function nub already does that, but weeding out duplicates for large lists is much faster if you cram them into a set and then convert them back to a list than using nub. But using nub only requires the type of the list’s elements to be part of the Eq typeclass, whereas if you want to cram elements into a set, the type of the list has to be in Ord.

        -
        
        -ghci> let setNub xs = Set.toList $ Set.fromList xs
        +fromList [3,4,5,6,7,8]
        +

        Sets are often used to weed a list of duplicates from a list by first +making it into a set with fromList and then converting it +back to a list with toList. The +Data.List function nub already does that, but +weeding out duplicates for large lists is much faster if you cram them +into a set and then convert them back to a list than using +nub. But using nub only requires the type of +the list’s elements to be part of the Eq typeclass, whereas +if you want to cram elements into a set, the type of the list has to be +in Ord.

        +
        ghci> let setNub xs = Set.toList $ Set.fromList xs
         ghci> setNub "HEY WHATS CRACKALACKIN"
         " ACEHIKLNRSTWY"
         ghci> nub "HEY WHATS CRACKALACKIN"
        -"HEY WATSCRKLIN"
        -
        -

        setNub is generally faster than nub on big lists but as you can see, nub preserves the ordering of the list’s elements, while setNub does not.

        +"HEY WATSCRKLIN"
        +

        setNub is generally faster than nub on big +lists but as you can see, nub preserves the ordering of the +list’s elements, while setNub does not.

        Making our own modules

        -making modules -

        We’ve looked at some cool modules so far, but how do we make our own module? Almost every programming language enables you to split your code up into several files and Haskell is no different. When making programs, it’s good practice to take functions and types that work towards a similar purpose and put them in a module. That way, you can easily reuse those functions in other programs by just importing your module.

        -

        Let’s see how we can make our own modules by making a little module that provides some functions for calculating the volume and area of a few geometrical objects. We’ll start by creating a file called Geometry.hs.

        -

        We say that a module exports functions. What that means is that when I import a module, I can use the functions that it exports. It can define functions that its functions call internally, but we can only see and use the ones that it exports.

        -

        At the beginning of a module, we specify the module name. If we have a file called Geometry.hs, then we should name our module Geometry. Then, we specify the functions that it exports and after that, we can start writing the functions. So we’ll start with this.

        -
        
        -module Geometry
        +making modules
        +

        We’ve looked at some cool modules so far, but how do we make our own +module? Almost every programming language enables you to split your code +up into several files and Haskell is no different. When making programs, +it’s good practice to take functions and types that work towards a +similar purpose and put them in a module. That way, you can easily reuse +those functions in other programs by just importing your module.

        +

        Let’s see how we can make our own modules by making a little module +that provides some functions for calculating the volume and area of a +few geometrical objects. We’ll start by creating a file called +Geometry.hs.

        +

        We say that a module exports functions. What that means is +that when I import a module, I can use the functions that it exports. It +can define functions that its functions call internally, but we can only +see and use the ones that it exports.

        +

        At the beginning of a module, we specify the module name. If we have +a file called Geometry.hs, then we should name our module +Geometry. Then, we specify the functions that it exports +and after that, we can start writing the functions. So we’ll start with +this.

        +
        module Geometry
         ( sphereVolume
         , sphereArea
         , cubeVolume
         , cubeArea
         , cuboidArea
         , cuboidVolume
        -) where
        -
        -

        As you can see, we’ll be doing areas and volumes for spheres, cubes and cuboids. Let’s go ahead and define our functions then:

        -
        
        -module Geometry
        +) where
        +

        As you can see, we’ll be doing areas and volumes for spheres, cubes +and cuboids. Let’s go ahead and define our functions then:

        +
        module Geometry
         ( sphereVolume
         , sphereArea
         , cubeVolume
        @@ -839,20 +1197,40 @@ 

        Making our own modules

        cuboidArea a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float -rectangleArea a b = a * b -
        -

        Pretty standard geometry right here. There are a few things to take note of though. Because a cube is only a special case of a cuboid, we defined its area and volume by treating it as a cuboid whose sides are all of the same length. We also defined a helper function called rectangleArea, which calculates a rectangle’s area based on the lenghts of its sides. It’s rather trivial because it’s just multiplication. Notice that we used it in our functions in the module (namely cuboidArea and cuboidVolume) but we didn’t export it! Because we want our module to just present functions for dealing with three-dimensional objects, we used rectangleArea but we didn’t export it.

        -

        When making a module, we usually export only those functions that act as a sort of interface to our module so that the implementation is hidden. If someone is using our Geometry module, they don’t have to concern themselves with functions that we don’t export. We can decide to change those functions completely or delete them in a newer version (we could delete rectangleArea and just use * instead) and no one will mind because we weren’t exporting them in the first place.

        +rectangleArea a b = a * b
        +

        Pretty standard geometry right here. There are a few things to take +note of though. Because a cube is only a special case of a cuboid, we +defined its area and volume by treating it as a cuboid whose sides are +all of the same length. We also defined a helper function called +rectangleArea, which calculates a rectangle’s area based on +the lenghts of its sides. It’s rather trivial because it’s just +multiplication. Notice that we used it in our functions in the module +(namely cuboidArea and cuboidVolume) but we +didn’t export it! Because we want our module to just present functions +for dealing with three-dimensional objects, we used +rectangleArea but we didn’t export it.

        +

        When making a module, we usually export only those functions that act +as a sort of interface to our module so that the implementation is +hidden. If someone is using our Geometry module, they don’t +have to concern themselves with functions that we don’t export. We can +decide to change those functions completely or delete them in a newer +version (we could delete rectangleArea and just use +* instead) and no one will mind because we weren’t +exporting them in the first place.

        To use our module, we just do:

        -
        
        -import Geometry
        -
        -

        Geometry.hs has to be in the same folder that the program that’s importing it is in, though.

        -

        Modules can also have a hierarchical structure. Each module can have a number of submodules and they can have submodules of their own. Let’s section these functions off so that Geometry is a module that has three submodules, one for each type of object.

        -

        First, we’ll make a folder called Geometry. Mind the capital G. In it, we’ll place three files: Sphere.hs, Cuboid.hs, and Cube.hs. Here’s what the files will contain:

        +
        import Geometry
        +

        Geometry.hs has to be in the same folder that the +program that’s importing it is in, though.

        +

        Modules can also have a hierarchical structure. Each module can have +a number of submodules and they can have submodules of their own. Let’s +section these functions off so that Geometry is a module +that has three submodules, one for each type of object.

        +

        First, we’ll make a folder called Geometry. Mind the +capital G. In it, we’ll place three files: Sphere.hs, +Cuboid.hs, and Cube.hs. Here’s what the files +will contain:

        Sphere.hs

        -
        
        -module Geometry.Sphere
        +
        module Geometry.Sphere
         ( volume
         , area
         ) where
        @@ -861,11 +1239,9 @@ 

        Making our own modules

        volume radius = (4.0 / 3.0) * pi * (radius ^ 3) area :: Float -> Float -area radius = 4 * pi * (radius ^ 2) -
        +area radius = 4 * pi * (radius ^ 2)

        Cuboid.hs

        -
        
        -module Geometry.Cuboid
        +
        module Geometry.Cuboid
         ( volume
         , area
         ) where
        @@ -877,11 +1253,9 @@ 

        Making our own modules

        area a b c = rectangleArea a b * 2 + rectangleArea a c * 2 + rectangleArea c b * 2 rectangleArea :: Float -> Float -> Float -rectangleArea a b = a * b -
        +rectangleArea a b = a * b

        Cube.hs

        -
        
        -module Geometry.Cube
        +
        module Geometry.Cube
         ( volume
         , area
         ) where
        @@ -892,21 +1266,36 @@ 

        Making our own modules

        volume side = Cuboid.volume side side side area :: Float -> Float -area side = Cuboid.area side side side -
        -

        Alright! So first is Geometry.Sphere. Notice how we placed it in a folder called Geometry and then defined the module name as Geometry.Sphere. We did the same for the cuboid. Also notice how in all three submodules, we defined functions with the same names. We can do this because they’re separate modules. We want to use functions from Geometry.Cuboid in Geometry.Cube but we can’t just straight up do import Geometry.Cuboid because it exports functions with the same names as Geometry.Cube. That’s why we do a qualified import and all is well.

        -

        So now if we’re in a file that’s on the same level as the Geometry folder, we can do, say:

        -
        
        -import Geometry.Sphere
        -
        -

        And then we can call area and volume and they’ll give us the area and volume for a sphere. And if we want to juggle two or more of these modules, we have to do qualified imports because they export functions with the same names. So we just do something like:

        -
        
        -import qualified Geometry.Sphere as Sphere
        +area side = Cuboid.area side side side
        +

        Alright! So first is Geometry.Sphere. Notice how we +placed it in a folder called Geometry and then defined the +module name as Geometry.Sphere. We did the same for the +cuboid. Also notice how in all three submodules, we defined functions +with the same names. We can do this because they’re separate modules. We +want to use functions from Geometry.Cuboid in +Geometry.Cube but we can’t just straight up do +import Geometry.Cuboid because it exports functions with +the same names as Geometry.Cube. That’s why we do a +qualified import and all is well.

        +

        So now if we’re in a file that’s on the same level as the +Geometry folder, we can do, say:

        +
        import Geometry.Sphere
        +

        And then we can call area and volume and +they’ll give us the area and volume for a sphere. And if we want to +juggle two or more of these modules, we have to do qualified imports +because they export functions with the same names. So we just do +something like:

        +
        import qualified Geometry.Sphere as Sphere
         import qualified Geometry.Cuboid as Cuboid
        -import qualified Geometry.Cube as Cube
        -
        -

        And then we can call Sphere.area, Sphere.volume, Cuboid.area, etc. and each will calculate the area or volume for their corresponding object.

        -

        The next time you find yourself writing a file that’s really big and has a lot of functions, try to see which functions serve some common purpose and then see if you can put them in their own module. You’ll be able to just import your module the next time you’re writing a program that requires some of the same functionality.

        +import qualified Geometry.Cube as Cube
        +

        And then we can call Sphere.area, +Sphere.volume, Cuboid.area, etc. and each will +calculate the area or volume for their corresponding object.

        +

        The next time you find yourself writing a file that’s really big and +has a lot of functions, try to see which functions serve some common +purpose and then see if you can put them in their own module. You’ll be +able to just import your module the next time you’re writing a program +that requires some of the same functionality.

        • diff --git a/docs/recursion.html b/docs/recursion.html index 0882101..426ee35 100644 --- a/docs/recursion.html +++ b/docs/recursion.html @@ -31,115 +31,321 @@
        -

        Recursion

        +

        Recursion

        Hello recursion!

        -SOVIET RUSSIA -

        We mention recursion briefly in the previous chapter. In this chapter, we’ll take a closer look at recursion, why it’s important to Haskell and how we can work out very concise and elegant solutions to problems by thinking recursively.

        -

        If you still don’t know what recursion is, read this sentence. Haha! Just kidding! Recursion is actually a way of defining functions in which the function is applied inside its own definition. Definitions in mathematics are often given recursively. For instance, the fibonacci sequence is defined recursively. First, we define the first two fibonacci numbers non-recursively. We say that F(0) = 0 and F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 and 1, respectively. Then we say that for any other natural number, that fibonacci number is the sum of the previous two fibonacci numbers. So F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + F(1), which is (F(1) + F(0)) + F(1). Because we’ve now come down to only non-recursively defined fibonacci numbers, we can safely say that F(3) is 2. Having an element or two in a recursion definition defined non-recursively (like F(0) and F(1) here) is also called the edge condition and is important if you want your recursive function to terminate. If we hadn’t defined F(0) and F(1) non recursively, you’d never get a solution any number because you’d reach 0 and then you’d go into negative numbers. All of a sudden, you’d be saying that F(-2000) is F(-2001) + F(-2002) and there still wouldn’t be an end in sight!

        -

        Recursion is important to Haskell because unlike imperative languages, you do computations in Haskell by declaring what something is instead of declaring how you get it. That’s why there are no while loops or for loops in Haskell and instead we many times have to use recursion to declare what something is.

        +SOVIET RUSSIA +

        We mention recursion briefly in the previous chapter. In this +chapter, we’ll take a closer look at recursion, why it’s important to +Haskell and how we can work out very concise and elegant solutions to +problems by thinking recursively.

        +

        If you still don’t know what recursion is, read this sentence. Haha! +Just kidding! Recursion is actually a way of defining functions in which +the function is applied inside its own definition. Definitions in +mathematics are often given recursively. For instance, the fibonacci +sequence is defined recursively. First, we define the first two +fibonacci numbers non-recursively. We say that F(0) = 0 and +F(1) = 1, meaning that the 0th and 1st fibonacci numbers are 0 +and 1, respectively. Then we say that for any other natural number, that +fibonacci number is the sum of the previous two fibonacci numbers. So +F(n) = F(n-1) + F(n-2). That way, F(3) is F(2) + +F(1), which is (F(1) + F(0)) + F(1). Because we’ve now +come down to only non-recursively defined fibonacci numbers, we can +safely say that F(3) is 2. Having an element or two in a +recursion definition defined non-recursively (like F(0) and +F(1) here) is also called the edge condition +and is important if you want your recursive function to terminate. If we +hadn’t defined F(0) and F(1) non recursively, you’d +never get a solution any number because you’d reach 0 and then you’d go +into negative numbers. All of a sudden, you’d be saying that +F(-2000) is F(-2001) + F(-2002) and there still +wouldn’t be an end in sight!

        +

        Recursion is important to Haskell because unlike imperative +languages, you do computations in Haskell by declaring what something +is instead of declaring how you get it. That’s why +there are no while loops or for loops in Haskell and instead we many +times have to use recursion to declare what something is.

        Maximum awesome

        -

        The maximum function takes a list of things that can be ordered (e.g. instances of the Ord typeclass) and returns the biggest of them. Think about how you’d implement that in an imperative fashion. You’d probably set up a variable to hold the maximum value so far and then you’d loop through the elements of a list and if an element is bigger than then the current maximum value, you’d replace it with that element. The maximum value that remains at the end is the result. Whew! That’s quite a lot of words to describe such a simple algorithm!

        -

        Now let’s see how we’d define it recursively. We could first set up an edge condition and say that the maximum of a singleton list is equal to the only element in it. Then we can say that the maximum of a longer list is the head if the head is bigger than the maximum of the tail. If the maximum of the tail is bigger, well, then it’s the maximum of the tail. That’s it! Now let’s implement that in Haskell.

        -
        
        -maximum' :: (Ord a) => [a] -> a
        +

        The maximum function takes a list of things that can be +ordered (e.g. instances of the Ord typeclass) and returns +the biggest of them. Think about how you’d implement that in an +imperative fashion. You’d probably set up a variable to hold the maximum +value so far and then you’d loop through the elements of a list and if +an element is bigger than then the current maximum value, you’d replace +it with that element. The maximum value that remains at the end is the +result. Whew! That’s quite a lot of words to describe such a simple +algorithm!

        +

        Now let’s see how we’d define it recursively. We could first set up +an edge condition and say that the maximum of a singleton list is equal +to the only element in it. Then we can say that the maximum of a longer +list is the head if the head is bigger than the maximum of the tail. If +the maximum of the tail is bigger, well, then it’s the maximum of the +tail. That’s it! Now let’s implement that in Haskell.

        +
        maximum' :: (Ord a) => [a] -> a
         maximum' [] = error "maximum of empty list"
         maximum' [x] = x
         maximum' (x:xs)
             | x > maxTail = x
             | otherwise = maxTail
        -    where maxTail = maximum' xs
        -
        -

        As you can see, pattern matching goes great with recursion! Most imperative languages don’t have pattern matching so you have to make a lot of if else statements to test for edge conditions. Here, we simply put them out as patterns. So the first edge condition says that if the list is empty, crash! Makes sense because what’s the maximum of an empty list? I don’t know. The second pattern also lays out an edge condition. It says that if it’s the singleton list, just give back the only element.

        -

        Now the third pattern is where the action happens. We use pattern matching to split a list into a head and a tail. This is a very common idiom when doing recursion with lists, so get used to it. We use a where binding to define maxTail as the maximum of the rest of the list. Then we check if the head is greater than the maximum of the rest of the list. If it is, we return the head. Otherwise, we return the maximum of the rest of the list.

        -

        Let’s take an example list of numbers and check out how this would work on them: [2,5,1]. If we call maximum' on that, the first two patterns won’t match. The third one will and the list is split into 2 and [5,1]. The where clause wants to know the maximum of [5,1], so we follow that route. It matches the third pattern again and [5,1] is split into 5 and [1]. Again, the where clause wants to know the maximum of [1]. Because that’s the edge condition, it returns 1. Finally! So going up one step, comparing 5 to the maximum of [1] (which is 1), we obviously get back 5. So now we know that the maximum of [5,1] is 5. We go up one step again where we had 2 and [5,1]. Comparing 2 with the maximum of [5,1], which is 5, we choose 5.

        -

        An even clearer way to write this function is to use max. If you remember, max is a function that takes two numbers and returns the bigger of them. Here’s how we could rewrite maximum' by using max:

        -
        
        -maximum' :: (Ord a) => [a] -> a
        +    where maxTail = maximum' xs
        +

        As you can see, pattern matching goes great with recursion! Most +imperative languages don’t have pattern matching so you have to make a +lot of if else statements to test for edge conditions. Here, we simply +put them out as patterns. So the first edge condition says that if the +list is empty, crash! Makes sense because what’s the maximum of an empty +list? I don’t know. The second pattern also lays out an edge condition. +It says that if it’s the singleton list, just give back the only +element.

        +

        Now the third pattern is where the action happens. We use pattern +matching to split a list into a head and a tail. This is a very common +idiom when doing recursion with lists, so get used to it. We use a +where binding to define maxTail as the maximum of +the rest of the list. Then we check if the head is greater than the +maximum of the rest of the list. If it is, we return the head. +Otherwise, we return the maximum of the rest of the list.

        +

        Let’s take an example list of numbers and check out how this would +work on them: [2,5,1]. If we call maximum' on +that, the first two patterns won’t match. The third one will and the +list is split into 2 and [5,1]. The +where clause wants to know the maximum of [5,1], +so we follow that route. It matches the third pattern again and +[5,1] is split into 5 and [1]. +Again, the where clause wants to know the maximum of +[1]. Because that’s the edge condition, it returns +1. Finally! So going up one step, comparing 5 +to the maximum of [1] (which is 1), we +obviously get back 5. So now we know that the maximum of +[5,1] is 5. We go up one step again where we +had 2 and [5,1]. Comparing 2 with +the maximum of [5,1], which is 5, we choose +5.

        +

        An even clearer way to write this function is to use +max. If you remember, max is a function that +takes two numbers and returns the bigger of them. Here’s how we could +rewrite maximum' by using max:

        +
        maximum' :: (Ord a) => [a] -> a
         maximum' [] = error "maximum of empty list"
         maximum' [x] = x
        -maximum' (x:xs) = max x (maximum' xs)
        -
        -

        How’s that for elegant! In essence, the maximum of a list is the max of the first element and the maximum of the tail.

        -max -

        A few more recursive functions

        -

        Now that we know how to generally think recursively, let’s implement a few functions using recursion. First off, we’ll implement replicate. replicate takes an Int and some element and returns a list that has several repetitions of the same element. For instance, replicate 3 5 returns [5,5,5]. Let’s think about the edge condition. My guess is that the edge condition is 0 or less. If we try to replicate something zero times, it should return an empty list. Also for negative numbers, because it doesn’t really make sense.

        -
        
        -replicate' :: (Num i, Ord i) => i -> a -> [a]
        +maximum' (x:xs) = max x (maximum' xs)
        +

        How’s that for elegant! In essence, the maximum of a list is the max +of the first element and the maximum of the tail.

        +max +

        A few more recursive +functions

        +

        Now that we know how to generally think recursively, let’s implement +a few functions using recursion. First off, we’ll implement +replicate. replicate takes an Int +and some element and returns a list that has several repetitions of the +same element. For instance, replicate 3 5 returns +[5,5,5]. Let’s think about the edge condition. My guess is +that the edge condition is 0 or less. If we try to replicate something +zero times, it should return an empty list. Also for negative numbers, +because it doesn’t really make sense.

        +
        replicate' :: (Num i, Ord i) => i -> a -> [a]
         replicate' n x
             | n <= 0    = []
        -    | otherwise = x:replicate' (n-1) x
        -
        -

        We used guards here instead of patterns because we’re testing for a boolean condition. If n is less than or equal to 0, return an empty list. Otherwise return a list that has x as the first element and then x replicated n-1 times as the tail. Eventually, the (n-1) part will cause our function to reach the edge condition.

        -

        Note: Num is not a subclass of Ord. This is because not every number type has an ordering, e.g. complex numbers aren’t ordered. So that’s why we have to specify both the Num and Ord class constraints when doing addition or subtraction and also comparison.

        -

        Next up, we’ll implement take. It takes a certain number of elements from a list. For instance, take 3 [5,4,3,2,1] will return [5,4,3]. If we try to take 0 or less elements from a list, we get an empty list. Also if we try to take anything from an empty list, we get an empty list. Notice that those are two edge conditions right there. So let’s write that out:

        -
        
        -take' :: (Num i, Ord i) => i -> [a] -> [a]
        +    | otherwise = x:replicate' (n-1) x
        +

        We used guards here instead of patterns because we’re testing for a +boolean condition. If n is less than or equal to 0, return +an empty list. Otherwise return a list that has x as the +first element and then x replicated n-1 times as the tail. +Eventually, the (n-1) part will cause our function to reach +the edge condition.

        +
        +

        Note: Num is not a subclass of +Ord. This is because not every number type has an ordering, +e.g. complex numbers aren’t ordered. So that’s why we have to specify +both the Num and Ord class constraints when +doing addition or subtraction and also comparison.

        +
        +

        Next up, we’ll implement take. It takes a certain number +of elements from a list. For instance, take 3 [5,4,3,2,1] +will return [5,4,3]. If we try to take 0 or less elements +from a list, we get an empty list. Also if we try to take anything from +an empty list, we get an empty list. Notice that those are two edge +conditions right there. So let’s write that out:

        +
        take' :: (Num i, Ord i) => i -> [a] -> [a]
         take' n _
             | n <= 0   = []
         take' _ []     = []
        -take' n (x:xs) = x : take' (n-1) xs
        -
        -painter -

        The first pattern specifies that if we try to take a 0 or negative number of elements, we get an empty list. Notice that we’re using _ to match the list because we don’t really care what it is in this case. Also notice that we use a guard, but without an otherwise part. That means that if n turns out to be more than 0, the matching will fall through to the next pattern. The second pattern indicates that if we try to take anything from an empty list, we get an empty list. The third pattern breaks the list into a head and a tail. And then we state that taking n elements from a list equals a list that has x as the head and then a list that takes n-1 elements from the tail as a tail. Try using a piece of paper to write down how the evaluation would look like if we try to take, say, 3 from [4,3,2,1].

        -

        reverse simply reverses a list. Think about the edge condition. What is it? Come on … it’s the empty list! An empty list reversed equals the empty list itself. O-kay. What about the rest of it? Well, you could say that if we split a list to a head and a tail, the reversed list is equal to the reversed tail and then the head at the end.

        -
        
        -reverse' :: [a] -> [a]
        +take' n (x:xs) = x : take' (n-1) xs
        +painter +

        The first pattern specifies that if we try to take a 0 or negative +number of elements, we get an empty list. Notice that we’re using +_ to match the list because we don’t really care what it is +in this case. Also notice that we use a guard, but without an +otherwise part. That means that if n turns out +to be more than 0, the matching will fall through to the next pattern. +The second pattern indicates that if we try to take anything from an +empty list, we get an empty list. The third pattern breaks the list into +a head and a tail. And then we state that taking n elements +from a list equals a list that has x as the head and then a +list that takes n-1 elements from the tail as a tail. Try +using a piece of paper to write down how the evaluation would look like +if we try to take, say, 3 from [4,3,2,1].

        +

        reverse simply reverses a list. Think about the edge +condition. What is it? Come on … it’s the empty list! An empty list +reversed equals the empty list itself. O-kay. What about the rest of it? +Well, you could say that if we split a list to a head and a tail, the +reversed list is equal to the reversed tail and then the head at the +end.

        +
        reverse' :: [a] -> [a]
         reverse' [] = []
        -reverse' (x:xs) = reverse' xs ++ [x]
        -
        +reverse' (x:xs) = reverse' xs ++ [x]

        There we go!

        -

        Because Haskell supports infinite lists, our recursion doesn’t really have to have an edge condition. But if it doesn’t have it, it will either keep churning at something infinitely or produce an infinite data structure, like an infinite list. The good thing about infinite lists though is that we can cut them where we want. repeat takes an element and returns an infinite list that just has that element. A recursive implementation of that is really easy, watch.

        -
        
        -repeat' :: a -> [a]
        -repeat' x = x:repeat' x
        -
        -

        Calling repeat 3 will give us a list that starts with 3 and then has an infinite amount of 3’s as a tail. So calling repeat 3 would evaluate like 3:repeat 3, which is 3:(3:repeat 3), which is 3:(3:(3:repeat 3)), etc. repeat 3 will never finish evaluating, whereas take 5 (repeat 3) will give us a list of five 3’s. So essentially it’s like doing replicate 5 3.

        -

        zip takes two lists and zips them together. zip [1,2,3] [2,3] returns [(1,2),(2,3)], because it truncates the longer list to match the length of the shorter one. How about if we zip something with an empty list? Well, we get an empty list back then. So there’s our edge condition. However, zip takes two lists as parameters, so there are actually two edge conditions.

        -
        
        -zip' :: [a] -> [b] -> [(a,b)]
        +

        Because Haskell supports infinite lists, our recursion doesn’t really +have to have an edge condition. But if it doesn’t have it, it will +either keep churning at something infinitely or produce an infinite data +structure, like an infinite list. The good thing about infinite lists +though is that we can cut them where we want. repeat takes +an element and returns an infinite list that just has that element. A +recursive implementation of that is really easy, watch.

        +
        repeat' :: a -> [a]
        +repeat' x = x:repeat' x
        +

        Calling repeat 3 will give us a list that starts with +3 and then has an infinite amount of 3’s as a tail. So +calling repeat 3 would evaluate like +3:repeat 3, which is 3:(3:repeat 3), which is +3:(3:(3:repeat 3)), etc. repeat 3 will never +finish evaluating, whereas take 5 (repeat 3) will give us a +list of five 3’s. So essentially it’s like doing +replicate 5 3.

        +

        zip takes two lists and zips them together. +zip [1,2,3] [2,3] returns [(1,2),(2,3)], +because it truncates the longer list to match the length of the shorter +one. How about if we zip something with an empty list? Well, we get an +empty list back then. So there’s our edge condition. However, +zip takes two lists as parameters, so there are actually +two edge conditions.

        +
        zip' :: [a] -> [b] -> [(a,b)]
         zip' _ [] = []
         zip' [] _ = []
        -zip' (x:xs) (y:ys) = (x,y):zip' xs ys
        -
        -

        First two patterns say that if the first list or second list is empty, we get an empty list. The third one says that two lists zipped are equal to pairing up their heads and then tacking on the zipped tails. Zipping [1,2,3] and ['a','b'] will eventually try to zip [3] with []. The edge condition patterns kick in and so the result is (1,'a'):(2,'b'):[], which is exactly the same as [(1,'a'),(2,'b')].

        -

        Let’s implement one more standard library function — elem. It takes an element and a list and sees if that element is in the list. The edge condition, as is most of the times with lists, is the empty list. We know that an empty list contains no elements, so it certainly doesn’t have the droids we’re looking for.

        -
        
        -elem' :: (Eq a) => a -> [a] -> Bool
        +zip' (x:xs) (y:ys) = (x,y):zip' xs ys
        +

        First two patterns say that if the first list or second list is +empty, we get an empty list. The third one says that two lists zipped +are equal to pairing up their heads and then tacking on the zipped +tails. Zipping [1,2,3] and ['a','b'] will +eventually try to zip [3] with []. The edge +condition patterns kick in and so the result is +(1,'a'):(2,'b'):[], which is exactly the same as +[(1,'a'),(2,'b')].

        +

        Let’s implement one more standard library function — +elem. It takes an element and a list and sees if that +element is in the list. The edge condition, as is most of the times with +lists, is the empty list. We know that an empty list contains no +elements, so it certainly doesn’t have the droids we’re looking for.

        +
        elem' :: (Eq a) => a -> [a] -> Bool
         elem' a [] = False
         elem' a (x:xs)
             | a == x    = True
        -    | otherwise = a `elem'` xs
        -
        -

        Pretty simple and expected. If the head isn’t the element then we check the tail. If we reach an empty list, the result is False.

        + | otherwise = a `elem'` xs
        +

        Pretty simple and expected. If the head isn’t the element then we +check the tail. If we reach an empty list, the result is +False.

        Quick, sort!

        -

        We have a list of items that can be sorted. Their type is an instance of the Ord typeclass. And now, we want to sort them! There’s a very cool algorithm for sorting called quicksort. It’s a very clever way of sorting items. While it takes upwards of 10 lines to implement quicksort in imperative languages, the implementation is much shorter and elegant in Haskell. Quicksort has become a sort of poster child for Haskell. Therefore, let’s implement it here, even though implementing quicksort in Haskell is considered really cheesy because everyone does it to showcase how elegant Haskell is.

        -quickman -

        So, the type signature is going to be quicksort :: (Ord a) => [a] -> [a]. No surprises there. The edge condition? Empty list, as is expected. A sorted empty list is an empty list. Now here comes the main algorithm: a sorted list is a list that has all the values smaller than (or equal to) the head of the list in front (and those values are sorted), then comes the head of the list in the middle and then come all the values that are bigger than the head (they’re also sorted). Notice that we said sorted two times in this definition, so we’ll probably have to make the recursive call twice! Also notice that we defined it using the verb is to define the algorithm instead of saying do this, do that, then do that …. That’s the beauty of functional programming! How are we going to filter the list so that we get only the elements smaller than the head of our list and only elements that are bigger? List comprehensions. So, let’s dive in and define this function.

        -
        
        -quicksort :: (Ord a) => [a] -> [a]
        +

        We have a list of items that can be sorted. Their type is an instance +of the Ord typeclass. And now, we want to sort them! +There’s a very cool algorithm for sorting called quicksort. It’s a very +clever way of sorting items. While it takes upwards of 10 lines to +implement quicksort in imperative languages, the implementation is much +shorter and elegant in Haskell. Quicksort has become a sort of poster +child for Haskell. Therefore, let’s implement it here, even though +implementing quicksort in Haskell is considered really cheesy because +everyone does it to showcase how elegant Haskell is.

        +quickman +

        So, the type signature is going to be +quicksort :: (Ord a) => [a] -> [a]. No surprises +there. The edge condition? Empty list, as is expected. A sorted empty +list is an empty list. Now here comes the main algorithm: a +sorted list is a list that has all the values smaller than (or equal to) +the head of the list in front (and those values are sorted), then comes +the head of the list in the middle and then come all the values that are +bigger than the head (they’re also sorted). Notice that we said +sorted two times in this definition, so we’ll probably have to +make the recursive call twice! Also notice that we defined it using the +verb is to define the algorithm instead of saying do this, +do that, then do that …. That’s the beauty of functional +programming! How are we going to filter the list so that we get only the +elements smaller than the head of our list and only elements that are +bigger? List comprehensions. So, let’s dive in and define this +function.

        +
        quicksort :: (Ord a) => [a] -> [a]
         quicksort [] = []
         quicksort (x:xs) =
             let smallerSorted = quicksort [a | a <- xs, a <= x]
                 biggerSorted = quicksort [a | a <- xs, a > x]
        -    in  smallerSorted ++ [x] ++ biggerSorted
        -
        -

        Let’s give it a small test run to see if it appears to behave correctly.

        -
        
        -ghci> quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]
        +    in  smallerSorted ++ [x] ++ biggerSorted
        +

        Let’s give it a small test run to see if it appears to behave +correctly.

        +
        ghci> quicksort [10,2,5,3,1,6,7,4,2,3,4,8,9]
         [1,2,2,3,3,4,4,5,6,7,8,9,10]
         ghci> quicksort "the quick brown fox jumps over the lazy dog"
        -"        abcdeeefghhijklmnoooopqrrsttuuvwxyz"
        -
        -

        Booyah! That’s what I’m talking about! So if we have, say [5,1,9,4,6,7,3] and we want to sort it, this algorithm will first take the head, which is 5 and then put it in the middle of two lists that are smaller and bigger than it. So at one point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that once the list is sorted completely, the number 5 will stay in the fourth place since there are 3 numbers lower than it and 3 numbers higher than it. Now, if we sort [1,4,3] and [9,6,7], we have a sorted list! We sort the two lists using the same function. Eventually, we’ll break it up so much that we reach empty lists and an empty list is already sorted in a way, by virtue of being empty. Here’s an illustration:

        -quicksort -

        An element that is in place and won’t move anymore is represented in orange. If you read them from left to right, you’ll see the sorted list. Although we chose to compare all the elements to the heads, we could have used any element to compare against. In quicksort, an element that you compare against is called a pivot. They’re in green here. We chose the head because it’s easy to get by pattern matching. The elements that are smaller than the pivot are light green and elements larger than the pivot are dark green. The yellowish gradient thing represents an application of quicksort.

        +" abcdeeefghhijklmnoooopqrrsttuuvwxyz"
        +

        Booyah! That’s what I’m talking about! So if we have, say +[5,1,9,4,6,7,3] and we want to sort it, this algorithm will +first take the head, which is 5 and then put it in the +middle of two lists that are smaller and bigger than it. So at one +point, you’ll have [1,4,3] ++ [5] ++ [9,6,7]. We know that +once the list is sorted completely, the number 5 will stay +in the fourth place since there are 3 numbers lower than it and 3 +numbers higher than it. Now, if we sort [1,4,3] and +[9,6,7], we have a sorted list! We sort the two lists using +the same function. Eventually, we’ll break it up so much that we reach +empty lists and an empty list is already sorted in a way, by virtue of +being empty. Here’s an illustration:

        +quicksort +

        An element that is in place and won’t move anymore is represented in +orange. If you read +them from left to right, you’ll see the sorted list. Although we chose +to compare all the elements to the heads, we could have used any element +to compare against. In quicksort, an element that you compare against is +called a pivot. They’re in green here. We chose the +head because it’s easy to get by pattern matching. The elements that are +smaller than the pivot are light green and elements +larger than the pivot are dark green. The yellowish +gradient thing represents an application of quicksort.

        Thinking recursively

        -

        We did quite a bit of recursion so far and as you’ve probably noticed, there’s a pattern here. Usually you define an edge case and then you define a function that does something between some element and the function applied to the rest. It doesn’t matter if it’s a list, a tree or any other data structure. A sum is the first element of a list plus the sum of the rest of the list. A product of a list is the first element of the list times the product of the rest of the list. The length of a list is one plus the length of the tail of the list. Ekcetera, ekcetera …

        -brain -

        Of course, these also have edge cases. Usually the edge case is some scenario where a recursive application doesn’t make sense. When dealing with lists, the edge case is most often the empty list. If you’re dealing with trees, the edge case is usually a node that doesn’t have any children.

        -

        It’s similar when you’re dealing with numbers recursively. Usually it has to do with some number and the function applied to that number modified. We did the factorial function earlier and it’s the product of a number and the factorial of that number minus one. Such a recursive application doesn’t make sense with zero, because factorials are defined only for positive integers. Often the edge case value turns out to be an identity. The identity for multiplication is 1 because if you multiply something by 1, you get that something back. Also when doing sums of lists, we define the sum of an empty list as 0 and 0 is the identity for addition. In quicksort, the edge case is the empty list and the identity is also the empty list, because if you add an empty list to a list, you just get the original list back.

        -

        So when trying to think of a recursive way to solve a problem, try to think of when a recursive solution doesn’t apply and see if you can use that as an edge case, think about identities and think about whether you’ll break apart the parameters of the function (for instance, lists are usually broken into a head and a tail via pattern matching) and on which part you’ll use the recursive call.

        - +

        We did quite a bit of recursion so far and as you’ve probably +noticed, there’s a pattern here. Usually you define an edge case and +then you define a function that does something between some element and +the function applied to the rest. It doesn’t matter if it’s a list, a +tree or any other data structure. A sum is the first element of a list +plus the sum of the rest of the list. A product of a list is the first +element of the list times the product of the rest of the list. The +length of a list is one plus the length of the tail of the list. +Ekcetera, ekcetera …

        +brain +

        Of course, these also have edge cases. Usually the edge case is some +scenario where a recursive application doesn’t make sense. When dealing +with lists, the edge case is most often the empty list. If you’re +dealing with trees, the edge case is usually a node that doesn’t have +any children.

        +

        It’s similar when you’re dealing with numbers recursively. Usually it +has to do with some number and the function applied to that number +modified. We did the factorial function earlier and it’s the product of +a number and the factorial of that number minus one. Such a recursive +application doesn’t make sense with zero, because factorials are defined +only for positive integers. Often the edge case value turns out to be an +identity. The identity for multiplication is 1 because if you multiply +something by 1, you get that something back. Also when doing sums of +lists, we define the sum of an empty list as 0 and 0 is the identity for +addition. In quicksort, the edge case is the empty list and the identity +is also the empty list, because if you add an empty list to a list, you +just get the original list back.

        +

        So when trying to think of a recursive way to solve a problem, try to +think of when a recursive solution doesn’t apply and see if you can use +that as an edge case, think about identities and think about whether +you’ll break apart the parameters of the function (for instance, lists +are usually broken into a head and a tail via pattern matching) and on +which part you’ll use the recursive call.

        • diff --git a/docs/starting-out.html b/docs/starting-out.html index 68ab088..675a08c 100644 --- a/docs/starting-out.html +++ b/docs/starting-out.html @@ -31,24 +31,22 @@
        -

        Starting Out

        +

        Starting Out

        Ready, set, go!

        -

        -egg -Alright, let’s get started! If you’re the sort of horrible person who doesn’t read introductions to things and you skipped it, you might want to read the last section in the introduction anyway because it explains what you need to follow this tutorial and how we’re going to load functions. The first thing we’re going to do is run ghc’s interactive mode and call some function to get a very basic feel for Haskell. Open your terminal and type in ghci. You will be greeted with something like this. -

        -
        
        -GHCi, version 9.2.4: https://www.haskell.org/ghc/  :? for help
        -ghci>
        -
        -

        -Congratulations, you’re in GHCI! -

        -

        -Here’s some simple arithmetic. -

        -
        
        -ghci> 2 + 15
        +

        egg Alright, let’s get started! If +you’re the sort of horrible person who doesn’t read introductions to +things and you skipped it, you might want to read the last section in +the introduction anyway because it explains what you need to follow this +tutorial and how we’re going to load functions. The first thing we’re +going to do is run ghc’s interactive mode and call some function to get +a very basic feel for Haskell. Open your terminal and type in +ghci. You will be greeted with something like this.

        +
        GHCi, version 9.2.4: https://www.haskell.org/ghc/  :? for help
        +ghci>
        +

        Congratulations, you’re in GHCI!

        +

        Here’s some simple arithmetic.

        +
        ghci> 2 + 15
         17
         ghci> 49 * 100
         4900
        @@ -57,24 +55,25 @@ 

        Ready, set, go!

        ghci> 5 / 2 2.5 ghci>
        -

        -This is pretty self-explanatory. We can also use several operators on one line and all the usual precedence rules are obeyed. We can use parentheses to make the precedence explicit or to change it. -

        -
        
        -ghci> (50 * 100) - 4999
        +

        This is pretty self-explanatory. We can also use several operators on +one line and all the usual precedence rules are obeyed. We can use +parentheses to make the precedence explicit or to change it.

        +
        ghci> (50 * 100) - 4999
         1
         ghci> 50 * 100 - 4999
         1
         ghci> 50 * (100 - 4999)
         -244950
        -

        -Pretty cool, huh? Yeah, I know it’s not but bear with me. A little pitfall to watch out for here is negating numbers. If we want to have a negative number, it’s always best to surround it with parentheses. Doing 5 * -3 will make GHCI yell at you but doing 5 * (-3) will work just fine. -

        -

        -Boolean algebra is also pretty straightforward. As you probably know, && means a boolean and, || means a boolean or. not negates a True or a False. -

        -
        
        -ghci> True && False
        +

        Pretty cool, huh? Yeah, I know it’s not but bear with me. A little +pitfall to watch out for here is negating numbers. If we want to have a +negative number, it’s always best to surround it with parentheses. Doing +5 * -3 will make GHCI yell at you but doing +5 * (-3) will work just fine.

        +

        Boolean algebra is also pretty straightforward. As you probably know, +&& means a boolean and, || +means a boolean or. not negates a +True or a False.

        +
        ghci> True && False
         False
         ghci> True && True
         True
        @@ -84,11 +83,8 @@ 

        Ready, set, go!

        True ghci> not (True && True) False
        -

        -Testing for equality is done like so. -

        -
        
        -ghci> 5 == 5
        +

        Testing for equality is done like so.

        +
        ghci> 5 == 5
         True
         ghci> 1 == 0
         False
        @@ -97,167 +93,251 @@ 

        Ready, set, go!

        ghci> 5 /= 4 True ghci> "hello" == "hello" -True
        -

        -What about doing 5 + "llama" or 5 == True? Well, if we try the first snippet, we get a big scary error message! -

        -
        
        -    • No instance for (Num String) arising from a use of ‘+’
        +True
        +

        What about doing 5 + "llama" or 5 == True? +Well, if we try the first snippet, we get a big scary error message!

        +
        • No instance for (Num String) arising from a use of ‘+’
             • In the expression: 5 + "llama"
        -      In an equation for ‘it’: it = 5 + "llama"
        -
        -

        -Yikes! What GHCI is telling us here is that "llama" is not a number and so it doesn’t know how to add it to 5. Even if it wasn’t "llama" but "four" or "4", Haskell still wouldn’t consider it to be a number. + expects its left and right side to be numbers. If we tried to do True == 5, GHCI would tell us that the types don’t match. Whereas + works only on things that are considered numbers, == works on any two things that can be compared. But the catch is that they both have to be the same type of thing. You can’t compare apples and oranges. We’ll take a closer look at types a bit later. Note: you can do 5 + 4.0 because 5 is sneaky and can act like an integer or a floating-point number. 4.0 can’t act like an integer, so 5 is the one that has to adapt. -

        -

        -You may not have known it but we’ve been using functions now all along. For instance, * is a function that takes two numbers and multiplies them. As you’ve seen, we call it by sandwiching it between them. This is what we call an infix function. Most functions that aren’t used with numbers are prefix functions. Let’s take a look at them. -

        -

        -phoen -Functions are usually prefix, so from now on we won’t explicitly state that a function is of the prefix form, we’ll just assume it. In most imperative languages, functions are called by writing the function name and then writing its parameters in parentheses, usually separated by commas. In Haskell, functions are called by writing the function name, a space and then the parameters, separated by spaces. For a start, we’ll try calling one of the most boring functions in Haskell. -

        -
        
        -ghci> succ 8
        -9 
        -

        -The succ function takes anything that has a defined successor and returns that successor. As you can see, we just separate the function name from the parameter with a space. Calling a function with several parameters is also simple. The functions min and max take two things that can be put in an order (like integers!). min returns the one that’s lesser and max returns the one that’s greater. See for yourself: -

        -
        
        -ghci> min 9 10
        +      In an equation for ‘it’: it = 5 + "llama"
        +

        Yikes! What GHCI is telling us here is that "llama" is +not a number and so it doesn’t know how to add it to 5. Even if it +wasn’t "llama" but "four" or "4", +Haskell still wouldn’t consider it to be a number. + +expects its left and right side to be numbers. If we tried to do +True == 5, GHCI would tell us that the types don’t match. +Whereas + works only on things that are considered numbers, +== works on any two things that can be compared. But the +catch is that they both have to be the same type of thing. You can’t +compare apples and oranges. We’ll take a closer look at types a bit +later. Note: you can do 5 + 4.0 because 5 is +sneaky and can act like an integer or a floating-point number. +4.0 can’t act like an integer, so 5 is the one +that has to adapt.

        +

        You may not have known it but we’ve been using functions now all +along. For instance, * is a function that takes two numbers +and multiplies them. As you’ve seen, we call it by sandwiching it +between them. This is what we call an infix function. Most +functions that aren’t used with numbers are prefix functions. +Let’s take a look at them.

        +

        phoen Functions are usually prefix, so +from now on we won’t explicitly state that a function is of the prefix +form, we’ll just assume it. In most imperative languages, functions are +called by writing the function name and then writing its parameters in +parentheses, usually separated by commas. In Haskell, functions are +called by writing the function name, a space and then the parameters, +separated by spaces. For a start, we’ll try calling one of the most +boring functions in Haskell.

        +
        ghci> succ 8
        +9
        +

        The succ function takes anything that has a defined +successor and returns that successor. As you can see, we just separate +the function name from the parameter with a space. Calling a function +with several parameters is also simple. The functions min +and max take two things that can be put in an order (like +integers!). min returns the one that’s lesser and +max returns the one that’s greater. See for yourself:

        +
        ghci> min 9 10
         9
         ghci> max 100 101
        -101 
        - -

        -Function application (calling a function by putting a space after it and then typing out the parameters) has the highest precedence of them all. What that means for us is that these two statements are equivalent. -

        -
        
        -ghci> succ 9 + max 5 4 + 1
        +101
        +

        Function application (calling a function by putting a space after it +and then typing out the parameters) has the highest precedence of them +all. What that means for us is that these two statements are +equivalent.

        +
        ghci> succ 9 + max 5 4 + 1
         16
         ghci> (succ 9) + (max 5 4) + 1
        -16
        -
        -

        -However, if we wanted to get the successor of the product of numbers 9 and 10, we couldn’t write succ 9 * 10 because that would get the successor of 9, which would then be multiplied by 10. So 100. We’d have to write succ (9 * 10) to get 91. -

        -

        If a function takes two parameters, we can also call it as an infix function by surrounding it with backticks. For instance, the div function takes two integers and does integral division between them. Doing div 92 10 results in a 9. But when we call it like that, there may be some confusion as to which number is doing the division and which one is being divided. So we can call it as an infix function by doing 92 `div` 10 and suddenly it’s much clearer.

        -

        Lots of people who come from imperative languages tend to stick to the notion that parentheses should denote function application. For example, in C, you use parentheses to call functions like foo(), bar(1) or baz(3, "haha"). Like we said, spaces are used for function application in Haskell. So those functions in Haskell would be foo, bar 1 and baz 3 "haha". So if you see something like bar (bar 3), it doesn’t mean that bar is called with bar and 3 as parameters. It means that we first call the function bar with 3 as the parameter to get some number and then we call bar again with that number. In C, that would be something like bar(bar(3)).

        +16
        +

        However, if we wanted to get the successor of the product of numbers +9 and 10, we couldn’t write succ 9 * 10 because that would +get the successor of 9, which would then be multiplied by 10. So 100. +We’d have to write succ (9 * 10) to get 91.

        +

        If a function takes two parameters, we can also call it as an infix +function by surrounding it with backticks. For instance, the +div function takes two integers and does integral division +between them. Doing div 92 10 results in a 9. But when we +call it like that, there may be some confusion as to which number is +doing the division and which one is being divided. So we can call it as +an infix function by doing 92 `div` 10 and suddenly it’s +much clearer.

        +

        Lots of people who come from imperative languages tend to stick to +the notion that parentheses should denote function application. For +example, in C, you use parentheses to call functions like +foo(), bar(1) or baz(3, "haha"). +Like we said, spaces are used for function application in Haskell. So +those functions in Haskell would be foo, bar 1 +and baz 3 "haha". So if you see something like +bar (bar 3), it doesn’t mean that bar is +called with bar and 3 as parameters. It means +that we first call the function bar with 3 as +the parameter to get some number and then we call bar again +with that number. In C, that would be something like +bar(bar(3)).

        Baby’s first functions

        -

        -In the previous section we got a basic feel for calling functions. Now let’s try making our own! Open up your favorite text editor and punch in this function that takes a number and multiplies it by two. -

        -
        
        -doubleMe x = x + x
        -

        -Functions are defined in a similar way that they are called. The function name is followed by parameters separated by spaces. But when defining functions, there’s a = and after that we define what the function does. Save this as baby.hs or something. Now navigate to where it’s saved and run ghci from there. Once inside GHCI, do :l baby. Now that our script is loaded, we can play with the function that we defined. -

        -
        
        -ghci> :l baby
        +

        In the previous section we got a basic feel for calling functions. +Now let’s try making our own! Open up your favorite text editor and +punch in this function that takes a number and multiplies it by two.

        +
        doubleMe x = x + x
        +

        Functions are defined in a similar way that they are called. The +function name is followed by parameters separated by spaces. But when +defining functions, there’s a = and after that we define +what the function does. Save this as baby.hs or something. +Now navigate to where it’s saved and run ghci from there. +Once inside GHCI, do :l baby. Now that our script is +loaded, we can play with the function that we defined.

        +
        ghci> :l baby
         [1 of 1] Compiling Main             ( baby.hs, interpreted )
         Ok, one module loaded.
         ghci> doubleMe 9
         18
         ghci> doubleMe 8.3
        -16.6 
        -

        -Because + works on integers as well as on floating-point numbers (anything that can be considered a number, really), our function also works on any number. Let’s make a function that takes two numbers and multiplies each by two and then adds them together. -

        -
        
        -doubleUs x y = x*2 + y*2 
        -

        -Simple. We could have also defined it as doubleUs x y = x + x + y + y. Testing it out produces pretty predictable results (remember to append this function to the baby.hs file, save it and then do :l baby inside GHCI). -

        -
        
        -ghci> doubleUs 4 9
        +16.6
        +

        Because + works on integers as well as on floating-point +numbers (anything that can be considered a number, really), our function +also works on any number. Let’s make a function that takes two numbers +and multiplies each by two and then adds them together.

        +
        doubleUs x y = x*2 + y*2
        +

        Simple. We could have also defined it as +doubleUs x y = x + x + y + y. Testing it out produces +pretty predictable results (remember to append this function to the +baby.hs file, save it and then do :l baby +inside GHCI).

        +
        ghci> doubleUs 4 9
         26
         ghci> doubleUs 2.3 34.2
         73.0
         ghci> doubleUs 28 88 + doubleMe 123
        -478
        -
        -

        -As expected, you can call your own functions from other functions that you made. With that in mind, we could redefine doubleUs like this: -

        -
        
        -doubleUs x y = doubleMe x + doubleMe y 
        -

        -This is a very simple example of a common pattern you will see throughout Haskell. Making basic functions that are obviously correct and then combining them into more complex functions. This way you also avoid repetition. What if some mathematicians figured out that 2 is actually 3 and you had to change your program? You could just redefine doubleMe to be x + x + x and since doubleUs calls doubleMe, it would automatically work in this strange new world where 2 is 3. -

        -

        Functions in Haskell don’t have to be in any particular order, so it doesn’t matter if you define doubleMe first and then doubleUs or if you do it the other way around.

        -

        Now we’re going to make a function that multiplies a number by 2 but only if that number is smaller than or equal to 100 because numbers bigger than 100 are big enough as it is! -

        -
        
        -doubleSmallNumber x = if x > 100
        +478
        +

        As expected, you can call your own functions from other functions +that you made. With that in mind, we could redefine +doubleUs like this:

        +
        doubleUs x y = doubleMe x + doubleMe y
        +

        This is a very simple example of a common pattern you will see +throughout Haskell. Making basic functions that are obviously correct +and then combining them into more complex functions. This way you also +avoid repetition. What if some mathematicians figured out that 2 is +actually 3 and you had to change your program? You could just redefine +doubleMe to be x + x + x and since +doubleUs calls doubleMe, it would +automatically work in this strange new world where 2 is 3.

        +

        Functions in Haskell don’t have to be in any particular order, so it +doesn’t matter if you define doubleMe first and then +doubleUs or if you do it the other way around.

        +

        Now we’re going to make a function that multiplies a number by 2 but +only if that number is smaller than or equal to 100 because numbers +bigger than 100 are big enough as it is!

        +
        doubleSmallNumber x = if x > 100
                                 then x
        -                        else x*2 
        -this is you -

        -Right here we introduced Haskell’s if statement. You’re probably familiar with if statements from other languages. The difference between Haskell’s if statement and if statements in imperative languages is that the else part is mandatory in Haskell. In imperative languages you can just skip a couple of steps if the condition isn’t satisfied but in Haskell every expression and function must return something. We could have also written that if statement in one line but I find this way more readable. Another thing about the if statement in Haskell is that it is an expression. An expression is basically a piece of code that returns a value. 5 is an expression because it returns 5, 4 + 8 is an expression, x + y is an expression because it returns the sum of x and y. Because the else is mandatory, an if statement will always return something and that’s why it’s an expression. If we wanted to add one to every number that’s produced in our previous function, we could have written its body like this. -

        -
        
        -doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
        -
        -

        -Had we omitted the parentheses, it would have added one only if x wasn’t greater than 100. Note the ' at the end of the function name. That apostrophe doesn’t have any special meaning in Haskell’s syntax. It’s a valid character to use in a function name. We usually use ' to either denote a strict version of a function (one that isn’t lazy) or a slightly modified version of a function or a variable. Because ' is a valid character in functions, we can make a function like this. -

        -
        
        -conanO'Brien = "It's a-me, Conan O'Brien!" 
        -

        -There are two noteworthy things here. The first is that in the function name we didn’t capitalize Conan’s name. That’s because functions can’t begin with uppercase letters. We’ll see why a bit later. The second thing is that this function doesn’t take any parameters. When a function doesn’t take any parameters, we usually say it’s a definition (or a name). Because we can’t change what names (and functions) mean once we’ve defined them, conanO'Brien and the string "It's a-me, Conan O'Brien!" can be used interchangeably. -

        + else x*2
        +this is you +

        Right here we introduced Haskell’s if statement. You’re probably +familiar with if statements from other languages. The difference between +Haskell’s if statement and if statements in imperative languages is that +the else part is mandatory in Haskell. In imperative languages you can +just skip a couple of steps if the condition isn’t satisfied but in +Haskell every expression and function must return something. We could +have also written that if statement in one line but I find this way more +readable. Another thing about the if statement in Haskell is that it is +an expression. An expression is basically a piece of code that +returns a value. 5 is an expression because it returns 5, +4 + 8 is an expression, x + y is an expression +because it returns the sum of x and y. Because +the else is mandatory, an if statement will always return something and +that’s why it’s an expression. If we wanted to add one to every number +that’s produced in our previous function, we could have written its body +like this.

        +
        doubleSmallNumber' x = (if x > 100 then x else x*2) + 1
        +

        Had we omitted the parentheses, it would have added one only if +x wasn’t greater than 100. Note the ' at the +end of the function name. That apostrophe doesn’t have any special +meaning in Haskell’s syntax. It’s a valid character to use in a function +name. We usually use ' to either denote a strict version of +a function (one that isn’t lazy) or a slightly modified version of a +function or a variable. Because ' is a valid character in +functions, we can make a function like this.

        +
        conanO'Brien = "It's a-me, Conan O'Brien!"
        +

        There are two noteworthy things here. The first is that in the +function name we didn’t capitalize Conan’s name. That’s because +functions can’t begin with uppercase letters. We’ll see why a bit later. +The second thing is that this function doesn’t take any parameters. When +a function doesn’t take any parameters, we usually say it’s a +definition (or a name). Because we can’t change what +names (and functions) mean once we’ve defined them, +conanO'Brien and the string +"It's a-me, Conan O'Brien!" can be used +interchangeably.

        An intro to lists

        -

        -BUY A DOG -Much like shopping lists in the real world, lists in Haskell are very useful. It’s the most used data structure and it can be used in a multitude of different ways to model and solve a whole bunch of problems. Lists are SO awesome. In this section we’ll look at the basics of lists, strings (which are lists) and list comprehensions. -

        -

        -In Haskell, lists are a homogenous data structure. They store several elements of the same type. That means that we can have a list of integers or a list of characters but we can’t have a list that has a few integers and then a few characters. And now, a list! -

        -
        
        -ghci> lostNumbers = [4,8,15,16,23,42]
        +

        BUY A DOG Much like shopping lists in +the real world, lists in Haskell are very useful. It’s the most used +data structure and it can be used in a multitude of different ways to +model and solve a whole bunch of problems. Lists are SO awesome. In this +section we’ll look at the basics of lists, strings (which are lists) and +list comprehensions.

        +

        In Haskell, lists are a homogenous data structure. +They store several elements of the same type. That means that we can +have a list of integers or a list of characters but we can’t have a list +that has a few integers and then a few characters. And now, a list!

        +
        ghci> lostNumbers = [4,8,15,16,23,42]
         ghci> lostNumbers
        -[4,8,15,16,23,42]
        -
        -

        -As you can see, lists are denoted by square brackets and the values in the lists are separated by commas. If we tried a list like [1,2,'a',3,'b','c',4], Haskell would complain that characters (which are, by the way, denoted as a character between single quotes) are not numbers. Speaking of characters, strings are just lists of characters. "hello" is just syntactic sugar for ['h','e','l','l','o']. Because strings are lists, we can use list functions on them, which is really handy. -

        -

        A common task is putting two lists together. This is done by using the ++ operator.

        -
        
        -ghci> [1,2,3,4] ++ [9,10,11,12]
        +[4,8,15,16,23,42]
        +

        As you can see, lists are denoted by square brackets and the values +in the lists are separated by commas. If we tried a list like +[1,2,'a',3,'b','c',4], Haskell would complain that +characters (which are, by the way, denoted as a character between single +quotes) are not numbers. Speaking of characters, strings are just lists +of characters. "hello" is just syntactic sugar for +['h','e','l','l','o']. Because strings are lists, we can +use list functions on them, which is really handy.

        +

        A common task is putting two lists together. This is done by using +the ++ operator.

        +
        ghci> [1,2,3,4] ++ [9,10,11,12]
         [1,2,3,4,9,10,11,12]
         ghci> "hello" ++ " " ++ "world"
         "hello world"
         ghci> ['w','o'] ++ ['o','t']
        -"woot"
        -
        -

        -Watch out when repeatedly using the ++ operator on long strings. When you put together two lists (even if you append a singleton list to a list, for instance: [1,2,3] ++ [4]), internally, Haskell has to walk through the whole list on the left side of ++. That’s not a problem when dealing with lists that aren’t too big. But putting something at the end of a list that’s fifty million entries long is going to take a while. However, putting something at the beginning of a list using the : operator (also called the cons operator) is instantaneous. -

        -
        
        -ghci> 'A':" SMALL CAT"
        +"woot"
        +

        Watch out when repeatedly using the ++ operator on long +strings. When you put together two lists (even if you append a singleton +list to a list, for instance: [1,2,3] ++ [4]), internally, +Haskell has to walk through the whole list on the left side of +++. That’s not a problem when dealing with lists that +aren’t too big. But putting something at the end of a list that’s fifty +million entries long is going to take a while. However, putting +something at the beginning of a list using the : operator +(also called the cons operator) is instantaneous.

        +
        ghci> 'A':" SMALL CAT"
         "A SMALL CAT"
         ghci> 5:[1,2,3,4,5]
        -[5,1,2,3,4,5]
        -
        -

        -Notice how : takes a number and a list of numbers or a character and a list of characters, whereas ++ takes two lists. Even if you’re adding an element to the end of a list with ++, you have to surround it with square brackets so it becomes a list.

        -

        -[1,2,3] is actually just syntactic sugar for 1:2:3:[]. [] is an empty list. If we prepend 3 to it, it becomes [3]. If we prepend 2 to that, it becomes [2,3], and so on. -

        -

        Note: [], [[]] and[[],[],[]] are all different things. The first one is an empty list, the seconds one is a list that contains one empty list, the third one is a list that contains three empty lists.

        -

        If you want to get an element out of a list by index, use !!. The indices start at 0.

        -
        
        -ghci> "Steve Buscemi" !! 6
        +[5,1,2,3,4,5]
        +

        Notice how : takes a number and a list of numbers or a +character and a list of characters, whereas ++ takes two +lists. Even if you’re adding an element to the end of a list with +++, you have to surround it with square brackets so it +becomes a list.

        +

        [1,2,3] is actually just syntactic sugar for +1:2:3:[]. [] is an empty list. If we prepend +3 to it, it becomes [3]. If we prepend +2 to that, it becomes [2,3], and so on.

        +
        +

        Note: [], [[]] +and[[],[],[]] are all different things. The first one is an +empty list, the seconds one is a list that contains one empty list, the +third one is a list that contains three empty lists.

        +
        +

        If you want to get an element out of a list by index, use +!!. The indices start at 0.

        +
        ghci> "Steve Buscemi" !! 6
         'B'
         ghci> [9.4,33.2,96.2,11.2,23.25] !! 1
        -33.2
        -
        -

        But if you try to get the sixth element from a list that only has four elements, you’ll get an error so be careful!

        -

        -Lists can also contain lists. They can also contain lists that contain lists that contain lists … -

        -
        
        -ghci> b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
        +33.2
        +

        But if you try to get the sixth element from a list that only has +four elements, you’ll get an error so be careful!

        +

        Lists can also contain lists. They can also contain lists that +contain lists that contain lists …

        +
        ghci> b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
         ghci> b
         [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]
         ghci> b ++ [[1,1,1,1]]
        @@ -265,11 +345,17 @@ 

        An intro to lists

        ghci> [6,6,6]:b [[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]] ghci> b !! 2 -[1,2,2,3,4]
        -

        The lists within a list can be of different lengths but they can’t be of different types. Just like you can’t have a list that has some characters and some numbers, you can’t have a list that has some lists of characters and some lists of numbers.

        -

        Lists can be compared if the stuff they contain can be compared. When using <, <=, > and >= to compare lists, they are compared in lexicographical order. First the heads are compared. If they are equal then the second elements are compared, etc.

        -
        
        -ghci> [3,2,1] > [2,1,0]
        +[1,2,2,3,4]
        +

        The lists within a list can be of different lengths but they can’t be +of different types. Just like you can’t have a list that has some +characters and some numbers, you can’t have a list that has some lists +of characters and some lists of numbers.

        +

        Lists can be compared if the stuff they contain can be compared. When +using <, <=, > and +>= to compare lists, they are compared in +lexicographical order. First the heads are compared. If they are equal +then the second elements are compared, etc.

        +
        ghci> [3,2,1] > [2,1,0]
         True
         ghci> [3,2,1] > [2,10,100]
         True
        @@ -278,52 +364,56 @@ 

        An intro to lists

        ghci> [3,4,2] > [2,4] True ghci> [3,4,2] == [3,4,2] -True -
        -

        -What else can you do with lists? Here are some basic functions that operate on lists. -

        -

        head takes a list and returns its head. The head of a list is basically its first element. -

        -
        
        -ghci> head [5,4,3,2,1]
        -5 
        -

        tail takes a list and returns its tail. In other words, it chops off a list’s head.

        -
        
        -ghci> tail [5,4,3,2,1]
        -[4,3,2,1] 
        -

        last takes a list and returns its last element.

        -
        
        -ghci> last [5,4,3,2,1]
        -1 
        -

        init takes a list and returns everything except its last element.

        -
        
        -ghci> init [5,4,3,2,1]
        -[5,4,3,2] 
        +True
        +

        What else can you do with lists? Here are some basic functions that +operate on lists.

        +

        head takes a list and returns its +head. The head of a list is basically its first element.

        +
        ghci> head [5,4,3,2,1]
        +5
        +

        tail takes a list and returns its +tail. In other words, it chops off a list’s head.

        +
        ghci> tail [5,4,3,2,1]
        +[4,3,2,1]
        +

        last takes a list and returns its +last element.

        +
        ghci> last [5,4,3,2,1]
        +1
        +

        init takes a list and returns +everything except its last element.

        +
        ghci> init [5,4,3,2,1]
        +[5,4,3,2]

        If we think of a list as a monster, here’s what’s what.

        -list monster +list monster

        But what happens if we try to get the head of an empty list?

        -
        
        -ghci> head []
        +
        ghci> head []
         *** Exception: Prelude.head: empty list
        -

        Oh my! It all blows up in our face! If there’s no monster, it doesn’t have a head. When using head, tail, last and init, be careful not to use them on empty lists. This error cannot be caught at compile time, so it’s always good practice to take precautions against accidentally telling Haskell to give you some elements from an empty list.

        -

        length takes a list and returns its length, obviously.

        -
        
        -ghci> length [5,4,3,2,1]
        +

        Oh my! It all blows up in our face! If there’s no monster, it doesn’t +have a head. When using head, tail, +last and init, be careful not to use them on +empty lists. This error cannot be caught at compile time, so it’s always +good practice to take precautions against accidentally telling Haskell +to give you some elements from an empty list.

        +

        length takes a list and returns +its length, obviously.

        +
        ghci> length [5,4,3,2,1]
         5
        -

        null checks if a list is empty. If it is, it returns True, otherwise it returns False. Use this function instead of xs == [] (if you have a list called xs).

        -
        
        -ghci> null [1,2,3]
        +

        null checks if a list is empty. +If it is, it returns True, otherwise it returns +False. Use this function instead of xs == [] +(if you have a list called xs).

        +
        ghci> null [1,2,3]
         False
         ghci> null []
         True

        reverse reverses a list.

        -
        
        -ghci> reverse [5,4,3,2,1]
        +
        ghci> reverse [5,4,3,2,1]
         [1,2,3,4,5]
        -

        take takes a number and a list. It extracts that many elements from the beginning of the list. Watch.

        -
        
        -ghci> take 3 [5,4,3,2,1]
        +

        take takes a number and a list. +It extracts that many elements from the beginning of the list. +Watch.

        +
        ghci> take 3 [5,4,3,2,1]
         [5,4,3]
         ghci> take 1 [3,9,3]
         [3]
        @@ -331,211 +421,342 @@ 

        An intro to lists

        [1,2] ghci> take 0 [6,6,6] []
        -

        See how if we try to take more elements than there are in the list, it just returns the list. If we try to take 0 elements, we get an empty list.

        -

        drop works in a similar way, only it drops the number of elements from the beginning of a list.

        -
        
        -ghci> drop 3 [8,4,2,1,5,6]
        +

        See how if we try to take more elements than there are in the list, +it just returns the list. If we try to take 0 elements, we get an empty +list.

        +

        drop works in a similar way, only +it drops the number of elements from the beginning of a list.

        +
        ghci> drop 3 [8,4,2,1,5,6]
         [1,5,6]
         ghci> drop 0 [1,2,3,4]
         [1,2,3,4]
         ghci> drop 100 [1,2,3,4]
        -[] 
        -

        maximum takes a list of stuff that can be put in some kind of order and returns the biggest element.

        minimum returns the smallest.

        -
        
        -ghci> minimum [8,4,2,1,5,6]
        +[]
        +

        maximum takes a list of stuff +that can be put in some kind of order and returns the biggest +element.

        +

        minimum returns the smallest.

        +
        ghci> minimum [8,4,2,1,5,6]
         1
         ghci> maximum [1,9,2,3,4]
        -9 
        -

        sum takes a list of numbers and returns their sum.

        product takes a list of numbers and returns their product.

        -
        
        -ghci> sum [5,2,1,6,3,2,5,7]
        +9
        +

        sum takes a list of numbers and +returns their sum.

        +

        product takes a list of numbers +and returns their product.

        +
        ghci> sum [5,2,1,6,3,2,5,7]
         31
         ghci> product [6,2,1,2]
         24
         ghci> product [1,2,5,6,7,9,2,0]
        -0 
        -

        elem takes a thing and a list of things and tells us if that thing is an element of the list. It’s usually called as an infix function because it’s easier to read that way.

        -
        
        -ghci> 4 `elem` [3,4,5,6]
        +0
        +

        elem takes a thing and a list of +things and tells us if that thing is an element of the list. It’s +usually called as an infix function because it’s easier to read that +way.

        +
        ghci> 4 `elem` [3,4,5,6]
         True
         ghci> 10 `elem` [3,4,5,6]
        -False
        -
        -

        Those were a few basic functions that operate on lists. We’ll take a look at more list functions later.

        +False
        +

        Those were a few basic functions that operate on lists. We’ll take a +look at more list functions later.

        Texas ranges

        -

        -draw -What if we want a list of all numbers between 1 and 20? Sure, we could just type them all out but obviously that’s not a solution for gentlemen who demand excellence from their programming languages. Instead, we’ll use ranges. Ranges are a way of making lists that are arithmetic sequences of elements that can be enumerated. Numbers can be enumerated. One, two, three, four, etc. Characters can also be enumerated. The alphabet is an enumeration of characters from A to Z. Names can’t be enumerated. What comes after “John”? I don’t know. -

        -

        To make a list containing all the natural numbers from 1 to 20, you just write [1..20]. That is the equivalent of writing [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and there’s no difference between writing one or the other except that writing out long enumeration sequences manually is stupid.

        -
        
        -ghci> [1..20]
        +

        draw What if we want a list of all +numbers between 1 and 20? Sure, we could just type them all out but +obviously that’s not a solution for gentlemen who demand excellence from +their programming languages. Instead, we’ll use ranges. Ranges are a way +of making lists that are arithmetic sequences of elements that can be +enumerated. Numbers can be enumerated. One, two, three, four, etc. +Characters can also be enumerated. The alphabet is an enumeration of +characters from A to Z. Names can’t be enumerated. What comes after +“John”? I don’t know.

        +

        To make a list containing all the natural numbers from 1 to 20, you +just write [1..20]. That is the equivalent of writing +[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] and +there’s no difference between writing one or the other except that +writing out long enumeration sequences manually is stupid.

        +
        ghci> [1..20]
         [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
         ghci> ['a'..'z']
         "abcdefghijklmnopqrstuvwxyz"
         ghci> ['K'..'Z']
        -"KLMNOPQRSTUVWXYZ" 
        -

        -Ranges are cool because you can also specify a step. What if we want all even numbers between 1 and 20? Or every third number between 1 and 20? -

        -
        
        -ghci> [2,4..20]
        +"KLMNOPQRSTUVWXYZ"
        +

        Ranges are cool because you can also specify a step. What if we want +all even numbers between 1 and 20? Or every third number between 1 and +20?

        +
        ghci> [2,4..20]
         [2,4,6,8,10,12,14,16,18,20]
         ghci> [3,6..20]
        -[3,6,9,12,15,18] 
        -

        It’s simply a matter of separating the first two elements with a comma and then specifying what the upper limit is. While pretty smart, ranges with steps aren’t as smart as some people expect them to be. You can’t do [1,2,4,8,16..100] and expect to get all the powers of 2. Firstly because you can only specify one step. And secondly because some sequences that aren’t arithmetic are ambiguous if given only by a few of their first terms. -

        -

        To make a list with all the numbers from 20 to 1, you can’t just do [20..1], you have to do [20,19..1].

        -

        Watch out when using floating point numbers in ranges! Because they are not completely precise (by definition), their use in ranges can yield some pretty funky results.

        -
        
        -ghci> [0.1, 0.3 .. 1]
        -[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]
        -
        +[3,6,9,12,15,18]
        +

        It’s simply a matter of separating the first two elements with a +comma and then specifying what the upper limit is. While pretty smart, +ranges with steps aren’t as smart as some people expect them to be. You +can’t do [1,2,4,8,16..100] and expect to get all the powers +of 2. Firstly because you can only specify one step. And secondly +because some sequences that aren’t arithmetic are ambiguous if given +only by a few of their first terms.

        +

        To make a list with all the numbers from 20 to 1, you can’t just do +[20..1], you have to do [20,19..1].

        +

        Watch out when using floating point numbers in ranges! Because they +are not completely precise (by definition), their use in ranges can +yield some pretty funky results.

        +
        ghci> [0.1, 0.3 .. 1]
        +[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]

        My advice is not to use them in list ranges.

        -

        -You can also use ranges to make infinite lists by just not specifying an upper limit. Later we’ll go into more detail on infinite lists. For now, let’s examine how you would get the first 24 multiples of 13. Sure, you could do [13,26..24*13]. But there’s a better way: take 24 [13,26..]. Because Haskell is lazy, it won’t try to evaluate the infinite list immediately because it would never finish. It’ll wait to see what you want to get out of that infinite lists. And here it sees you just want the first 24 elements and it gladly obliges. -

        +

        You can also use ranges to make infinite lists by just not specifying +an upper limit. Later we’ll go into more detail on infinite lists. For +now, let’s examine how you would get the first 24 multiples of 13. Sure, +you could do [13,26..24*13]. But there’s a better way: +take 24 [13,26..]. Because Haskell is lazy, it won’t try to +evaluate the infinite list immediately because it would never finish. +It’ll wait to see what you want to get out of that infinite lists. And +here it sees you just want the first 24 elements and it gladly +obliges.

        A handful of functions that produce infinite lists:

        -

        cycle takes a list and cycles it into an infinite list. If you just try to display the result, it will go on forever so you have to slice it off somewhere.

        -
        
        -ghci> take 10 (cycle [1,2,3])
        +

        cycle takes a list and cycles it +into an infinite list. If you just try to display the result, it will go +on forever so you have to slice it off somewhere.

        +
        ghci> take 10 (cycle [1,2,3])
         [1,2,3,1,2,3,1,2,3,1]
         ghci> take 12 (cycle "LOL ")
        -"LOL LOL LOL " 
        -

        repeat takes an element and produces an infinite list of just that element. It’s like cycling a list with only one element.

        -
        
        -ghci> take 10 (repeat 5)
        -[5,5,5,5,5,5,5,5,5,5]
        -
        -

        Although it’s simpler to just use the replicate function if you want some number of the same element in a list. replicate 3 10 returns [10,10,10].

        +"LOL LOL LOL "
        +

        repeat takes an element and +produces an infinite list of just that element. It’s like cycling a list +with only one element.

        +
        ghci> take 10 (repeat 5)
        +[5,5,5,5,5,5,5,5,5,5]
        +

        Although it’s simpler to just use the replicate function if you want some number +of the same element in a list. replicate 3 10 returns +[10,10,10].

        I’m a list comprehension

        -

        -frog -If you’ve ever taken a course in mathematics, you’ve probably run into set comprehensions. They’re normally used for building more specific sets out of general sets. A basic comprehension for a set that contains the first ten even natural numbers is set notation. The part before the pipe is called the output function, x is the variable, N is the input set and x <= 10 is the predicate. That means that the set contains the doubles of all natural numbers that satisfy the predicate. -

        -

        If we wanted to write that in Haskell, we could do something like take 10 [2,4..]. But what if we didn’t want doubles of the first 10 natural numbers but some kind of more complex function applied on them? We could use a list comprehension for that. List comprehensions are very similar to set comprehensions. We’ll stick to getting the first 10 even numbers for now. The list comprehension we could use is [x*2 | x <- [1..10]]. x is drawn from [1..10] and for every element in [1..10] (which we have bound to x), we get that element, only doubled. Here’s that comprehension in action.

        -
        
        -ghci> [x*2 | x <- [1..10]]
        -[2,4,6,8,10,12,14,16,18,20]
        -
        -

        As you can see, we get the desired results. Now let’s add a condition (or a predicate) to that comprehension. Predicates go after the binding parts and are separated from them by a comma. Let’s say we want only the elements which, doubled, are greater than or equal to 12. -

        -
        
        -ghci> [x*2 | x <- [1..10], x*2 >= 12]
        -[12,14,16,18,20]
        -
        -

        Cool, it works. How about if we wanted all numbers from 50 to 100 whose remainder when divided with the number 7 is 3? Easy.

        -
        
        -ghci> [ x | x <- [50..100], x `mod` 7 == 3]
        -[52,59,66,73,80,87,94] 
        -

        Success! Note that weeding out lists by predicates is also called filtering. We took a list of numbers and we filtered them by the predicate. Now for another example. Let’s say we want a comprehension that replaces each odd number greater than 10 with "BANG!" and each odd number that’s less than 10 with "BOOM!". If a number isn’t odd, we throw it out of our list. For convenience, we’ll put that comprehension inside a function so we can easily reuse it.

        -
        
        -boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x] 
        -

        The last part of the comprehension is the predicate. The function odd returns True on an odd number and False on an even one. The element is included in the list only if all the predicates evaluate to True.

        -
        
        -ghci> boomBangs [7..13]
        -["BOOM!","BOOM!","BANG!","BANG!"] 
        -

        We can include several predicates. If we wanted all numbers from 10 to 20 that are not 13, 15 or 19, we’d do:

        
        -ghci> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19]
        +

        frog If you’ve ever taken a course in +mathematics, you’ve probably run into set comprehensions. +They’re normally used for building more specific sets out of general +sets. A basic comprehension for a set that contains the first ten even +natural numbers is . The part before the pipe is called the output +function, x is the variable, N is the input +set and x <= 10 is the predicate. That means that the +set contains the doubles of all natural numbers that satisfy the +predicate.

        +

        If we wanted to write that in Haskell, we could do something like +take 10 [2,4..]. But what if we didn’t want doubles of the +first 10 natural numbers but some kind of more complex function applied +on them? We could use a list comprehension for that. List comprehensions +are very similar to set comprehensions. We’ll stick to getting the first +10 even numbers for now. The list comprehension we could use is +[x*2 | x <- [1..10]]. x is drawn from +[1..10] and for every element in [1..10] +(which we have bound to x), we get that element, only +doubled. Here’s that comprehension in action.

        +
        ghci> [x*2 | x <- [1..10]]
        +[2,4,6,8,10,12,14,16,18,20]
        +

        As you can see, we get the desired results. Now let’s add a condition +(or a predicate) to that comprehension. Predicates go after the binding +parts and are separated from them by a comma. Let’s say we want only the +elements which, doubled, are greater than or equal to 12.

        +
        ghci> [x*2 | x <- [1..10], x*2 >= 12]
        +[12,14,16,18,20]
        +

        Cool, it works. How about if we wanted all numbers from 50 to 100 +whose remainder when divided with the number 7 is 3? Easy.

        +
        ghci> [ x | x <- [50..100], x `mod` 7 == 3]
        +[52,59,66,73,80,87,94]
        +

        Success! Note that weeding out lists by predicates is also called +filtering. We took a list of numbers and we filtered +them by the predicate. Now for another example. Let’s say we want a +comprehension that replaces each odd number greater than 10 with +"BANG!" and each odd number that’s less than 10 with +"BOOM!". If a number isn’t odd, we throw it out of our +list. For convenience, we’ll put that comprehension inside a function so +we can easily reuse it.

        +
        boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <- xs, odd x]
        +

        The last part of the comprehension is the predicate. The function +odd returns True on an odd number and +False on an even one. The element is included in the list +only if all the predicates evaluate to True.

        +
        ghci> boomBangs [7..13]
        +["BOOM!","BOOM!","BANG!","BANG!"]
        +

        We can include several predicates. If we wanted all numbers from 10 +to 20 that are not 13, 15 or 19, we’d do:

        +
        ghci> [ x | x <- [10..20], x /= 13, x /= 15, x /= 19]
         [10,11,12,14,16,17,18,20]
        -

        Not only can we have multiple predicates in list comprehensions (an element must satisfy all the predicates to be included in the resulting list), we can also draw from several lists. When drawing from several lists, comprehensions produce all combinations of the given lists and then join them by the output function we supply. A list produced by a comprehension that draws from two lists of length 4 will have a length of 16, provided we don’t filter them. If we have two lists, [2,5,10] and [8,10,11] and we want to get the products of all the possible combinations between numbers in those lists, here’s what we’d do.

        -
        
        -ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
        -[16,20,22,40,50,55,80,100,110] 
        -

        As expected, the length of the new list is 9. What if we wanted all possible products that are more than 50?

        -
        
        -ghci> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50]
        -[55,80,100,110] 
        -

        How about a list comprehension that combines a list of adjectives and a list of nouns … for epic hilarity.

        -
        
        -ghci> nouns = ["hobo","frog","pope"]
        +

        Not only can we have multiple predicates in list comprehensions (an +element must satisfy all the predicates to be included in the resulting +list), we can also draw from several lists. When drawing from several +lists, comprehensions produce all combinations of the given lists and +then join them by the output function we supply. A list produced by a +comprehension that draws from two lists of length 4 will have a length +of 16, provided we don’t filter them. If we have two lists, +[2,5,10] and [8,10,11] and we want to get the +products of all the possible combinations between numbers in those +lists, here’s what we’d do.

        +
        ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
        +[16,20,22,40,50,55,80,100,110]
        +

        As expected, the length of the new list is 9. What if we wanted all +possible products that are more than 50?

        +
        ghci> [ x*y | x <- [2,5,10], y <- [8,10,11], x*y > 50]
        +[55,80,100,110]
        +

        How about a list comprehension that combines a list of adjectives and +a list of nouns … for epic hilarity.

        +
        ghci> nouns = ["hobo","frog","pope"]
         ghci> adjectives = ["lazy","grouchy","scheming"]
         ghci> [adjective ++ " " ++ noun | adjective <- adjectives, noun <- nouns]
         ["lazy hobo","lazy frog","lazy pope","grouchy hobo","grouchy frog",
        -"grouchy pope","scheming hobo","scheming frog","scheming pope"] 
        -

        I know! Let’s write our own version of length! We’ll call it length'.

        -
        
        -length' xs = sum [1 | _ <- xs] 
        -

        _ means that we don’t care what we’ll draw from the list anyway so instead of writing a variable name that we’ll never use, we just write _. This function replaces every element of a list with 1 and then sums that up. This means that the resulting sum will be the length of our list.

        -

        Just a friendly reminder: because strings are lists, we can use list comprehensions to process and produce strings. Here’s a function that takes a string and removes everything except uppercase letters from it.

        -
        
        -removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']] 
        -

        -Testing it out: -

        -
        
        -ghci> removeNonUppercase "Hahaha! Ahahaha!"
        +"grouchy pope","scheming hobo","scheming frog","scheming pope"]
        +

        I know! Let’s write our own version of length! We’ll +call it length'.

        +
        length' xs = sum [1 | _ <- xs]
        +

        _ means that we don’t care what we’ll draw from the list +anyway so instead of writing a variable name that we’ll never use, we +just write _. This function replaces every element of a +list with 1 and then sums that up. This means that the +resulting sum will be the length of our list.

        +

        Just a friendly reminder: because strings are lists, we can use list +comprehensions to process and produce strings. Here’s a function that +takes a string and removes everything except uppercase letters from +it.

        +
        removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
        +

        Testing it out:

        +
        ghci> removeNonUppercase "Hahaha! Ahahaha!"
         "HA"
         ghci> removeNonUppercase "IdontLIKEFROGS"
        -"ILIKEFROGS" 
        -

        The predicate here does all the work. It says that the character will be included in the new list only if it’s an element of the list ['A'..'Z']. Nested list comprehensions are also possible if you’re operating on lists that contain lists. A list contains several lists of numbers. Let’s remove all odd numbers without flattening the list.

        -
        
        -ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
        +"ILIKEFROGS"
        +

        The predicate here does all the work. It says that the character will +be included in the new list only if it’s an element of the list +['A'..'Z']. Nested list comprehensions are also possible if +you’re operating on lists that contain lists. A list contains several +lists of numbers. Let’s remove all odd numbers without flattening the +list.

        +
        ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]
         ghci> [ [ x | x <- xs, even x ] | xs <- xxs]
        -[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
        -
        -

        You can write list comprehensions across several lines. So if you’re not in GHCI, it’s better to split longer list comprehensions across multiple lines, especially if they’re nested.

        +[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]
        +

        You can write list comprehensions across several lines. So if you’re +not in GHCI, it’s better to split longer list comprehensions across +multiple lines, especially if they’re nested.

        Tuples

        -tuples -

        In some ways, tuples are like lists — they are a way to store several values into a single value. However, there are a few fundamental differences. A list of numbers is a list of numbers. That’s its type and it doesn’t matter if it has only one number in it or an infinite amount of numbers. Tuples, however, are used when you know exactly how many values you want to combine and its type depends on how many components it has and the types of the components. They are denoted with parentheses and their components are separated by commas.

        -

        Another key difference is that they don’t have to be homogenous. Unlike a list, a tuple can contain a combination of several types.

        -

        Think about how we’d represent a two-dimensional vector in Haskell. One way would be to use a list. That would kind of work. So what if we wanted to put a couple of vectors in a list to represent points of a shape on a two-dimensional plane? We could do something like [[1,2],[8,11],[4,5]]. The problem with that method is that we could also do stuff like [[1,2],[8,11,5],[4,5]], which Haskell has no problem with since it’s still a list of lists with numbers, but it kind of doesn’t make sense. But a tuple of size two (also called a pair) is its own type, which means that a list can’t have a couple of pairs in it and then a triple (a tuple of size three), so let’s use that instead. Instead of surrounding the vectors with square brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, we’d get this error:

        -
        
        -    • Couldn't match expected type ‘(a, b)’
        +tuples
        +

        In some ways, tuples are like lists — they are a way to store several +values into a single value. However, there are a few fundamental +differences. A list of numbers is a list of numbers. That’s its type and +it doesn’t matter if it has only one number in it or an infinite amount +of numbers. Tuples, however, are used when you know exactly how many +values you want to combine and its type depends on how many components +it has and the types of the components. They are denoted with +parentheses and their components are separated by commas.

        +

        Another key difference is that they don’t have to be homogenous. +Unlike a list, a tuple can contain a combination of several types.

        +

        Think about how we’d represent a two-dimensional vector in Haskell. +One way would be to use a list. That would kind of work. So what if we +wanted to put a couple of vectors in a list to represent points of a +shape on a two-dimensional plane? We could do something like +[[1,2],[8,11],[4,5]]. The problem with that method is that +we could also do stuff like [[1,2],[8,11,5],[4,5]], which +Haskell has no problem with since it’s still a list of lists with +numbers, but it kind of doesn’t make sense. But a tuple of size two +(also called a pair) is its own type, which means that a list can’t have +a couple of pairs in it and then a triple (a tuple of size three), so +let’s use that instead. Instead of surrounding the vectors with square +brackets, we use parentheses: [(1,2),(8,11),(4,5)]. What if +we tried to make a shape like [(1,2),(8,11,5),(4,5)]? Well, +we’d get this error:

        +
        • Couldn't match expected type ‘(a, b)’
                           with actual type ‘(a0, b0, c0)’
             • In the expression: (8, 11, 5)
               In the expression: [(1, 2), (8, 11, 5), (4, 5)]
               In an equation for ‘it’: it = [(1, 2), (8, 11, 5), (4, 5)]
             • Relevant bindings include
        -        it :: [(a, b)] (bound at <interactive>:1:1)
        -
        -

        It’s telling us that we tried to use a pair and a triple in the same list, which is not supposed to happen. You also couldn’t make a list like [(1,2),("One",2)] because the first element of the list is a pair of numbers and the second element is a pair consisting of a string and a number. Tuples can also be used to represent a wide variety of data. For instance, if we wanted to represent someone’s name and age in Haskell, we could use a triple: ("Christopher", "Walken", 55). As seen in this example, tuples can also contain lists.

        -

        Use tuples when you know in advance how many components some piece of data should have. Tuples are much more rigid because each different size of tuple is its own type, so you can’t write a general function to append an element to a tuple — you’d have to write a function for appending to a pair, one function for appending to a triple, one function for appending to a 4-tuple, etc.

        -

        While there are singleton lists, there’s no such thing as a singleton tuple. It doesn’t really make much sense when you think about it. A singleton tuple would just be the value it contains and as such would have no benefit to us.

        -

        Like lists, tuples can be compared with each other if their components can be compared. Only you can’t compare two tuples of different sizes, whereas you can compare two lists of different sizes. Two useful functions that operate on pairs:

        -

        fst takes a pair and returns its first component.

        -
        
        -ghci> fst (8,11)
        +        it :: [(a, b)] (bound at <interactive>:1:1)
        +

        It’s telling us that we tried to use a pair and a triple in the same +list, which is not supposed to happen. You also couldn’t make a list +like [(1,2),("One",2)] because the first element of the +list is a pair of numbers and the second element is a pair consisting of +a string and a number. Tuples can also be used to represent a wide +variety of data. For instance, if we wanted to represent someone’s name +and age in Haskell, we could use a triple: +("Christopher", "Walken", 55). As seen in this example, +tuples can also contain lists.

        +

        Use tuples when you know in advance how many components some piece of +data should have. Tuples are much more rigid because each different size +of tuple is its own type, so you can’t write a general function to +append an element to a tuple — you’d have to write a function for +appending to a pair, one function for appending to a triple, one +function for appending to a 4-tuple, etc.

        +

        While there are singleton lists, there’s no such thing as a singleton +tuple. It doesn’t really make much sense when you think about it. A +singleton tuple would just be the value it contains and as such would +have no benefit to us.

        +

        Like lists, tuples can be compared with each other if their +components can be compared. Only you can’t compare two tuples of +different sizes, whereas you can compare two lists of different sizes. +Two useful functions that operate on pairs:

        +

        fst takes a pair and returns its +first component.

        +
        ghci> fst (8,11)
         8
         ghci> fst ("Wow", False)
         "Wow"
        -

        snd takes a pair and returns its second component. Surprise!

        -
        
        -ghci> snd (8,11)
        +

        snd takes a pair and returns its +second component. Surprise!

        +
        ghci> snd (8,11)
         11
         ghci> snd ("Wow", False)
         False
        -

        Note: these functions operate only on pairs. They won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting data from tuples in different ways a bit later.

        -

        A cool function that produces a list of pairs: zip. It takes two lists and then zips them together into one list by joining the matching elements into pairs. It’s a really simple function but it has loads of uses. It’s especially useful for when you want to combine two lists in a way or traverse two lists simultaneously. Here’s a demonstration.

        -
        
        -ghci> zip [1,2,3,4,5] [5,5,5,5,5]
        +
        +

        Note: these functions operate only on pairs. They +won’t work on triples, 4-tuples, 5-tuples, etc. We’ll go over extracting +data from tuples in different ways a bit later.

        +
        +

        A cool function that produces a list of pairs: zip. It takes two lists and then zips them +together into one list by joining the matching elements into pairs. It’s +a really simple function but it has loads of uses. It’s especially +useful for when you want to combine two lists in a way or traverse two +lists simultaneously. Here’s a demonstration.

        +
        ghci> zip [1,2,3,4,5] [5,5,5,5,5]
         [(1,5),(2,5),(3,5),(4,5),(5,5)]
         ghci> zip [1 .. 5] ["one", "two", "three", "four", "five"]
        -[(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
        -
        -

        It pairs up the elements and produces a new list. The first element goes with the first, the second with the second, etc. Notice that because pairs can have different types in them, zip can take two lists that contain different types and zip them up. What happens if the lengths of the lists don’t match?

        -
        
        -ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["im","a","turtle"]
        -[(5,"im"),(3,"a"),(2,"turtle")]
        -
        -

        The longer list simply gets cut off to match the length of the shorter one. Because Haskell is lazy, we can zip finite lists with infinite lists:

        -
        
        -ghci> zip [1..] ["apple", "orange", "cherry", "mango"]
        -[(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
        -
        -look at meee -

        Here’s a problem that combines tuples and list comprehensions: which right triangle that has integers for all sides and all sides equal to or smaller than 10 has a perimeter of 24? First, let’s try generating all triangles with sides equal to or smaller than 10:

        -
        ghci> triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ] 
        -

        We’re just drawing from three lists and our output function is combining them into a triple. If you evaluate that by typing out triangles in GHCI, you’ll get a list of all possible triangles with sides under or equal to 10. Next, we’ll add a condition that they all have to be right triangles. We’ll also modify this function by taking into consideration that side b isn’t larger than the hypothenuse and that side a isn’t larger than side b.

        -
        
        -ghci> rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2] 
        -

        We’re almost done. Now, we just modify the function by saying that we want the ones where the perimeter is 24.

        -
        
        -ghci> rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
        +[(1,"one"),(2,"two"),(3,"three"),(4,"four"),(5,"five")]
        +

        It pairs up the elements and produces a new list. The first element +goes with the first, the second with the second, etc. Notice that +because pairs can have different types in them, zip can +take two lists that contain different types and zip them up. What +happens if the lengths of the lists don’t match?

        +
        ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["im","a","turtle"]
        +[(5,"im"),(3,"a"),(2,"turtle")]
        +

        The longer list simply gets cut off to match the length of the +shorter one. Because Haskell is lazy, we can zip finite lists with +infinite lists:

        +
        ghci> zip [1..] ["apple", "orange", "cherry", "mango"]
        +[(1,"apple"),(2,"orange"),(3,"cherry"),(4,"mango")]
        +look at meee +

        Here’s a problem that combines tuples and list comprehensions: which +right triangle that has integers for all sides and all sides equal to or +smaller than 10 has a perimeter of 24? First, let’s try generating all +triangles with sides equal to or smaller than 10:

        +
        ghci> triangles = [ (a,b,c) | c <- [1..10], b <- [1..10], a <- [1..10] ]
        +

        We’re just drawing from three lists and our output function is +combining them into a triple. If you evaluate that by typing out +triangles in GHCI, you’ll get a list of all possible +triangles with sides under or equal to 10. Next, we’ll add a condition +that they all have to be right triangles. We’ll also modify this +function by taking into consideration that side b isn’t larger than the +hypothenuse and that side a isn’t larger than side b.

        +
        ghci> rightTriangles = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2]
        +

        We’re almost done. Now, we just modify the function by saying that we +want the ones where the perimeter is 24.

        +
        ghci> rightTriangles' = [ (a,b,c) | c <- [1..10], b <- [1..c], a <- [1..b], a^2 + b^2 == c^2, a+b+c == 24]
         ghci> rightTriangles'
        -[(6,8,10)]
        -
        -

        And there’s our answer! This is a common pattern in functional programming. You take a starting set of solutions and then you apply transformations to those solutions and filter them until you get the right ones.

        +[(6,8,10)]
        +

        And there’s our answer! This is a common pattern in functional +programming. You take a starting set of solutions and then you apply +transformations to those solutions and filter them until you get the +right ones.

        • diff --git a/docs/syntax-in-functions.html b/docs/syntax-in-functions.html index dbbd4da..d538e02 100644 --- a/docs/syntax-in-functions.html +++ b/docs/syntax-in-functions.html @@ -31,349 +31,539 @@
        -

        Syntax in Functions

        +

        Syntax in +Functions

        Pattern matching

        -four! -

        This chapter will cover some of Haskell’s cool syntactic constructs and we’ll start with pattern matching. Pattern matching consists of specifying patterns to which some data should conform and then checking to see if it does and deconstructing the data according to those patterns.

        -

        When defining functions, you can define separate function bodies for different patterns. This leads to really neat code that’s simple and readable. You can pattern match on any data type — numbers, characters, lists, tuples, etc. Let’s make a really trivial function that checks if the number we supplied to it is a seven or not.

        -
        
        -lucky :: (Integral a) => a -> String
        +four!
        +

        This chapter will cover some of Haskell’s cool syntactic constructs +and we’ll start with pattern matching. Pattern matching consists of +specifying patterns to which some data should conform and then checking +to see if it does and deconstructing the data according to those +patterns.

        +

        When defining functions, you can define separate function bodies for +different patterns. This leads to really neat code that’s simple and +readable. You can pattern match on any data type — numbers, characters, +lists, tuples, etc. Let’s make a really trivial function that checks if +the number we supplied to it is a seven or not.

        +
        lucky :: (Integral a) => a -> String
         lucky 7 = "LUCKY NUMBER SEVEN!"
        -lucky x = "Sorry, you're out of luck, pal!" 
        -

        When you call lucky, the patterns will be checked from top to bottom and when it conforms to a pattern, the corresponding function body will be used. The only way a number can conform to the first pattern here is if it is 7. If it’s not, it falls through to the second pattern, which matches anything and binds it to x. This function could have also been implemented by using an if statement. But what if we wanted a function that says the numbers from 1 to 5 and says "Not between 1 and 5" for any other number? Without pattern matching, we’d have to make a pretty convoluted if then else tree. However, with it:

        -
        
        -sayMe :: (Integral a) => a -> String
        +lucky x = "Sorry, you're out of luck, pal!"
        +

        When you call lucky, the patterns will be checked from +top to bottom and when it conforms to a pattern, the corresponding +function body will be used. The only way a number can conform to the +first pattern here is if it is 7. If it’s not, it falls through to the +second pattern, which matches anything and binds it to x. +This function could have also been implemented by using an if statement. +But what if we wanted a function that says the numbers from 1 to 5 and +says "Not between 1 and 5" for any other number? Without +pattern matching, we’d have to make a pretty convoluted if then else +tree. However, with it:

        +
        sayMe :: (Integral a) => a -> String
         sayMe 1 = "One!"
         sayMe 2 = "Two!"
         sayMe 3 = "Three!"
         sayMe 4 = "Four!"
         sayMe 5 = "Five!"
        -sayMe x = "Not between 1 and 5"
        -
        -

        Note that if we moved the last pattern (the catch-all one) to the top, it would always say "Not between 1 and 5", because it would catch all the numbers and they wouldn’t have a chance to fall through and be checked for any other patterns.

        -

        Remember the factorial function we implemented previously? We defined the factorial of a number n as product [1..n]. We can also define a factorial function recursively, the way it is usually defined in mathematics. We start by saying that the factorial of 0 is 1. Then we state that the factorial of any positive integer is that integer multiplied by the factorial of its predecessor. Here’s how that looks like translated in Haskell terms.

        -
        
        -factorial :: (Integral a) => a -> a
        +sayMe x = "Not between 1 and 5"
        +

        Note that if we moved the last pattern (the catch-all one) to the +top, it would always say "Not between 1 and 5", because it +would catch all the numbers and they wouldn’t have a chance to fall +through and be checked for any other patterns.

        +

        Remember the factorial function we implemented previously? We defined +the factorial of a number n as product [1..n]. +We can also define a factorial function recursively, the way it +is usually defined in mathematics. We start by saying that the factorial +of 0 is 1. Then we state that the factorial of any positive integer is +that integer multiplied by the factorial of its predecessor. Here’s how +that looks like translated in Haskell terms.

        +
        factorial :: (Integral a) => a -> a
         factorial 0 = 1
        -factorial n = n * factorial (n - 1)
        -
        -

        -This is the first time we’ve defined a function recursively. Recursion is important in Haskell and we’ll take a closer look at it later. But in a nutshell, this is what happens if we try to get the factorial of, say, 3. It tries to compute 3 * factorial 2. The factorial of 2 is 2 * factorial 1, so for now we have 3 * (2 * factorial 1). factorial 1 is 1 * factorial 0, so we have 3 * (2 * (1 * factorial 0)). Now here comes the trick — we’ve defined the factorial of 0 to be just 1 and because it encounters that pattern before the catch-all one, it just returns 1. So the final result is equivalent to 3 * (2 * (1 * 1)). Had we written the second pattern on top of the first one, it would catch all numbers, including 0 and our calculation would never terminate. That’s why order is important when specifying patterns and it’s always best to specify the most specific ones first and then the more general ones later.

        -

        -Pattern matching can also fail. If we define a function like this: -

        -
        
        -charName :: Char -> String
        +factorial n = n * factorial (n - 1)
        +

        This is the first time we’ve defined a function recursively. +Recursion is important in Haskell and we’ll take a closer look at it +later. But in a nutshell, this is what happens if we try to get the +factorial of, say, 3. It tries to compute 3 * factorial 2. +The factorial of 2 is 2 * factorial 1, so for now we have +3 * (2 * factorial 1). factorial 1 is +1 * factorial 0, so we have +3 * (2 * (1 * factorial 0)). Now here comes the trick — +we’ve defined the factorial of 0 to be just 1 and because it encounters +that pattern before the catch-all one, it just returns 1. So the final +result is equivalent to 3 * (2 * (1 * 1)). Had we written +the second pattern on top of the first one, it would catch all numbers, +including 0 and our calculation would never terminate. That’s why order +is important when specifying patterns and it’s always best to specify +the most specific ones first and then the more general ones later.

        +

        Pattern matching can also fail. If we define a function like +this:

        +
        charName :: Char -> String
         charName 'a' = "Albert"
         charName 'b' = "Broseph"
        -charName 'c' = "Cecil"
        -
        -

        and then try to call it with an input that we didn’t expect, this is what happens:

        -
        
        -ghci> charName 'a'
        +charName 'c' = "Cecil"
        +

        and then try to call it with an input that we didn’t expect, this is +what happens:

        +
        ghci> charName 'a'
         "Albert"
         ghci> charName 'b'
         "Broseph"
         ghci> charName 'h'
        -"*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in function charName
        -
        -

        -It complains that we have non-exhaustive patterns, and rightfully so. When making patterns, we should always include a catch-all pattern so that our program doesn’t crash if we get some unexpected input. -

        -

        Pattern matching can also be used on tuples. What if we wanted to make a function that takes two vectors in a 2D space (that are in the form of pairs) and adds them together? To add together two vectors, we add their x components separately and then their y components separately. Here’s how we would have done it if we didn’t know about pattern matching:

        -
        
        -addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
        -addVectors a b = (fst a + fst b, snd a + snd b)
        -
        -

        Well, that works, but there’s a better way to do it. Let’s modify the function so that it uses pattern matching.

        -
        
        -addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
        -addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
        -
        -

        There we go! Much better. Note that this is already a catch-all pattern. The type of addVectors (in both cases) is addVectors :: (Num a) => (a, a) -> (a, a) - > (a, a), so we are guaranteed to get two pairs as parameters.

        -

        fst and snd extract the components of pairs. But what about triples? Well, there are no provided functions that do that but we can make our own.

        -
        
        -first :: (a, b, c) -> a
        +"*** Exception: tut.hs:(53,0)-(55,21): Non-exhaustive patterns in function charName
        +

        It complains that we have non-exhaustive patterns, and rightfully so. +When making patterns, we should always include a catch-all pattern so +that our program doesn’t crash if we get some unexpected input.

        +

        Pattern matching can also be used on tuples. What if we wanted to +make a function that takes two vectors in a 2D space (that are in the +form of pairs) and adds them together? To add together two vectors, we +add their x components separately and then their y components +separately. Here’s how we would have done it if we didn’t know about +pattern matching:

        +
        addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
        +addVectors a b = (fst a + fst b, snd a + snd b)
        +

        Well, that works, but there’s a better way to do it. Let’s modify the +function so that it uses pattern matching.

        +
        addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)
        +addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
        +

        There we go! Much better. Note that this is already a catch-all +pattern. The type of addVectors (in both cases) is +addVectors :: (Num a) => (a, a) -> (a, a) - > (a, a), +so we are guaranteed to get two pairs as parameters.

        +

        fst and snd extract the components of +pairs. But what about triples? Well, there are no provided functions +that do that but we can make our own.

        +
        first :: (a, b, c) -> a
         first (x, _, _) = x
         
         second :: (a, b, c) -> b
         second (_, y, _) = y
         
         third :: (a, b, c) -> c
        -third (_, _, z) = z
        -
        -

        The _ means the same thing as it does in list comprehensions. It means that we really don’t care what that part is, so we just write a _.

        -

        Which reminds me, you can also pattern match in list comprehensions. Check this out:

        -
        
        -ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]
        +third (_, _, z) = z
        +

        The _ means the same thing as it does in list +comprehensions. It means that we really don’t care what that part is, so +we just write a _.

        +

        Which reminds me, you can also pattern match in list comprehensions. +Check this out:

        +
        ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]
         ghci> [a+b | (a,b) <- xs]
        -[4,7,6,8,11,4] 
        -

        -Should a pattern match fail, it will just move on to the next element. -

        -

        Lists themselves can also be used in pattern matching. You can match with the empty list [] or any pattern that involves : and the empty list. But since [1,2,3] is just syntactic sugar for 1:2:3:[], you can also use the former pattern. A pattern like x:xs will bind the head of the list to x and the rest of it to xs, even if there’s only one element so xs ends up being an empty list.

        -

        Note: The x:xs pattern is used a lot, especially with recursive functions. But patterns that have : in them only match against lists of length 1 or more.

        -

        If you want to bind, say, the first three elements to variables and the rest of the list to another variable, you can use something like x:y:z:zs. It will only match against lists that have three elements or more.

        -

        Now that we know how to pattern match against list, let’s make our own implementation of the head function.

        -
        
        -head' :: [a] -> a
        +[4,7,6,8,11,4]
        +

        Should a pattern match fail, it will just move on to the next +element.

        +

        Lists themselves can also be used in pattern matching. You can match +with the empty list [] or any pattern that involves +: and the empty list. But since [1,2,3] is +just syntactic sugar for 1:2:3:[], you can also use the +former pattern. A pattern like x:xs will bind the head of +the list to x and the rest of it to xs, even +if there’s only one element so xs ends up being an empty +list.

        +
        +

        Note: The x:xs pattern is used a lot, +especially with recursive functions. But patterns that have +: in them only match against lists of length 1 or more.

        +
        +

        If you want to bind, say, the first three elements to variables and +the rest of the list to another variable, you can use something like +x:y:z:zs. It will only match against lists that have three +elements or more.

        +

        Now that we know how to pattern match against list, let’s make our +own implementation of the head function.

        +
        head' :: [a] -> a
         head' [] = error "Can't call head on an empty list, dummy!"
        -head' (x:_) = x
        -
        +head' (x:_) = x

        Checking if it works:

        -
        
        -ghci> head' [4,5,6]
        +
        ghci> head' [4,5,6]
         4
         ghci> head' "Hello"
        -'H'
        -
        -

        -Nice! Notice that if you want to bind to several variables (even if one of them is just _ and doesn’t actually bind at all), we have to surround them in parentheses. Also notice the error function that we used. It takes a string and generates a runtime error, using that string as information about what kind of error occurred. It causes the program to crash, so it’s not good to use it too much. But calling head on an empty list doesn’t make sense. -

        -

        Let’s make a trivial function that tells us some of the first elements of the list in (in)convenient English form.

        -
        
        -tell :: (Show a) => [a] -> String
        +'H'
        +

        Nice! Notice that if you want to bind to several variables (even if +one of them is just _ and doesn’t actually bind at all), we +have to surround them in parentheses. Also notice the error +function that we used. It takes a string and generates a runtime error, +using that string as information about what kind of error occurred. It +causes the program to crash, so it’s not good to use it too much. But +calling head on an empty list doesn’t make sense.

        +

        Let’s make a trivial function that tells us some of the first +elements of the list in (in)convenient English form.

        +
        tell :: (Show a) => [a] -> String
         tell [] = "The list is empty"
         tell (x:[]) = "The list has one element: " ++ show x
         tell (x:y:[]) = "The list has two elements: " ++ show x ++ " and " ++ show y
        -tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
        -
        -

        This function is safe because it takes care of the empty list, a singleton list, a list with two elements and a list with more than two elements. Note that (x:[]) and (x:y:[]) could be rewritten as [x] and [x,y] (because its syntactic sugar, we don’t need the parentheses). We can’t rewrite (x:y:_) with square brackets because it matches any list of length 2 or more.

        -

        We already implemented our own length function using list comprehension. Now we’ll do it by using pattern matching and a little recursion:

        -
        
        -length' :: (Num b) => [a] -> b
        +tell (x:y:_) = "This list is long. The first two elements are: " ++ show x ++ " and " ++ show y
        +

        This function is safe because it takes care of the empty list, a +singleton list, a list with two elements and a list with more than two +elements. Note that (x:[]) and (x:y:[]) could +be rewritten as [x] and [x,y] (because its +syntactic sugar, we don’t need the parentheses). We can’t rewrite +(x:y:_) with square brackets because it matches any list of +length 2 or more.

        +

        We already implemented our own length function using +list comprehension. Now we’ll do it by using pattern matching and a +little recursion:

        +
        length' :: (Num b) => [a] -> b
         length' [] = 0
         length' (_:xs) = 1 + length' xs
        -

        This is similar to the factorial function we wrote earlier. First we defined the result of a known input — the empty list. This is also known as the edge condition. Then in the second pattern we take the list apart by splitting it into a head and a tail. We say that the length is equal to 1 plus the length of the tail. We use _ to match the head because we don’t actually care what it is. Also note that we’ve taken care of all possible patterns of a list. The first pattern matches an empty list and the second one matches anything that isn’t an empty list.

        -

        Let’s see what happens if we call length' on "ham". First, it will check if it’s an empty list. Because it isn’t, it falls through to the second pattern. It matches on the second pattern and there it says that the length is 1 + length' "am", because we broke it into a head and a tail and discarded the head. O-kay. The length' of "am" is, similarly, 1 + length' "m". So right now we have 1 + (1 + length' "m"). length' "m" is 1 + length' "" (could also be written as 1 + length' []). And we’ve defined length' [] to be 0. So in the end we have 1 + (1 + (1 + 0)).

        -

        Let’s implement sum. We know that the sum of an empty list is 0. We write that down as a pattern. And we also know that the sum of a list is the head plus the sum of the rest of the list. So if we write that down, we get:

        -
        
        -sum' :: (Num a) => [a] -> a
        +

        This is similar to the factorial function we wrote earlier. First we +defined the result of a known input — the empty list. This is also known +as the edge condition. Then in the second pattern we take the list apart +by splitting it into a head and a tail. We say that the length is equal +to 1 plus the length of the tail. We use _ to match the +head because we don’t actually care what it is. Also note that we’ve +taken care of all possible patterns of a list. The first pattern matches +an empty list and the second one matches anything that isn’t an empty +list.

        +

        Let’s see what happens if we call length' on +"ham". First, it will check if it’s an empty list. Because +it isn’t, it falls through to the second pattern. It matches on the +second pattern and there it says that the length is +1 + length' "am", because we broke it into a head and a +tail and discarded the head. O-kay. The length' of +"am" is, similarly, 1 + length' "m". So right +now we have 1 + (1 + length' "m"). length' "m" +is 1 + length' "" (could also be written as +1 + length' []). And we’ve defined length' [] +to be 0. So in the end we have +1 + (1 + (1 + 0)).

        +

        Let’s implement sum. We know that the sum of an empty +list is 0. We write that down as a pattern. And we also know that the +sum of a list is the head plus the sum of the rest of the list. So if we +write that down, we get:

        +
        sum' :: (Num a) => [a] -> a
         sum' [] = 0
        -sum' (x:xs) = x + sum' xs
        -
        -

        There’s also a thing called as patterns. Those are a handy way of breaking something up according to a pattern and binding it to names whilst still keeping a reference to the whole thing. You do that by putting a name and an @ in front of a pattern. For instance, the pattern xs@(x:y:ys). This pattern will match exactly the same thing as x:y:ys but you can easily get the whole list via xs instead of repeating yourself by typing out x:y:ys in the function body again. Here’s a quick and dirty example:

        -
        
        -capital :: String -> String
        +sum' (x:xs) = x + sum' xs
        +

        There’s also a thing called as patterns. Those are a handy +way of breaking something up according to a pattern and binding it to +names whilst still keeping a reference to the whole thing. You do that +by putting a name and an @ in front of a pattern. For +instance, the pattern xs@(x:y:ys). This pattern will match +exactly the same thing as x:y:ys but you can easily get the +whole list via xs instead of repeating yourself by typing +out x:y:ys in the function body again. Here’s a quick and +dirty example:

        +
        capital :: String -> String
         capital "" = "Empty string, whoops!"
        -capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
        -
        -
        
        -ghci> capital "Dracula"
        -"The first letter of Dracula is D"
        -
        -

        Normally we use as patterns to avoid repeating ourselves when matching against a bigger pattern when we have to use the whole thing again in the function body.

        -

        One more thing — you can’t use ++ in pattern matches. If you tried to pattern match against (xs ++ ys), what would be in the first and what would be in the second list? It doesn’t make much sense. It would make sense to match stuff against (xs ++ [x,y,z]) or just (xs ++ [x]), but because of the nature of lists, you can’t do that.

        +capital all@(x:xs) = "The first letter of " ++ all ++ " is " ++ [x]
        +
        ghci> capital "Dracula"
        +"The first letter of Dracula is D"
        +

        Normally we use as patterns to avoid repeating ourselves when +matching against a bigger pattern when we have to use the whole thing +again in the function body.

        +

        One more thing — you can’t use ++ in pattern matches. If +you tried to pattern match against (xs ++ ys), what would +be in the first and what would be in the second list? It doesn’t make +much sense. It would make sense to match stuff against +(xs ++ [x,y,z]) or just (xs ++ [x]), but +because of the nature of lists, you can’t do that.

        Guards, guards!

        -guards -

        Whereas patterns are a way of making sure a value conforms to some form and deconstructing it, guards are a way of testing whether some property of a value (or several of them) are true or false. That sounds a lot like an if statement and it’s very similar. The thing is that guards are a lot more readable when you have several conditions and they play really nicely with patterns.

        -

        Instead of explaining their syntax, let’s just dive in and make a function using guards. We’re going to make a simple function that responds differently depending on the density given. Density (or specific mass) is a substance’s mass per unit of volume (here, grams per liter). If a substance has a density of less than 1.2, it will float in air, as 1.2g/L is the density of air. If it has more than 1000g/L (the density of water), it will sink in water. Between are things (like people, usually) that will neither float away nor sink in water. - - So here’s the function (we won’t be calculating density right now, this function just gets a density and responds)

        -
        
        -densityTell :: (RealFloat a) => a -> String
        +guards
        +

        Whereas patterns are a way of making sure a value conforms to some +form and deconstructing it, guards are a way of testing whether some +property of a value (or several of them) are true or false. That sounds +a lot like an if statement and it’s very similar. The thing is that +guards are a lot more readable when you have several conditions and they +play really nicely with patterns.

        +

        Instead of explaining their syntax, let’s just dive in and make a +function using guards. We’re going to make a simple function that +responds differently depending on the density given. Density +(or specific mass) is a substance’s mass per unit of volume (here, grams +per liter). If a substance has a density of less than 1.2, it will float +in air, as 1.2g/L is the density of air. If it has more than 1000g/L +(the density of water), it will sink in water. Between are things (like +people, usually) that will neither float away nor sink in water. So +here’s the function (we won’t be calculating density right now, this +function just gets a density and responds)

        +
        densityTell :: (RealFloat a) => a -> String
         densityTell density
             | density < 1.2 = "Wow! You're going for a ride in the sky!"
             | density <= 1000.0 = "Have fun swimming, but watch out for sharks!"
        -    | otherwise   = "If it's sink or swim, you're going to sink."
        -
        -

        Guards are indicated by pipes that follow a function’s name and its parameters. Usually, they’re indented a bit to the right and lined up. A guard is basically a boolean expression. If it evaluates to True, then the corresponding function body is used. If it evaluates to False, checking drops through to the next guard and so on. If we call this function with 24.3, it will first check if that’s smaller than or equal to 1.2. Because it isn’t, it falls through to the next guard. The check is carried out with the second guard and because 24.3 is less than 1000.0, the second string is returned.

        -

        This is very reminiscent of a big if else tree in imperative languages, only this is far better and more readable. While big if else trees are usually frowned upon, sometimes a problem is defined in such a discrete way that you can’t get around them. Guards are a very nice alternative for this.

        -

        Many times, the last guard is otherwise. otherwise is defined simply as otherwise = True and catches everything. This is very similar to patterns, only they check if the input satisfies a pattern but guards check for boolean conditions. If all the guards of a function evaluate to False (and we haven’t provided an otherwise catch-all guard), evaluation falls through to the next pattern. That’s how patterns and guards play nicely together. If no suitable guards or patterns are found, an error is thrown.

        -

        Of course we can use guards with functions that take as many parameters as we want. Instead of having the user calculate the density of the substance on their own before calling the function, let’s modify this function so that it takes a mass (in grams) and volume (in liters).

        -
        
        -densityTell :: (RealFloat a) => a -> a -> String
        +    | otherwise   = "If it's sink or swim, you're going to sink."
        +

        Guards are indicated by pipes that follow a function’s name and its +parameters. Usually, they’re indented a bit to the right and lined up. A +guard is basically a boolean expression. If it evaluates to +True, then the corresponding function body is used. If it +evaluates to False, checking drops through to the next +guard and so on. If we call this function with 24.3, it +will first check if that’s smaller than or equal to 1.2. +Because it isn’t, it falls through to the next guard. The check is +carried out with the second guard and because 24.3 is less +than 1000.0, the second string is returned.

        +

        This is very reminiscent of a big if else tree in imperative +languages, only this is far better and more readable. While big if else +trees are usually frowned upon, sometimes a problem is defined in such a +discrete way that you can’t get around them. Guards are a very nice +alternative for this.

        +

        Many times, the last guard is otherwise. +otherwise is defined simply as +otherwise = True and catches everything. This is very +similar to patterns, only they check if the input satisfies a pattern +but guards check for boolean conditions. If all the guards of a function +evaluate to False (and we haven’t provided an +otherwise catch-all guard), evaluation falls through to the +next pattern. That’s how patterns and guards play +nicely together. If no suitable guards or patterns are found, an error +is thrown.

        +

        Of course we can use guards with functions that take as many +parameters as we want. Instead of having the user calculate the density +of the substance on their own before calling the function, let’s modify +this function so that it takes a mass (in grams) and volume (in +liters).

        +
        densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
             | mass / volume <= 1000.0 = "Have fun swimming, but watch out for sharks!"
        -    | otherwise   = "If it's sink or swim, you're going to sink."
        -
        + | otherwise = "If it's sink or swim, you're going to sink."

        Let’s see if cat food will float …

        -
        
        -ghci> densityTell 400 1
        -"Have fun swimming, but watch out for sharks!"
        -
        -

        Looks like it will! At least until it dissolves into the pool… Yuck!

        -

        Note that there’s no = right after the function name and its parameters, before the first guard. Many newbies get syntax errors because they sometimes put it there.

        -

        Another very simple example: let’s implement our own max function. If you remember, it takes two things that can be compared and returns the larger of them.

        -
        
        -max' :: (Ord a) => a -> a -> a
        +
        ghci> densityTell 400 1
        +"Have fun swimming, but watch out for sharks!"
        +

        Looks like it will! At least until it dissolves into the pool… +Yuck!

        +

        Note that there’s no = right after the function name and +its parameters, before the first guard. Many newbies get syntax errors +because they sometimes put it there.

        +

        Another very simple example: let’s implement our own max +function. If you remember, it takes two things that can be compared and +returns the larger of them.

        +
        max' :: (Ord a) => a -> a -> a
         max' a b
             | a > b     = a
        -    | otherwise = b
        -
        -

        Guards can also be written inline, although I’d advise against that because it’s less readable, even for very short functions. But to demonstrate, we could write max' like this:

        -
        
        -max' :: (Ord a) => a -> a -> a
        -max' a b | a > b = a | otherwise = b
        -
        - -

        Ugh! Not very readable at all! Moving on: let’s implement our own compare by using guards.

        -
        
        -myCompare :: (Ord a) => a -> a -> Ordering
        +    | otherwise = b
        +

        Guards can also be written inline, although I’d advise against that +because it’s less readable, even for very short functions. But to +demonstrate, we could write max' like this:

        +
        max' :: (Ord a) => a -> a -> a
        +max' a b | a > b = a | otherwise = b
        +

        Ugh! Not very readable at all! Moving on: let’s implement our own +compare by using guards.

        +
        myCompare :: (Ord a) => a -> a -> Ordering
         a `myCompare` b
             | a > b     = GT
             | a == b    = EQ
        -    | otherwise = LT
        -
        -
        
        -ghci> 3 `myCompare` 2
        -GT
        -
        -

        Note: Not only can we call functions as infix with backticks, we can also define them using backticks. Sometimes it’s easier to read that way.

        + | otherwise = LT
        +
        ghci> 3 `myCompare` 2
        +GT
        +
        +

        Note: Not only can we call functions as infix with +backticks, we can also define them using backticks. Sometimes it’s +easier to read that way.

        +

        Where!?

        -

        In the previous section, we defined a density calculator function and responder like this:

        -
        
        -densityTell :: (RealFloat a) => a -> a -> String
        +

        In the previous section, we defined a density calculator function and +responder like this:

        +
        densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | mass / volume < 1.2 = "Wow! You're going for a ride in the sky!"
             | mass / volume <= 1000.0 = "Have fun swimming, but watch out for sharks!"
        -    | otherwise   = "If it's sink or swim, you're going to sink."
        -
        -

        Notice that we repeat ourselves here two times. We repeat ourselves two times. Repeating yourself (two times) while programming is about as desirable as getting kicked inna head. Since we repeat the same expression twice, it would be ideal if we could calculate it once, bind it to a name and then use that name instead of the expression. Well, we can modify our function like this:

        -
        
        -densityTell :: (RealFloat a) => a -> a -> String
        +    | otherwise   = "If it's sink or swim, you're going to sink."
        +

        Notice that we repeat ourselves here two times. We repeat ourselves +two times. Repeating yourself (two times) while programming is about as +desirable as getting kicked inna head. Since we repeat the same +expression twice, it would be ideal if we could calculate it once, bind +it to a name and then use that name instead of the expression. Well, we +can modify our function like this:

        +
        densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | density < 1.2 = "Wow! You're going for a ride in the sky!"
             | density <= 1000.0 = "Have fun swimming, but watch out for sharks!"
             | otherwise   = "If it's sink or swim, you're going to sink."
        -    where density = mass / volume
        -
        -

        We put the keyword where after the guards (usually it’s best to indent it as much as the pipes are indented) and then we define several names or functions. These names are visible across the guards and give us the advantage of not having to repeat ourselves. If we decide that we want to calculate density a bit differently, we only have to change it once. It also improves readability by giving names to things and can make our programs faster since stuff like our density variable here is calculated only once. We could go a bit overboard and present our function like this:

        -
        
        -densityTell :: (RealFloat a) => a -> a -> String
        +    where density = mass / volume
        +

        We put the keyword where after the guards (usually it’s +best to indent it as much as the pipes are indented) and then we define +several names or functions. These names are visible across the guards +and give us the advantage of not having to repeat ourselves. If we +decide that we want to calculate density a bit differently, we only have +to change it once. It also improves readability by giving names to +things and can make our programs faster since stuff like our +density variable here is calculated only once. We could go +a bit overboard and present our function like this:

        +
        densityTell :: (RealFloat a) => a -> a -> String
         densityTell mass volume
             | density < air = "Wow! You're going for a ride in the sky!"
             | density <= water = "Have fun swimming, but watch out for sharks!"
             | otherwise   = "If it's sink or swim, you're going to sink."
             where density = mass / volume
                   air = 1.2
        -          water = 1000.0
        -
        -

        The names we define in the where section of a function are only visible to that function, so we don’t have to worry about them polluting the namespace of other functions. Notice that all the names are aligned at a single column. If we don’t align them nice and proper, Haskell gets confused because then it doesn’t know they’re all part of the same block.

        -

        where bindings aren’t shared across function bodies of different patterns. If you want several patterns of one function to access some shared name, you have to define it globally.

        -

        You can also use where bindings to pattern match! We could have rewritten the where section of our previous function as:

        -
        
        -    ...
        +          water = 1000.0
        +

        The names we define in the where section of a function are only +visible to that function, so we don’t have to worry about them polluting +the namespace of other functions. Notice that all the names are aligned +at a single column. If we don’t align them nice and proper, Haskell gets +confused because then it doesn’t know they’re all part of the same +block.

        +

        where bindings aren’t shared across function bodies of +different patterns. If you want several patterns of one function to +access some shared name, you have to define it globally.

        +

        You can also use where bindings to pattern match! We +could have rewritten the where section of our previous function as:

        +
        ...
             where density = mass / volume
        -          (air, water) = (1.2, 1000.0)
        -
        -

        Let’s make another fairly trivial function where we get a first and a last name and give someone back their initials.

        -
        
        -initials :: String -> String -> String
        +          (air, water) = (1.2, 1000.0)
        +

        Let’s make another fairly trivial function where we get a first and a +last name and give someone back their initials.

        +
        initials :: String -> String -> String
         initials firstname lastname = [f] ++ ". " ++ [l] ++ "."
             where (f:_) = firstname
        -          (l:_) = lastname
        -
        -

        We could have done this pattern matching directly in the function’s parameters (it would have been shorter and clearer actually) but this just goes to show that it’s possible to do it in where bindings as well.

        -

        Just like we’ve defined constants in where blocks, you can also define functions. Staying true to our solids programming theme, let’s make a function that takes a list of mass-volume pairs and returns a list of densities.

        -
        
        -calcDensities :: (RealFloat a) => [(a, a)] -> [a]
        +          (l:_) = lastname
        +

        We could have done this pattern matching directly in the function’s +parameters (it would have been shorter and clearer actually) but this +just goes to show that it’s possible to do it in where bindings as +well.

        +

        Just like we’ve defined constants in where blocks, you can also +define functions. Staying true to our solids programming theme, let’s +make a function that takes a list of mass-volume pairs and returns a +list of densities.

        +
        calcDensities :: (RealFloat a) => [(a, a)] -> [a]
         calcDensities xs = [density m v | (m, v) <- xs]
        -    where density mass volume = mass / volume
        -
        -

        And that’s all there is to it! The reason we had to introduce density as a function in this example is because we can’t just calculate one density from the function’s parameters. We have to examine the list passed to the function and there’s a different density for every pair in there.

        -

        where bindings can also be nested. It’s a common idiom to make a function and define some helper function in its where clause and then to give those functions helper functions as well, each with its own where clause.

        + where density mass volume = mass / volume
        +

        And that’s all there is to it! The reason we had to introduce +density as a function in this example is because we can’t +just calculate one density from the function’s parameters. We have to +examine the list passed to the function and there’s a different density +for every pair in there.

        +

        where bindings can also be nested. It’s a common idiom to +make a function and define some helper function in its where +clause and then to give those functions helper functions as well, each +with its own where clause.

        Let it be

        -

        -Very similar to where bindings are let bindings. Where bindings are a syntactic construct that let you bind to variables at the end of a function and the whole function can see them, including all the guards. Let bindings let you bind to variables anywhere and are expressions themselves, but are very local, so they don’t span across guards. Just like any construct in Haskell that is used to bind values to names, let bindings can be used for pattern matching. Let’s see them in action! This is how we could define a function that gives us a cylinder’s surface area based on its height and radius: -

        -
        
        -cylinder :: (RealFloat a) => a -> a -> a
        +

        Very similar to where bindings are let bindings. Where bindings are a +syntactic construct that let you bind to variables at the end of a +function and the whole function can see them, including all the guards. +Let bindings let you bind to variables anywhere and are expressions +themselves, but are very local, so they don’t span across guards. Just +like any construct in Haskell that is used to bind values to names, let +bindings can be used for pattern matching. Let’s see them in action! +This is how we could define a function that gives us a cylinder’s +surface area based on its height and radius:

        +
        cylinder :: (RealFloat a) => a -> a -> a
         cylinder r h =
             let sideArea = 2 * pi * r * h
                 topArea = pi * r ^2
        -    in  sideArea + 2 * topArea
        -
        -let it be -

        The form is let <bindings> in <expression>. The names that you define in the let part are accessible to the expression after the in part. As you can see, we could have also defined this with a where binding. Notice that the names are also aligned in a single column. So what’s the difference between the two? For now it just seems that let puts the bindings first and the expression that uses them later whereas where is the other way around.

        -

        The difference is that let bindings are expressions themselves. where bindings are just syntactic constructs. Remember when we did the if statement and it was explained that an if else statement is an expression and you can cram it in almost anywhere?

        -
        
        -ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
        +    in  sideArea + 2 * topArea
        +let it be +

        The form is let <bindings> in <expression>. +The names that you define in the let part are accessible to the +expression after the in part. As you can see, we could have +also defined this with a where binding. Notice that the names +are also aligned in a single column. So what’s the difference between +the two? For now it just seems that let puts the bindings first +and the expression that uses them later whereas where is the +other way around.

        +

        The difference is that let bindings are expressions +themselves. where bindings are just syntactic constructs. +Remember when we did the if statement and it was explained that an if +else statement is an expression and you can cram it in almost +anywhere?

        +
        ghci> [if 5 > 3 then "Woo" else "Boo", if 'a' > 'b' then "Foo" else "Bar"]
         ["Woo", "Bar"]
         ghci> 4 * (if 10 > 5 then 10 else 0) + 2
        -42
        -
        +42

        You can also do that with let bindings.

        -
        
        -ghci> 4 * (let a = 9 in a + 1) + 2
        -42
        -
        +
        ghci> 4 * (let a = 9 in a + 1) + 2
        +42

        They can also be used to introduce functions in a local scope:

        -
        
        -ghci> [let square x = x * x in (square 5, square 3, square 2)]
        -[(25,9,4)]
        -
        -

        If we want to bind to several variables inline, we obviously can’t align them at columns. That’s why we can separate them with semicolons.

        -
        
        -ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)
        -(6000000,"Hey there!")
        -
        -

        You don’t have to put a semicolon after the last binding but you can if you want. Like we said before, you can pattern match with let bindings. They’re very useful for quickly dismantling a tuple into components and binding them to names and such.

        -
        
        -ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100
        -600
        -
        -

        You can also put let bindings inside list comprehensions. Let’s rewrite our previous example of calculating lists of mass-volume pairs to use a let inside a list comprehension instead of defining an auxiliary function with a where.

        -
        
        -calcDensities :: (RealFloat a) => [(a, a)] -> [a]
        -calcDensities xs = [density | (m, v) <- xs, let density = m / v]
        -
        -

        We include a let inside a list comprehension much like we would a predicate, only it doesn’t filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the densities that will float in air:

        -
        
        -calcDensities :: (RealFloat a) => [(a, a)] -> [a]
        -calcDensities xs = [density | (m, v) <- xs, let density = m / v, density < 1.2]
        -
        -

        We can’t use the density name in the (m, v) <- xs part because it’s defined prior to the let binding.

        -

        We omitted the in part of the let binding when we used them in list comprehensions because the visibility of the names is already predefined there. However, we could use a let in binding in a predicate and the names defined would only be visible to that predicate. The in part can also be omitted when defining functions and constants directly in GHCi. If we do that, then the names will be visible throughout the entire interactive session.

        -
        
        -ghci> let zoot x y z = x * y + z
        +
        ghci> [let square x = x * x in (square 5, square 3, square 2)]
        +[(25,9,4)]
        +

        If we want to bind to several variables inline, we obviously can’t +align them at columns. That’s why we can separate them with +semicolons.

        +
        ghci> (let a = 100; b = 200; c = 300 in a*b*c, let foo="Hey "; bar = "there!" in foo ++ bar)
        +(6000000,"Hey there!")
        +

        You don’t have to put a semicolon after the last binding but you can +if you want. Like we said before, you can pattern match with +let bindings. They’re very useful for quickly dismantling a +tuple into components and binding them to names and such.

        +
        ghci> (let (a,b,c) = (1,2,3) in a+b+c) * 100
        +600
        +

        You can also put let bindings inside list comprehensions. +Let’s rewrite our previous example of calculating lists of mass-volume +pairs to use a let inside a list comprehension instead of +defining an auxiliary function with a where.

        +
        calcDensities :: (RealFloat a) => [(a, a)] -> [a]
        +calcDensities xs = [density | (m, v) <- xs, let density = m / v]
        +

        We include a let inside a list comprehension much like we +would a predicate, only it doesn’t filter the list, it only binds to +names. The names defined in a let inside a list comprehension +are visible to the output function (the part before the |) +and all predicates and sections that come after of the binding. So we +could make our function return only the densities that will float in +air:

        +
        calcDensities :: (RealFloat a) => [(a, a)] -> [a]
        +calcDensities xs = [density | (m, v) <- xs, let density = m / v, density < 1.2]
        +

        We can’t use the density name in the +(m, v) <- xs part because it’s defined prior to the +let binding.

        +

        We omitted the in part of the let binding when we +used them in list comprehensions because the visibility of the names is +already predefined there. However, we could use a let in +binding in a predicate and the names defined would only be visible to +that predicate. The in part can also be omitted when defining +functions and constants directly in GHCi. If we do that, then the names +will be visible throughout the entire interactive session.

        +
        ghci> let zoot x y z = x * y + z
         ghci> zoot 3 9 2
         29
         ghci> let boot x y z = x * y + z in boot 3 4 2
         14
         ghci> boot
        -<interactive>:1:0: Not in scope: `boot'
        -
        -

        If let bindings are so cool, why not use them all the time instead of where bindings, you ask? Well, since let bindings are expressions and are fairly local in their scope, they can’t be used across guards. Some people prefer where bindings because the names come after the function they’re being used in. That way, the function body is closer to its name and type declaration and to some that’s more readable.

        +<interactive>:1:0: Not in scope: `boot'
        +

        If let bindings are so cool, why not use them all the time +instead of where bindings, you ask? Well, since let +bindings are expressions and are fairly local in their scope, they can’t +be used across guards. Some people prefer where bindings +because the names come after the function they’re being used in. That +way, the function body is closer to its name and type declaration and to +some that’s more readable.

        Case expressions

        -case -

        Many imperative languages (C, C++, Java, etc.) have case syntax and if you’ve ever programmed in them, you probably know what it’s about. It’s about taking a variable and then executing blocks of code for specific values of that variable and then maybe including a catch-all block of code in case the variable has some value for which we didn’t set up a case.

        -

        Haskell takes that concept and one-ups it. Like the name implies, case expressions are, well, expressions, much like if else expressions and let bindings. Not only can we evaluate expressions based on the possible cases of the value of a variable, we can also do pattern matching. Hmmm, taking a variable, pattern matching it, evaluating pieces of code based on its value, where have we heard this before? Oh yeah, pattern matching on parameters in function definitions! Well, that’s actually just syntactic sugar for case expressions. These two pieces of code do the same thing and are interchangeable:

        -
        
        -head' :: [a] -> a
        +case
        +

        Many imperative languages (C, C++, Java, etc.) have case syntax and +if you’ve ever programmed in them, you probably know what it’s about. +It’s about taking a variable and then executing blocks of code for +specific values of that variable and then maybe including a catch-all +block of code in case the variable has some value for which we didn’t +set up a case.

        +

        Haskell takes that concept and one-ups it. Like the name implies, +case expressions are, well, expressions, much like if else expressions +and let bindings. Not only can we evaluate expressions based on +the possible cases of the value of a variable, we can also do pattern +matching. Hmmm, taking a variable, pattern matching it, evaluating +pieces of code based on its value, where have we heard this before? Oh +yeah, pattern matching on parameters in function definitions! Well, +that’s actually just syntactic sugar for case expressions. These two +pieces of code do the same thing and are interchangeable:

        +
        head' :: [a] -> a
         head' [] = error "No head for empty lists!"
        -head' (x:_) = x
        -
        -
        
        -head' :: [a] -> a
        +head' (x:_) = x
        +
        head' :: [a] -> a
         head' xs = case xs of [] -> error "No head for empty lists!"
        -                      (x:_) -> x
        -
        + (x:_) -> x

        As you can see, the syntax for case expressions is pretty simple:

        -
        
        -case expression of pattern -> result
        +
        case expression of pattern -> result
                            pattern -> result
                            pattern -> result
        -                   ...
        -
        -

        expression is matched against the patterns. The pattern matching action is the same as expected: the first pattern that matches the expression is used. If it falls through the whole case expression and no suitable pattern is found, a runtime error occurs.

        -

        Whereas pattern matching on function parameters can only be done when defining functions, case expressions can be used pretty much anywhere. For instance:

        -
        
        -describeList :: [a] -> String
        +                   ...
        +

        expression is matched against the patterns. The pattern +matching action is the same as expected: the first pattern that matches +the expression is used. If it falls through the whole case expression +and no suitable pattern is found, a runtime error occurs.

        +

        Whereas pattern matching on function parameters can only be done when +defining functions, case expressions can be used pretty much anywhere. +For instance:

        +
        describeList :: [a] -> String
         describeList xs = "The list is " ++ case xs of [] -> "empty."
                                                        [x] -> "a singleton list."
        -                                               xs -> "a longer list."
        -
        -

        They are useful for pattern matching against something in the middle of an expression. Because pattern matching in function definitions is syntactic sugar for case expressions, we could have also defined this like so:

        -
        
        -describeList :: [a] -> String
        +                                               xs -> "a longer list."
        +

        They are useful for pattern matching against something in the middle +of an expression. Because pattern matching in function definitions is +syntactic sugar for case expressions, we could have also defined this +like so:

        +
        describeList :: [a] -> String
         describeList xs = "The list is " ++ what xs
             where what [] = "empty."
                   what [x] = "a singleton list."
        -          what xs = "a longer list."
        -
        - + what xs = "a longer list."
        • diff --git a/docs/types-and-typeclasses.html b/docs/types-and-typeclasses.html index 8a15d57..9c965fc 100644 --- a/docs/types-and-typeclasses.html +++ b/docs/types-and-typeclasses.html @@ -31,15 +31,32 @@
        -

        Types and Typeclasses

        +

        Types and Typeclasses

        Believe the type

        -moo -

        Previously we mentioned that Haskell has a static type system. The type of every expression is known at compile time, which leads to safer code. If you write a program where you try to divide a boolean type with some number, it won’t even compile. That’s good because it’s better to catch such errors at compile time instead of having your program crash. Everything in Haskell has a type, so the compiler can reason quite a lot about your program before compiling it.

        -

        Unlike Java or Pascal, Haskell has type inference. If we write a number, we don’t have to tell Haskell it’s a number. It can infer that on its own, so we don’t have to explicitly write out the types of our functions and expressions to get things done. We covered some of the basics of Haskell with only a very superficial glance at types. However, understanding the type system is a very important part of learning Haskell.

        -

        A type is a kind of label that every expression has. It tells us in which category of things that expression fits. The expression True is a boolean, "hello" is a string, etc.

        -

        Now we’ll use GHCI to examine the types of some expressions. We’ll do that by using the :t command which, followed by any valid expression, tells us its type. Let’s give it a whirl.

        -
        
        -ghci> :t 'a'
        +moo
        +

        Previously we mentioned that Haskell has a static type system. The +type of every expression is known at compile time, which leads to safer +code. If you write a program where you try to divide a boolean type with +some number, it won’t even compile. That’s good because it’s better to +catch such errors at compile time instead of having your program crash. +Everything in Haskell has a type, so the compiler can reason quite a lot +about your program before compiling it.

        +

        Unlike Java or Pascal, Haskell has type inference. If we write a +number, we don’t have to tell Haskell it’s a number. It can +infer that on its own, so we don’t have to explicitly write out +the types of our functions and expressions to get things done. We +covered some of the basics of Haskell with only a very superficial +glance at types. However, understanding the type system is a very +important part of learning Haskell.

        +

        A type is a kind of label that every expression has. It tells us in +which category of things that expression fits. The expression +True is a boolean, "hello" is a string, +etc.

        +

        Now we’ll use GHCI to examine the types of some expressions. We’ll do +that by using the :t command which, followed by any valid +expression, tells us its type. Let’s give it a whirl.

        +
        ghci> :t 'a'
         'a' :: Char
         ghci> :t True
         True :: Bool
        @@ -48,106 +65,169 @@ 

        Believe the type

        ghci> :t (True, 'a') (True, 'a') :: (Bool, Char) ghci> :t 4 == 5 -4 == 5 :: Bool -
        -

        -bomb -Here we see that doing :t on an expression prints out the expression followed by :: and its type. :: is read as “has type of”. Explicit types are always denoted with the first letter in capital case. 'a', as it would seem, has a type of Char. It’s not hard to conclude that it stands for character. True is of a Bool type. That makes sense. But what’s this? Examining the type of "HELLO!" yields a [Char]. The square brackets denote a list. So we read that as it being a list of characters. Unlike lists, each tuple length has its own type. So the expression of (True, 'a') has a type of (Bool, Char), whereas an expression such as ('a','b','c') would have the type of (Char, Char, Char). 4 == 5 will always return False, so its type is Bool. -

        -

        -Functions also have types. When writing our own functions, we can choose to give them an explicit type declaration. This is generally considered to be good practice except when writing very short functions. From here on, we’ll give all the functions that we make type declarations. Remember the list comprehension we made previously that filters a string so that only caps remain? Here’s how it looks like with a type declaration. -

        -
        
        -removeNonUppercase :: [Char] -> [Char]
        -removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
        -
        -

        -removeNonUppercase has a type of [Char] -> [Char], meaning that it maps from a string to a string. That’s because it takes one string as a parameter and returns another as a result. The [Char] type is synonymous with String so it’s clearer if we write removeNonUppercase :: String -> String. We didn’t have to give this function a type declaration because the compiler can infer by itself that it’s a function from a string to a string but we did anyway. But how do we write out the type of a function that takes several parameters? Here’s a simple function that takes three integers and adds them together: -

        -
        
        -addThree :: Int -> Int -> Int -> Int
        -addThree x y z = x + y + z
        -
        -

        The parameters are separated with -> and there’s no special distinction between the parameters and the return type. The return type is the last item in the declaration and the parameters are the first three. Later on we’ll see why they’re all just separated with -> instead of having some more explicit distinction between the return types and the parameters like Int, Int, Int -> Int or something. -

        -

        If you want to give your function a type declaration but are unsure as to what it should be, you can always just write the function without it and then check it with :t. Functions are expressions too, so :t works on them without a problem.

        +4 == 5 :: Bool
        +

        bomb Here we see that doing +:t on an expression prints out the expression followed by +:: and its type. :: is read as “has type of”. +Explicit types are always denoted with the first letter in capital case. +'a', as it would seem, has a type of Char. +It’s not hard to conclude that it stands for character. +True is of a Bool type. That makes sense. But +what’s this? Examining the type of "HELLO!" yields a +[Char]. The square brackets denote a list. So we read that +as it being a list of characters. Unlike lists, each tuple +length has its own type. So the expression of (True, 'a') +has a type of (Bool, Char), whereas an expression such as +('a','b','c') would have the type of +(Char, Char, Char). 4 == 5 will always return +False, so its type is Bool.

        +

        Functions also have types. When writing our own functions, we can +choose to give them an explicit type declaration. This is generally +considered to be good practice except when writing very short functions. +From here on, we’ll give all the functions that we make type +declarations. Remember the list comprehension we made previously that +filters a string so that only caps remain? Here’s how it looks like with +a type declaration.

        +
        removeNonUppercase :: [Char] -> [Char]
        +removeNonUppercase st = [ c | c <- st, c `elem` ['A'..'Z']]
        +

        removeNonUppercase has a type of +[Char] -> [Char], meaning that it maps from a string to +a string. That’s because it takes one string as a parameter and returns +another as a result. The [Char] type is synonymous with +String so it’s clearer if we write +removeNonUppercase :: String -> String. We didn’t have +to give this function a type declaration because the compiler can infer +by itself that it’s a function from a string to a string but we did +anyway. But how do we write out the type of a function that takes +several parameters? Here’s a simple function that takes three integers +and adds them together:

        +
        addThree :: Int -> Int -> Int -> Int
        +addThree x y z = x + y + z
        +

        The parameters are separated with -> and there’s no +special distinction between the parameters and the return type. The +return type is the last item in the declaration and the parameters are +the first three. Later on we’ll see why they’re all just separated with +-> instead of having some more explicit distinction +between the return types and the parameters like +Int, Int, Int -> Int or something.

        +

        If you want to give your function a type declaration but are unsure +as to what it should be, you can always just write the function without +it and then check it with :t. Functions are expressions +too, so :t works on them without a problem.

        Here’s an overview of some common types.

        -

        -Int stands for integer. It’s used for whole numbers. 7 can be an Int but 7.2 cannot. Int is bounded, which means that it has a minimum and a maximum value. Usually on 32-bit machines the maximum possible Int is 2147483647 and the minimum is -2147483648. -

        -

        Integer stands for, er … also integer. The main difference is that it’s not bounded so it can be used to represent really really big numbers. I mean like really big. Int, however, is more efficient. -

        -
        
        -factorial :: Integer -> Integer
        -factorial n = product [1..n]
        -
        -
        
        -ghci> factorial 50
        -30414093201713378043612608166064768844377641568960512000000000000
        -
        -

        Float is a real floating point with single precision.

        -
        
        -circumference :: Float -> Float
        -circumference r = 2 * pi * r
        -
        -
        
        -ghci> circumference 4.0
        -25.132742
        -
        -

        Double is a real floating point with double the precision!

        -
        
        -circumference' :: Double -> Double
        -circumference' r = 2 * pi * r
        -
        -
        
        -ghci> circumference' 4.0
        -25.132741228718345
        -
        -

        -Bool is a boolean type. It can have only two values: True and False. -

        -

        -Char represents a character. It’s denoted by single quotes. A list of characters is a string. -

        -

        Tuples are types but they are dependent on their length as well as the types of their components, so there is theoretically an infinite number of tuple types, which is too many to cover in this tutorial. Note that the empty tuple () is also a type which can only have a single value: ()

        +

        Int stands for integer. It’s used for +whole numbers. 7 can be an Int but +7.2 cannot. Int is bounded, which means that +it has a minimum and a maximum value. Usually on 32-bit machines the +maximum possible Int is 2147483647 and the minimum is +-2147483648.

        +

        Integer stands for, er … also +integer. The main difference is that it’s not bounded so it can be used +to represent really really big numbers. I mean like really big. +Int, however, is more efficient.

        +
        factorial :: Integer -> Integer
        +factorial n = product [1..n]
        +
        ghci> factorial 50
        +30414093201713378043612608166064768844377641568960512000000000000
        +

        Float is a real floating point with +single precision.

        +
        circumference :: Float -> Float
        +circumference r = 2 * pi * r
        +
        ghci> circumference 4.0
        +25.132742
        +

        Double is a real floating point with +double the precision!

        +
        circumference' :: Double -> Double
        +circumference' r = 2 * pi * r
        +
        ghci> circumference' 4.0
        +25.132741228718345
        +

        Bool is a boolean type. It can have +only two values: True and False.

        +

        Char represents a character. It’s +denoted by single quotes. A list of characters is a string.

        +

        Tuples are types but they are dependent on their length as well as +the types of their components, so there is theoretically an infinite +number of tuple types, which is too many to cover in this tutorial. Note +that the empty tuple () is also a type +which can only have a single value: ()

        Type variables

        -

        -What do you think is the type of the head function? Because head takes a list of any type and returns the first element, so what could it be? Let’s check! -

        -
        
        -ghci> :t head
        -head :: [a] -> a
        -
        -

        -box -Hmmm! What is this a? Is it a type? Remember that we previously stated that types are written in capital case, so it can’t exactly be a type. Because it’s not in capital case it’s actually a type variable. That means that a can be of any type. This is much like generics in other languages, only in Haskell it’s much more powerful because it allows us to easily write very general functions if they don’t use any specific behavior of the types in them. Functions that have type variables are called polymorphic functions. The type declaration of head states that it takes a list of any type and returns one element of that type. -

        -

        Although type variables can have names longer than one character, we usually give them names of a, b, c, d …

        -

        Remember fst? It returns the first component of a pair. Let’s examine its type.

        -
        
        -ghci> :t fst
        -fst :: (a, b) -> a
        -
        -

        -We see that fst takes a tuple which contains two types and returns an element which is of the same type as the pair’s first component. That’s why we can use fst on a pair that contains any two types. Note that just because a and b are different type variables, they don’t have to be different types. It just states that the first component’s type and the return value’s type are the same. -

        +

        What do you think is the type of the head function? +Because head takes a list of any type and returns the first +element, so what could it be? Let’s check!

        +
        ghci> :t head
        +head :: [a] -> a
        +

        box Hmmm! What is this a? +Is it a type? Remember that we previously stated that types are written +in capital case, so it can’t exactly be a type. Because it’s not in +capital case it’s actually a type variable. That means +that a can be of any type. This is much like generics in +other languages, only in Haskell it’s much more powerful because it +allows us to easily write very general functions if they don’t use any +specific behavior of the types in them. Functions that have type +variables are called polymorphic functions. The type +declaration of head states that it takes a list of any type +and returns one element of that type.

        +

        Although type variables can have names longer than one character, we +usually give them names of a, b, c, d …

        +

        Remember fst? It returns the first component of a pair. +Let’s examine its type.

        +
        ghci> :t fst
        +fst :: (a, b) -> a
        +

        We see that fst takes a tuple which contains two types +and returns an element which is of the same type as the pair’s first +component. That’s why we can use fst on a pair that +contains any two types. Note that just because a and +b are different type variables, they don’t have to be +different types. It just states that the first component’s type and the +return value’s type are the same.

        Typeclasses 101

        -class -

        A typeclass is a sort of interface that defines some behavior. If a type is a part of a typeclass, that means that it supports and implements the behavior the typeclass describes. A lot of people coming from OOP get confused by typeclasses because they think they are like classes in object oriented languages. Well, they’re not. You can think of them kind of as Java interfaces, only better. -

        +class +

        A typeclass is a sort of interface that defines some behavior. If a +type is a part of a typeclass, that means that it supports and +implements the behavior the typeclass describes. A lot of people coming +from OOP get confused by typeclasses because they think they are like +classes in object oriented languages. Well, they’re not. You can think +of them kind of as Java interfaces, only better.

        What’s the type signature of the == function?

        -
        
        -ghci> :t (==)
        -(==) :: (Eq a) => a -> a -> Bool
        -
        -

        Note: the equality operator, == is a function. So are +, *, -, / and pretty much all operators. If a function is comprised only of special characters, it’s considered an infix function by default. If we want to examine its type, pass it to another function or call it as a prefix function, we have to surround it in parentheses.

        -

        Interesting. We see a new thing here, the => symbol. Everything before the => symbol is called a class constraint. We can read the previous type declaration like this: the equality function takes any two values that are of the same type and returns a Bool. The type of those two values must be a member of the Eq class (this was the class constraint).

        -

        The Eq typeclass provides an interface for testing for equality. Any type where it makes sense to test for equality between two values of that type should be a member of the Eq class. All standard Haskell types except for IO (the type for dealing with input and output) and functions are a part of the Eq typeclass.

        -

        The elem function has a type of (Eq a) => a -> [a] -> Bool because it uses == over a list to check whether some value we’re looking for is in it.

        +
        ghci> :t (==)
        +(==) :: (Eq a) => a -> a -> Bool
        +
        +

        Note: the equality operator, == is a +function. So are +, *, -, +/ and pretty much all operators. If a function is comprised +only of special characters, it’s considered an infix function by +default. If we want to examine its type, pass it to another function or +call it as a prefix function, we have to surround it in parentheses.

        +
        +

        Interesting. We see a new thing here, the => symbol. +Everything before the => symbol is called a +class constraint. We can read the previous type +declaration like this: the equality function takes any two values that +are of the same type and returns a Bool. The type of those +two values must be a member of the Eq class (this was the +class constraint).

        +

        The Eq typeclass provides an interface for testing for +equality. Any type where it makes sense to test for equality between two +values of that type should be a member of the Eq class. All +standard Haskell types except for IO (the type for dealing with input +and output) and functions are a part of the Eq +typeclass.

        +

        The elem function has a type of +(Eq a) => a -> [a] -> Bool because it uses +== over a list to check whether some value we’re looking +for is in it.

        Some basic typeclasses:

        -

        Eq is used for types that support equality testing. The functions its members implement are == and /=. So if there’s an Eq class constraint for a type variable in a function, it uses == or /= somewhere inside its definition. All the types we mentioned previously except for functions are part of Eq, so they can be tested for equality.

        -
        
        -ghci> 5 == 5
        +

        Eq is used for types that support +equality testing. The functions its members implement are +== and /=. So if there’s an Eq +class constraint for a type variable in a function, it uses +== or /= somewhere inside its definition. All +the types we mentioned previously except for functions are part of +Eq, so they can be tested for equality.

        +
        ghci> 5 == 5
         True
         ghci> 5 /= 5
         False
        @@ -156,65 +236,78 @@ 

        Typeclasses 101

        ghci> "Ho Ho" == "Ho Ho" True ghci> 3.432 == 3.432 -True -
        -

        -Ord is for types that have an ordering.

        -
        
        -ghci> :t (>)
        -(>) :: (Ord a) => a -> a -> Bool
        -
        -

        -All the types we covered so far except for functions are part of Ord. Ord covers all the standard comparing functions such as >, <, >= and <=. The compare function takes two Ord members of the same type and returns an ordering. Ordering is a type that can be GT, LT or EQ, meaning greater than, lesser than and equal, respectively.

        -

        To be a member of Ord, a type must first have membership in the prestigious and exclusive Eq club.

        -
        
        -ghci> "Abrakadabra" < "Zebra"
        +True
        +

        Ord is for types that have an +ordering.

        +
        ghci> :t (>)
        +(>) :: (Ord a) => a -> a -> Bool
        +

        All the types we covered so far except for functions are part of +Ord. Ord covers all the standard comparing +functions such as >, <, +>= and <=. The compare +function takes two Ord members of the same type and returns +an ordering. Ordering is a type that can +be GT, LT or EQ, meaning +greater than, lesser than and equal, +respectively.

        +

        To be a member of Ord, a type must first have membership +in the prestigious and exclusive Eq club.

        +
        ghci> "Abrakadabra" < "Zebra"
         True
         ghci> "Abrakadabra" `compare` "Zebra"
         LT
         ghci> 5 >= 2
         True
         ghci> 5 `compare` 3
        -GT
        -
        -

        Members of Show can be presented as strings. All types covered so far except for functions are a part of Show. The most used function that deals with the Show typeclass is show. It takes a value whose type is a member of Show and presents it to us as a string.

        -
        
        -ghci> show 3
        +GT
        +

        Members of Show can be presented as +strings. All types covered so far except for functions are a part of +Show. The most used function that deals with the +Show typeclass is show. It takes a value whose +type is a member of Show and presents it to us as a +string.

        +
        ghci> show 3
         "3"
         ghci> show 5.334
         "5.334"
         ghci> show True
        -"True"
        -
        -

        Read is sort of the opposite typeclass of Show. The read function takes a string and returns a type which is a member of Read.

        -
        
        -ghci> read "True" || False
        +"True"
        +

        Read is sort of the opposite +typeclass of Show. The read function takes a +string and returns a type which is a member of Read.

        +
        ghci> read "True" || False
         True
         ghci> read "8.2" + 3.8
         12.0
         ghci> read "5" - 2
         3
         ghci> read "[1,2,3,4]" ++ [3]
        -[1,2,3,4,3]
        -
        -

        So far so good. Again, all types covered so far are in this typeclass. But what happens if we try to do just read "4"?

        -
        
        -ghci> read "4"
        +[1,2,3,4,3]
        +

        So far so good. Again, all types covered so far are in this +typeclass. But what happens if we try to do just +read "4"?

        +
        ghci> read "4"
         <interactive>:1:0:
             Ambiguous type variable `a' in the constraint:
               `Read a' arising from a use of `read' at <interactive>:1:0-7
        -    Probable fix: add a type signature that fixes these type variable(s)
        -
        -

        What GHCI is telling us here is that it doesn’t know what we want in return. Notice that in the previous uses of read we did something with the result afterwards. That way, GHCI could infer what kind of result we wanted out of our read. If we used it as a boolean, it knew it had to return a Bool. But now, it knows we want some type that is part of the Read class, it just doesn’t know which one. Let’s take a look at the type signature of read.

        -
        
        -ghci> :t read
        -read :: (Read a) => String -> a
        -
        -

        -See? It returns a type that’s part of Read but if we don’t try to use it in some way later, it has no way of knowing which type. That’s why we can use explicit type annotations. Type annotations are a way of explicitly saying what the type of an expression should be. We do that by adding :: at the end of the expression and then specifying a type. Observe: -

        -
        
        -ghci> read "5" :: Int
        +    Probable fix: add a type signature that fixes these type variable(s)
        +

        What GHCI is telling us here is that it doesn’t know what we want in +return. Notice that in the previous uses of read we did +something with the result afterwards. That way, GHCI could infer what +kind of result we wanted out of our read. If we used it as +a boolean, it knew it had to return a Bool. But now, it +knows we want some type that is part of the Read class, it +just doesn’t know which one. Let’s take a look at the type signature of +read.

        +
        ghci> :t read
        +read :: (Read a) => String -> a
        +

        See? It returns a type that’s part of Read but if we +don’t try to use it in some way later, it has no way of knowing which +type. That’s why we can use explicit type annotations. +Type annotations are a way of explicitly saying what the type of an +expression should be. We do that by adding :: at the end of +the expression and then specifying a type. Observe:

        +
        ghci> read "5" :: Int
         5
         ghci> read "5" :: Float
         5.0
        @@ -223,67 +316,102 @@ 

        Typeclasses 101

        ghci> read "[1,2,3,4]" :: [Int] [1,2,3,4] ghci> read "(3, 'a')" :: (Int, Char) -(3, 'a') -
        -

        Most expressions are such that the compiler can infer what their type is by itself. But sometimes, the compiler doesn’t know whether to return a value of type Int or Float for an expression like read "5". To see what the type is, Haskell would have to actually evaluate read "5". But since Haskell is a statically typed language, it has to know all the types before the code is compiled (or in the case of GHCI, evaluated). So we have to tell Haskell: “Hey, this expression should have this type, in case you don’t know!”.

        -

        Enum members are sequentially ordered types — they can be enumerated. The main advantage of the Enum typeclass is that we can use its types in list ranges. They also have defined successors and predecessors, which you can get with the succ and pred functions. Types in this class: (), Bool, Char, Ordering, Int, Integer, Float and Double.

        -
        
        -ghci> ['a'..'e']
        +(3, 'a')
        +

        Most expressions are such that the compiler can infer what their type +is by itself. But sometimes, the compiler doesn’t know whether to return +a value of type Int or Float for an expression +like read "5". To see what the type is, Haskell would have +to actually evaluate read "5". But since Haskell is a +statically typed language, it has to know all the types before the code +is compiled (or in the case of GHCI, evaluated). So we have to tell +Haskell: “Hey, this expression should have this type, in case you don’t +know!”.

        +

        Enum members are sequentially +ordered types — they can be enumerated. The main advantage of the +Enum typeclass is that we can use its types in list ranges. +They also have defined successors and predecessors, which you can get +with the succ and pred functions. Types in +this class: (), Bool, Char, +Ordering, Int, Integer, +Float and Double.

        +
        ghci> ['a'..'e']
         "abcde"
         ghci> [LT .. GT]
         [LT,EQ,GT]
         ghci> [3 .. 5]
         [3,4,5]
         ghci> succ 'B'
        -'C'
        -
        -

        Bounded members have an upper and a lower bound.

        -
        
        -ghci> minBound :: Int
        +'C'
        +

        Bounded members have an upper and a +lower bound.

        +
        ghci> minBound :: Int
         -2147483648
         ghci> maxBound :: Char
         '\1114111'
         ghci> maxBound :: Bool
         True
         ghci> minBound :: Bool
        -False
        -
        -

        minBound and maxBound are interesting because they have a type of (Bounded a) => a. In a sense they are polymorphic constants.

        -

        All tuples are also part of Bounded if the components are also in it.

        -
        
        -ghci> maxBound :: (Bool, Int, Char)
        -(True,2147483647,'\1114111')
        -
        -

        Num is a numeric typeclass. Its members have the property of being able to act like numbers. Let’s examine the type of a number.

        -
        
        -ghci> :t 20
        -20 :: (Num t) => t
        -
        -

        It appears that whole numbers are also polymorphic constants. They can act like any type that’s a member of the Num typeclass.

        -
        
        -ghci> 20 :: Int
        +False
        +

        minBound and maxBound are interesting +because they have a type of (Bounded a) => a. In a sense +they are polymorphic constants.

        +

        All tuples are also part of Bounded if the components +are also in it.

        +
        ghci> maxBound :: (Bool, Int, Char)
        +(True,2147483647,'\1114111')
        +

        Num is a numeric typeclass. Its +members have the property of being able to act like numbers. Let’s +examine the type of a number.

        +
        ghci> :t 20
        +20 :: (Num t) => t
        +

        It appears that whole numbers are also polymorphic constants. They +can act like any type that’s a member of the Num +typeclass.

        +
        ghci> 20 :: Int
         20
         ghci> 20 :: Integer
         20
         ghci> 20 :: Float
         20.0
         ghci> 20 :: Double
        -20.0
        -
        -

        Those are types that are in the Num typeclass. If we examine the type of *, we’ll see that it accepts all numbers.

        -
        
        -ghci> :t (*)
        -(*) :: (Num a) => a -> a -> a
        -
        -

        -It takes two numbers of the same type and returns a number of that type. That’s why (5 :: Int) * (6 :: Integer) will result in a type error whereas 5 * (6 :: Integer) will work just fine and produce an Integer because 5 can act like an Integer or an Int. -

        -

        To join Num, a type must already be friends with Show and Eq.

        -

        Integral is also a numeric typeclass. Num includes all numbers, including real numbers and integral numbers, Integral includes only integral (whole) numbers. In this typeclass are Int and Integer.

        -

        Floating includes only floating point numbers, so Float and Double.

        -

        A very useful function for dealing with numbers is fromIntegral. It has a type declaration of fromIntegral :: (Num b, Integral a) => a -> b. From its type signature we see that it takes an integral number and turns it into a more general number. That’s useful when you want integral and floating point types to work together nicely. For instance, the length function has a type declaration of length :: [a] -> Int instead of having a more general type of (Num b) => length :: [a] -> b. I think that’s there for historical reasons or something, although in my opinion, it’s pretty stupid. Anyway, if we try to get a length of a list and then add it to 3.2, we’ll get an error because we tried to add together an Int and a floating point number. So to get around this, we do fromIntegral (length [1,2,3,4]) + 3.2 and it all works out.

        -

        Notice that fromIntegral has several class constraints in its type signature. That’s completely valid and as you can see, the class constraints are separated by commas inside the parentheses.

        - +20.0
        +

        Those are types that are in the Num typeclass. If we +examine the type of *, we’ll see that it accepts all +numbers.

        +
        ghci> :t (*)
        +(*) :: (Num a) => a -> a -> a
        +

        It takes two numbers of the same type and returns a number of that +type. That’s why (5 :: Int) * (6 :: Integer) will result in +a type error whereas 5 * (6 :: Integer) will work just fine +and produce an Integer because 5 can act like +an Integer or an Int.

        +

        To join Num, a type must already be friends with +Show and Eq.

        +

        Integral is also a numeric +typeclass. Num includes all numbers, including real numbers +and integral numbers, Integral includes only integral +(whole) numbers. In this typeclass are Int and +Integer.

        +

        Floating includes only floating +point numbers, so Float and Double.

        +

        A very useful function for dealing with numbers is fromIntegral. It has a type declaration of +fromIntegral :: (Num b, Integral a) => a -> b. From +its type signature we see that it takes an integral number and turns it +into a more general number. That’s useful when you want integral and +floating point types to work together nicely. For instance, the +length function has a type declaration of +length :: [a] -> Int instead of having a more general +type of (Num b) => length :: [a] -> b. I think that’s +there for historical reasons or something, although in my opinion, it’s +pretty stupid. Anyway, if we try to get a length of a list and then add +it to 3.2, we’ll get an error because we tried to add +together an Int and a floating point number. So to get +around this, we do fromIntegral (length [1,2,3,4]) + 3.2 +and it all works out.

        +

        Notice that fromIntegral has several class constraints +in its type signature. That’s completely valid and as you can see, the +class constraints are separated by commas inside the parentheses.

        • diff --git a/docs/zippers.html b/docs/zippers.html index 15c7440..9b8f25c 100644 --- a/docs/zippers.html +++ b/docs/zippers.html @@ -29,64 +29,41 @@
        -

        Zippers

        - -hi im chet - -

        -While Haskell’s purity comes with a whole bunch of benefits, it makes us tackle -some problems differently than we would in impure languages. Because of -referential transparency, one value is as good as another in Haskell if it -represents the same thing. -

        - -

        -So if we have a tree full of fives (high-fives, maybe?) and we want to change -one of them into a six, we have to have some way of knowing exactly which five -in our tree we want to change. We have to know where it is in our tree. In -impure languages, we could just note where in our memory the five is located and -change that. But in Haskell, one five is as good as another, so we can’t -discriminate based on where in our memory they are. We also can’t really -change anything; when we say that we change a tree, we actually -mean that we take a tree and return a new one that’s similar to the original -tree, but slightly different. -

        - -

        -One thing we can do is to remember a path from the root of the tree to the element -that we want to change. We could say, take this tree, go left, go right and then -left again and change the element that’s there. While this works, it can be -inefficient. If we want to later change an element that’s near the element that -we previously changed, we have to walk all the way from the root of the tree to -our element again! -

        - -

        -In this chapter, we’ll see how we can take some data structure and focus on a -part of it in a way that makes changing its elements easy and walking around it -efficient. Nice! -

        - - +

        Zippers

        +hi im chet +

        While Haskell’s purity comes with a whole bunch of benefits, it makes +us tackle some problems differently than we would in impure languages. +Because of referential transparency, one value is as good as another in +Haskell if it represents the same thing.

        +

        So if we have a tree full of fives (high-fives, maybe?) and we want +to change one of them into a six, we have to have some way of knowing +exactly which five in our tree we want to change. We have to know where +it is in our tree. In impure languages, we could just note where in our +memory the five is located and change that. But in Haskell, one five is +as good as another, so we can’t discriminate based on where in our +memory they are. We also can’t really change anything; when we +say that we change a tree, we actually mean that we take a tree and +return a new one that’s similar to the original tree, but slightly +different.

        +

        One thing we can do is to remember a path from the root of the tree +to the element that we want to change. We could say, take this tree, go +left, go right and then left again and change the element that’s there. +While this works, it can be inefficient. If we want to later change an +element that’s near the element that we previously changed, we have to +walk all the way from the root of the tree to our element again!

        +

        In this chapter, we’ll see how we can take some data structure and +focus on a part of it in a way that makes changing its elements easy and +walking around it efficient. Nice!

        Taking a walk

        - -

        -Like we’ve learned in biology class, there are many different kinds of trees, so -let’s pick a seed that we will use to plant ours. Here it is: -

        - -
        
        -data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)
        -
        - -

        -So our tree is either empty or it’s a node that has an element and two -subtrees. Here’s a fine example of such a tree, which I give to you, the -reader, for free! -

        - -
        
        -freeTree :: Tree Char
        +

        Like we’ve learned in biology class, there are many different kinds +of trees, so let’s pick a seed that we will use to plant ours. Here it +is:

        +
        data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show)
        +

        So our tree is either empty or it’s a node that has an element and +two subtrees. Here’s a fine example of such a tree, which I give to you, +the reader, for free!

        +
        freeTree :: Tree Char
         freeTree =
             Node 'P'
                 (Node 'O'
        @@ -108,575 +85,337 @@ 

        Taking a walk

        (Node 'A' Empty Empty) (Node 'C' Empty Empty) ) - ) -
        - -

        -And here’s this tree represented graphically: -

        - -polly says her back hurts - -

        -Notice that W in the tree there? Say we want to -change it into a P. How would we go about doing that? -Well, one way would be to pattern match on our tree until we find the element -that’s located by first going right and then left and changing said element. -Here’s the code for this: -

        - -
        
        -changeToP :: Tree Char -> Tree Char
        -changeToP (Node x l (Node y (Node _ m n) r)) = Node x l (Node y (Node 'P' m n) r)
        -
        - -

        -Yuck! Not only is this rather ugly, it’s also kind of confusing. What happens -here? Well, we pattern match on our tree and name its root element -x (that’s becomes the 'P' in the -root) and its left subtree l. Instead of giving a -name to its right subtree, we further pattern match on it. We continue this -pattern matching until we reach the subtree whose root is our -'W'. Once we’ve done this, we rebuild the tree, only the -subtree that contained the 'W' at its root now -has a 'P'. -

        - -

        -Is there a better way of doing this? How about we make our function take a tree -along with a list of directions. The directions will be either -L or R, representing left and -right respectively, and we’ll change -the element that we arrive at if we follow the supplied directions. Here it is: -

        - -
        
        -data Direction = L | R deriving (Show)
        +        )
        +

        And here’s this tree represented graphically:

        +polly says her back hurts +

        Notice that W in the tree there? Say we want to change +it into a P. How would we go about doing that? Well, one +way would be to pattern match on our tree until we find the element +that’s located by first going right and then left and changing said +element. Here’s the code for this:

        +
        changeToP :: Tree Char -> Tree Char
        +changeToP (Node x l (Node y (Node _ m n) r)) = Node x l (Node y (Node 'P' m n) r)
        +

        Yuck! Not only is this rather ugly, it’s also kind of confusing. What +happens here? Well, we pattern match on our tree and name its root +element x (that’s becomes the 'P' in the root) +and its left subtree l. Instead of giving a name to its +right subtree, we further pattern match on it. We continue this pattern +matching until we reach the subtree whose root is our 'W'. +Once we’ve done this, we rebuild the tree, only the subtree that +contained the 'W' at its root now has a +'P'.

        +

        Is there a better way of doing this? How about we make our function +take a tree along with a list of directions. The directions will be +either L or R, representing left and right +respectively, and we’ll change the element that we arrive at if we +follow the supplied directions. Here it is:

        +
        data Direction = L | R deriving (Show)
         type Directions = [Direction]
         
         changeToP :: Directions-> Tree Char -> Tree Char
         changeToP (L:ds) (Node x l r) = Node x (changeToP ds l) r
         changeToP (R:ds) (Node x l r) = Node x l (changeToP ds r)
        -changeToP [] (Node _ l r) = Node 'P' l r
        -
        - -

        -If the first element in our list of directions is -L, we construct a new tree that’s like the old tree, only -its left subtree has an element changed to 'P'. When -we recursively call changeToP, we give it only the -tail of the list of directions, because we already took a left. We do the same -thing in the case of an R. If the list of directions is -empty, that means that we’re at our destination, so we return a tree that’s like -the one supplied, only it has 'P' as its root -element. -

        - -

        -To avoid printing out the whole tree, let’s make a function that takes a list of -directions and tells us what the element at the destination is: -

        - -
        
        -elemAt :: Directions -> Tree a -> a
        +changeToP [] (Node _ l r) = Node 'P' l r
        +

        If the first element in our list of directions is L, we +construct a new tree that’s like the old tree, only its left subtree has +an element changed to 'P'. When we recursively call +changeToP, we give it only the tail of the list of +directions, because we already took a left. We do the same thing in the +case of an R. If the list of directions is empty, that +means that we’re at our destination, so we return a tree that’s like the +one supplied, only it has 'P' as its root element.

        +

        To avoid printing out the whole tree, let’s make a function that +takes a list of directions and tells us what the element at the +destination is:

        +
        elemAt :: Directions -> Tree a -> a
         elemAt (L:ds) (Node _ l _) = elemAt ds l
         elemAt (R:ds) (Node _ _ r) = elemAt ds r
        -elemAt [] (Node x _ _) = x
        -
        - -

        -This function is actually quite similar to changeToP, -only instead of remembering stuff along the way and reconstructing the tree, it -ignores everything except its destination. Here we change the -'W' to a 'P' and see if the -change in our new tree sticks: -

        - -
        
        -ghci> let newTree = changeToP [R,L] freeTree
        +elemAt [] (Node x _ _) = x
        +

        This function is actually quite similar to changeToP, +only instead of remembering stuff along the way and reconstructing the +tree, it ignores everything except its destination. Here we change the +'W' to a 'P' and see if the change in our new +tree sticks:

        +
        ghci> let newTree = changeToP [R,L] freeTree
         ghci> elemAt [R,L] newTree
        -'P'
        -
        - -

        -Nice, this seems to work. In these functions, the list of directions acts as a -sort of focus, because it pinpoints one exact subtree from our tree. -A direction list of [R] focuses on the subtree -that’s right of the root, for example. An empty direction list focuses on the -main tree itself. -

        - -

        -While this technique may seem cool, it can be rather inefficient, especially if -we want to repeatedly change elements. Say we have a really huge tree and a long -direction list that points to some element all the way at the bottom of the -tree. We use the direction list to take a walk along the tree and change an -element at the bottom. If we want to change another element that’s close to the -element that we’ve just changed, we have to start from the root of the tree and -walk all the way to the bottom again! What a drag. -

        - -

        -In the next section, we’ll find a better way of focusing on a subtree, one that -allows us to efficiently switch focus to subtrees that are nearby. -

        - - +'P'
        +

        Nice, this seems to work. In these functions, the list of directions +acts as a sort of focus, because it pinpoints one exact subtree +from our tree. A direction list of [R] focuses on the +subtree that’s right of the root, for example. An empty direction list +focuses on the main tree itself.

        +

        While this technique may seem cool, it can be rather inefficient, +especially if we want to repeatedly change elements. Say we have a +really huge tree and a long direction list that points to some element +all the way at the bottom of the tree. We use the direction list to take +a walk along the tree and change an element at the bottom. If we want to +change another element that’s close to the element that we’ve just +changed, we have to start from the root of the tree and walk all the way +to the bottom again! What a drag.

        +

        In the next section, we’ll find a better way of focusing on a +subtree, one that allows us to efficiently switch focus to subtrees that +are nearby.

        A trail of breadcrumbs

        - -whoop dee doo - -

        -Okay, so for focusing on a subtree, we want something better than just a list -of directions that we always follow from the root of our tree. Would it help if -we start at the root of the tree and move either left or right one step at a -time and sort of leave breadcrumbs? That is, when we go left, we remember that -we went left and when we go right, we remember that we went right. Sure, we can -try that. -

        - -

        -To represent our breadcrumbs, we’ll also use a list of -Direction (which is either L -or R), only instead of calling it -Directions, we’ll call it Breadcrumbs -, because our directions will now be reversed since we’re leaving them as -we go down our tree: -

        - -
        
        -type Breadcrumbs = [Direction]
        -
        - -

        -Here’s a function that takes a tree and some breadcrumbs and moves to the left -subtree while adding L to the head of the list that -represents our breadcrumbs: -

        - -
        
        -goLeft :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
        -goLeft (Node _ l _, bs) = (l, L:bs)
        -
        - -

        -We ignore the element at the root and the right subtree and just return the -left subtree along with the old breadcrumbs with L -as the head. Here’s a function to go right: -

        - -
        
        -goRight :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
        -goRight (Node _ _ r, bs) = (r, R:bs)
        -
        - -

        -It works the same way. Let’s use these functions to take our -freeTree and go right and then left: -

        - -
        
        -ghci> goLeft (goRight (freeTree, []))
        -(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
        -
        - -almostthere - -

        -Okay, so now we have a tree that has 'W' -in its root and 'C' in the root of its left subtree -and 'R' in the root of its right subtree. The -breadcrumbs are [L,R], because we first went right -and then left. -

        - -

        -To make walking along our tree clearer, we can use the --: function that we defined like so: -

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

        -Which allows us to apply functions to values by first writing the value, then -writing a -: and then the function. So instead of -goRight (freeTree, []), we can write -(freeTree, []) -: goRight. Using this, we can rewrite -the above so that it’s more apparent that we’re first going right and then left: -

        - -
        
        -ghci> (freeTree, []) -: goRight -: goLeft
        -(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
        -
        - +whoop dee doo +

        Okay, so for focusing on a subtree, we want something better than +just a list of directions that we always follow from the root of our +tree. Would it help if we start at the root of the tree and move either +left or right one step at a time and sort of leave breadcrumbs? That is, +when we go left, we remember that we went left and when we go right, we +remember that we went right. Sure, we can try that.

        +

        To represent our breadcrumbs, we’ll also use a list of +Direction (which is either L or +R), only instead of calling it Directions, +we’ll call it Breadcrumbs , because our directions will now +be reversed since we’re leaving them as we go down our tree:

        +
        type Breadcrumbs = [Direction]
        +

        Here’s a function that takes a tree and some breadcrumbs and moves to +the left subtree while adding L to the head of the list +that represents our breadcrumbs:

        +
        goLeft :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
        +goLeft (Node _ l _, bs) = (l, L:bs)
        +

        We ignore the element at the root and the right subtree and just +return the left subtree along with the old breadcrumbs with +L as the head. Here’s a function to go right:

        +
        goRight :: (Tree a, Breadcrumbs) -> (Tree a, Breadcrumbs)
        +goRight (Node _ _ r, bs) = (r, R:bs)
        +

        It works the same way. Let’s use these functions to take our +freeTree and go right and then left:

        +
        ghci> goLeft (goRight (freeTree, []))
        +(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])
        +almostthere +

        Okay, so now we have a tree that has 'W' in its root and +'C' in the root of its left subtree and 'R' in +the root of its right subtree. The breadcrumbs are [L,R], +because we first went right and then left.

        +

        To make walking along our tree clearer, we can use the +-: function that we defined like so:

        +
        x -: f = f x
        +

        Which allows us to apply functions to values by first writing the +value, then writing a -: and then the function. So instead +of goRight (freeTree, []), we can write +(freeTree, []) -: goRight. Using this, we can rewrite the +above so that it’s more apparent that we’re first going right and then +left:

        +
        ghci> (freeTree, []) -: goRight -: goLeft
        +(Node 'W' (Node 'C' Empty Empty) (Node 'R' Empty Empty),[L,R])

        Going back up

        - -

        -What if we now want to go back up in our tree? From our breadcrumbs we know -that the current tree is the left subtree of its parent and that it is the -right subtree of its parent, but that’s it. They don’t tell us enough about the -parent of the current subtree for us to be able to -go up in the tree. It would seem that apart from the direction that we took, a -single breadcrumb should also contain all other data that we need to go back up. -In this case, that’s the element in the parent tree along with its right -subtree. -

        - -

        -In general, a single breadcrumb should contain all the data needed to -reconstruct the parent node. So it should have the information from all the paths -that we didn’t take and it should also know the direction that we did take, but -it must not contain the subtree that we’re currently focusing on. That’s because -we already have that subtree in the first component of the tuple, so if we also -had it in the breadcrumbs, we’d have duplicate information. -

        - -

        -Let’s modify our breadcrumbs so that they also contain information about everything -that we previously ignored when moving left and right. Instead of -Direction, we’ll make a new data type: -

        - -
        
        -data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) deriving (Show)
        -
        - -

        -Now, instead of just L, we have a -LeftCrumb that also contains the element in the node that we -moved from and the right tree that we didn’t visit. Instead of -R, we have RightCrumb, which -contains the element in the node that we moved from and the left tree that we -didn’t visit. -

        - -

        -These breadcrumbs now contain all the data needed to recreate the tree that we -walked through. So instead of just being normal bread crumbs, they’re now more -like floppy disks that we leave as we go along, because they contain a lot more -information than just the direction that we took. -

        - -

        -In essence, every breadcrumb is now like a tree node with a hole in it. When we -move deeper into a tree, the breadcrumb carries all the information that the -node that we moved away from carried except the subtree that we chose to -focus on. It also has to note where the hole is. In the case of a -LeftCrumb, we know that we moved left, so the -subtree that’s missing is the left one. -

        - -

        -Let’s also change our Breadcrumbs type synonym to -reflect this: -

        - -
        
        -type Breadcrumbs a = [Crumb a]
        -
        - -

        -Next up, we have to modify the goLeft and -goRight functions to store information about the -paths that we didn’t take in our breadcrumbs, instead of ignoring that -information like they did before. Here’s goLeft: -

        - -
        
        -goLeft :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
        -goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
        -
        - -

        -You can see that it’s very similar to our previous goLeft, -only instead of just adding a L to the head of our -list of breadcrumbs, we add a LeftCrumb to signify -that we went left and we equip our LeftCrumb with the -element in the node that we moved from (that’s the x) -and the right subtree that we chose not to visit. -

        - -

        -Note that this function assumes that the current tree that’s under focus isn’t -Empty. An empty tree doesn’t have any subtrees, so -if we try to go left from an empty tree, an error will occur because the pattern -match on Node won’t succeed and there’s no pattern -that takes care of Empty. -

        - -

        -goRight is similar: -

        - -
        
        -goRight :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
        -goRight (Node x l r, bs) = (r, RightCrumb x l:bs)
        -
        - -

        -We were previously able to go left and right. What we’ve gotten now is the -ability to actually go back up by remembering stuff about the parent nodes and -the paths that we didn’t visit. Here’s the goUp -function: -

        - -
        
        -goUp :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
        +

        What if we now want to go back up in our tree? From our breadcrumbs +we know that the current tree is the left subtree of its parent and that +it is the right subtree of its parent, but that’s it. They don’t tell us +enough about the parent of the current subtree for us to be able to go +up in the tree. It would seem that apart from the direction that we +took, a single breadcrumb should also contain all other data that we +need to go back up. In this case, that’s the element in the parent tree +along with its right subtree.

        +

        In general, a single breadcrumb should contain all the data needed to +reconstruct the parent node. So it should have the information from all +the paths that we didn’t take and it should also know the direction that +we did take, but it must not contain the subtree that we’re currently +focusing on. That’s because we already have that subtree in the first +component of the tuple, so if we also had it in the breadcrumbs, we’d +have duplicate information.

        +

        Let’s modify our breadcrumbs so that they also contain information +about everything that we previously ignored when moving left and right. +Instead of Direction, we’ll make a new data type:

        +
        data Crumb a = LeftCrumb a (Tree a) | RightCrumb a (Tree a) deriving (Show)
        +

        Now, instead of just L, we have a LeftCrumb +that also contains the element in the node that we moved from and the +right tree that we didn’t visit. Instead of R, we have +RightCrumb, which contains the element in the node that we +moved from and the left tree that we didn’t visit.

        +

        These breadcrumbs now contain all the data needed to recreate the +tree that we walked through. So instead of just being normal bread +crumbs, they’re now more like floppy disks that we leave as we go along, +because they contain a lot more information than just the direction that +we took.

        +

        In essence, every breadcrumb is now like a tree node with a hole in +it. When we move deeper into a tree, the breadcrumb carries all the +information that the node that we moved away from carried +except the subtree that we chose to focus on. It also has to +note where the hole is. In the case of a LeftCrumb, we know +that we moved left, so the subtree that’s missing is the left one.

        +

        Let’s also change our Breadcrumbs type synonym to +reflect this:

        +
        type Breadcrumbs a = [Crumb a]
        +

        Next up, we have to modify the goLeft and +goRight functions to store information about the paths that +we didn’t take in our breadcrumbs, instead of ignoring that information +like they did before. Here’s goLeft:

        +
        goLeft :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
        +goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
        +

        You can see that it’s very similar to our previous +goLeft, only instead of just adding a L to the +head of our list of breadcrumbs, we add a LeftCrumb to +signify that we went left and we equip our LeftCrumb with +the element in the node that we moved from (that’s the x) +and the right subtree that we chose not to visit.

        +

        Note that this function assumes that the current tree that’s under +focus isn’t Empty. An empty tree doesn’t have any subtrees, +so if we try to go left from an empty tree, an error will occur because +the pattern match on Node won’t succeed and there’s no +pattern that takes care of Empty.

        +

        goRight is similar:

        +
        goRight :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
        +goRight (Node x l r, bs) = (r, RightCrumb x l:bs)
        +

        We were previously able to go left and right. What we’ve gotten now +is the ability to actually go back up by remembering stuff about the +parent nodes and the paths that we didn’t visit. Here’s the +goUp function:

        +
        goUp :: (Tree a, Breadcrumbs a) -> (Tree a, Breadcrumbs a)
         goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)
        -goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
        -
        - -asstronaut - -

        -We’re focusing on the tree t and we check what the -latest Crumb is. If it’s a LeftCrumb, -then we construct a new tree where our tree t is the left -subtree and we use the information about the right subtree that we didn’t -visit and the element to fill out the rest of the Node. -Because we moved back so to speak and picked up the last breadcrumb to recreate -with it the parent tree, the new list of breadcrumbs doesn’t contain it. -

        - -

        -Note that this function causes an error if we’re already at the top of a tree -and we want to move up. Later on, we’ll use the Maybe -monad to represent possible failure when moving focus. -

        - -

        -With a pair of Tree a and Breadcrumbs a, -we have all the information to rebuild the whole tree and we also have a focus -on a subtree. This scheme also enables us to easily move up, left and right. -Such a pair that contains a focused part of a data structure and its surroundings is -called a zipper, because moving our focus up and down the data structure resembles the -operation of a zipper on a regular pair of pants. So it’s cool to make a type -synonym as such: -

        - -
        
        -type Zipper a = (Tree a, Breadcrumbs a)
        -
        - -

        -I’d prefer naming the type synonym Focus because that -makes it clearer that we’re focusing on a part of a data structure, but the term -zipper is more widely used to describe such a setup, so we’ll stick with -Zipper. -

        - -

        Manipulating trees under focus

        - -

        -Now that we can move up and down, let’s make a function that -modifies the element in the root of the subtree that the zipper is focusing on: -

        - -
        
        -modify :: (a -> a) -> Zipper a -> Zipper a
        +goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
        +asstronaut +

        We’re focusing on the tree t and we check what the +latest Crumb is. If it’s a LeftCrumb, then we +construct a new tree where our tree t is the left subtree +and we use the information about the right subtree that we didn’t visit +and the element to fill out the rest of the Node. Because +we moved back so to speak and picked up the last breadcrumb to recreate +with it the parent tree, the new list of breadcrumbs doesn’t contain +it.

        +

        Note that this function causes an error if we’re already at the top +of a tree and we want to move up. Later on, we’ll use the +Maybe monad to represent possible failure when moving +focus.

        +

        With a pair of Tree a and Breadcrumbs a, we +have all the information to rebuild the whole tree and we also have a +focus on a subtree. This scheme also enables us to easily move up, left +and right. Such a pair that contains a focused part of a data structure +and its surroundings is called a zipper, because moving our focus up and +down the data structure resembles the operation of a zipper on a regular +pair of pants. So it’s cool to make a type synonym as such:

        +
        type Zipper a = (Tree a, Breadcrumbs a)
        +

        I’d prefer naming the type synonym Focus because that +makes it clearer that we’re focusing on a part of a data structure, but +the term zipper is more widely used to describe such a setup, so we’ll +stick with Zipper.

        +

        Manipulating trees under +focus

        +

        Now that we can move up and down, let’s make a function that modifies +the element in the root of the subtree that the zipper is focusing +on:

        +
        modify :: (a -> a) -> Zipper a -> Zipper a
         modify f (Node x l r, bs) = (Node (f x) l r, bs)
        -modify f (Empty, bs) = (Empty, bs)
        -
        - -

        -If we’re focusing on a node, we modify its root element with the function -f. If we’re focusing on an empty tree, we leave it as -it is. Now we can start off with a tree, move to anywhere we want and modify an -element, all while keeping focus on that element so that we can easily move -further up or down. An example: -

        - -
        
        -ghci> let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))
        -
        - -

        -We go left, then right and then modify the root element by replacing it with a -'P'. This reads even better if we use --:: -

        - -
        
        -ghci> let newFocus = (freeTree,[]) -: goLeft -: goRight -: modify (\_ -> 'P')
        -
        - -

        -We can then move up if we want and replace an -element with a mysterious 'X': -

        - -
        
        -ghci> let newFocus2 = modify (\_ -> 'X') (goUp newFocus)
        -
        - -

        -Or if we wrote it with -:: -

        - -
        
        -ghci> let newFocus2 = newFocus -: goUp -: modify (\_ -> 'X')
        -
        - -

        -Moving up is easy because the breadcrumbs that we leave form the part of the -data structure that we’re not focusing on, but it’s inverted, sort of like turning a -sock inside out. That’s why when we want to move up, we don’t have to start from -the root and make our way down, but we just take the top of our inverted tree, -thereby uninverting a part of it and adding it to our focus. -

        - -

        -Each node has two subtrees, even if those subtrees are empty trees. So if -we’re focusing on an empty subtree, one thing we can do is to replace it with a -non-empty subtree, thus attaching a tree to a leaf node. The code for this is -simple: -

        - -
        
        -attach :: Tree a -> Zipper a -> Zipper a
        -attach t (_, bs) = (t, bs)
        -
        - -

        -We take a tree and a zipper and return a new zipper that has its focus replaced -with the supplied tree. Not only can we extend trees this way by replacing empty -subtrees with new trees, we can also replace whole existing subtrees. Let’s -attach a tree to the far left of our freeTree: -

        - -
        
        -ghci> let farLeft = (freeTree,[]) -: goLeft -: goLeft -: goLeft -: goLeft
        -ghci> let newFocus = farLeft -: attach (Node 'Z' Empty Empty)
        -
        - -

        -newFocus is now focused on the tree that we just -attached and the rest of the tree lies inverted in the breadcrumbs. If we were -to use goUp to walk all the way to the top of the -tree, it would be the same tree as freeTree but with -an additional 'Z' on its far left. -

        - -

        I’m going straight to the top, oh yeah, up where the air is fresh and clean!

        - -

        -Making a function that walks all the way to the top of the tree, regardless of -what we’re focusing on, is really easy. Here it is: -

        - -
        
        -topMost :: Zipper a -> Zipper a
        +modify f (Empty, bs) = (Empty, bs)
        +

        If we’re focusing on a node, we modify its root element with the +function f. If we’re focusing on an empty tree, we leave it +as it is. Now we can start off with a tree, move to anywhere we want and +modify an element, all while keeping focus on that element so that we +can easily move further up or down. An example:

        +
        ghci> let newFocus = modify (\_ -> 'P') (goRight (goLeft (freeTree,[])))
        +

        We go left, then right and then modify the root element by replacing +it with a 'P'. This reads even better if we use +-::

        +
        ghci> let newFocus = (freeTree,[]) -: goLeft -: goRight -: modify (\_ -> 'P')
        +

        We can then move up if we want and replace an element with a +mysterious 'X':

        +
        ghci> let newFocus2 = modify (\_ -> 'X') (goUp newFocus)
        +

        Or if we wrote it with -::

        +
        ghci> let newFocus2 = newFocus -: goUp -: modify (\_ -> 'X')
        +

        Moving up is easy because the breadcrumbs that we leave form the part +of the data structure that we’re not focusing on, but it’s inverted, +sort of like turning a sock inside out. That’s why when we want to move +up, we don’t have to start from the root and make our way down, but we +just take the top of our inverted tree, thereby uninverting a part of it +and adding it to our focus.

        +

        Each node has two subtrees, even if those subtrees are empty trees. +So if we’re focusing on an empty subtree, one thing we can do is to +replace it with a non-empty subtree, thus attaching a tree to a leaf +node. The code for this is simple:

        +
        attach :: Tree a -> Zipper a -> Zipper a
        +attach t (_, bs) = (t, bs)
        +

        We take a tree and a zipper and return a new zipper that has its +focus replaced with the supplied tree. Not only can we extend trees this +way by replacing empty subtrees with new trees, we can also replace +whole existing subtrees. Let’s attach a tree to the far left of our +freeTree:

        +
        ghci> let farLeft = (freeTree,[]) -: goLeft -: goLeft -: goLeft -: goLeft
        +ghci> let newFocus = farLeft -: attach (Node 'Z' Empty Empty)
        +

        newFocus is now focused on the tree that we just +attached and the rest of the tree lies inverted in the breadcrumbs. If +we were to use goUp to walk all the way to the top of the +tree, it would be the same tree as freeTree but with an +additional 'Z' on its far left.

        +

        I’m +going straight to the top, oh yeah, up where the air is fresh and +clean!

        +

        Making a function that walks all the way to the top of the tree, +regardless of what we’re focusing on, is really easy. Here it is:

        +
        topMost :: Zipper a -> Zipper a
         topMost (t,[]) = (t,[])
        -topMost z = topMost (goUp z)
        -
        - -

        -If our trail of beefed up breadcrumbs is empty, this means that we’re already at -the root of our tree, so we just return the current focus. Otherwise, we go up -to get the focus of the parent node and then recursively apply -topMost to that. So now we can walk around our tree, going -left and right and up, applying modify and -attach as we go along and then when we’re done with -our modifications, we use topMost to focus on the -root of our tree and see the changes that we’ve done in proper perspective. -

        - - +topMost z = topMost (goUp z)
        +

        If our trail of beefed up breadcrumbs is empty, this means that we’re +already at the root of our tree, so we just return the current focus. +Otherwise, we go up to get the focus of the parent node and then +recursively apply topMost to that. So now we can walk +around our tree, going left and right and up, applying +modify and attach as we go along and then when +we’re done with our modifications, we use topMost to focus +on the root of our tree and see the changes that we’ve done in proper +perspective.

        Focusing on lists

        - -

        -Zippers can be used with pretty much any data structure, so it’s no surprise -that they can be used to focus on sub-lists of lists. After all, lists are -pretty much like trees, only where a node in a tree has an element (or not) and -several subtrees, a node in a list has an element and only a single sub-list. -When we implemented -our own lists, we defined our data type like so: -

        - -
        
        -data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
        -
        - -the best damn thing - -

        -Contrast this with our definition of our binary tree and it’s easy to see how -lists can be viewed as trees where each node has only one subtree. -

        - -

        -A list like [1,2,3] can be written as -1:2:3:[]. It consists of the head of the list, which -is 1 and then the list’s tail, which is -2:3:[]. In turn, 2:3:[] -also has a head, which is 2 and a tail, which is -3:[]. With 3:[], the +

        Zippers can be used with pretty much any data structure, so it’s no +surprise that they can be used to focus on sub-lists of lists. After +all, lists are pretty much like trees, only where a node in a tree has +an element (or not) and several subtrees, a node in a list has an +element and only a single sub-list. When we implemented +our own lists, we defined our data type like so:

        +
        data List a = Empty | Cons a (List a) deriving (Show, Read, Eq, Ord)
        +the best damn thing +

        Contrast this with our definition of our binary tree and it’s easy to +see how lists can be viewed as trees where each node has only one +subtree.

        +

        A list like [1,2,3] can be written as +1:2:3:[]. It consists of the head of the list, which is +1 and then the list’s tail, which is 2:3:[]. +In turn, 2:3:[] also has a head, which is 2 +and a tail, which is 3:[]. With 3:[], the 3 is the head and the tail is the empty list -[]. -

        - -

        -Let’s make a zipper for lists. To change the focus on sub-lists of a list, we move either -forward or back (whereas with trees we moved either up or left or right). The -focused part will be a subtree and along with that we’ll leave breadcrumbs as -we move forward. Now what would a single breadcrumb for a list consist of? When -we were dealing with binary trees, we said that a breadcrumb has to hold the -element in the root of the parent node along with all the subtrees that we -didn’t choose. It also had to remember if we went left or right. So, it had to -have all the information that a node has except for the subtree that we chose -to focus on. -

        - -

        -Lists are simpler than trees, so we don’t have to remember if we -went left or right, because there’s only one way to go deeper into a list. -Because there’s only one subtree to each node, we don’t have to remember the -paths that we didn’t take either. It seems that all we have to remember is the -previous element. If we have a list like [3,4,5] and -we know that the previous element was 2, we can go -back by just putting that element at the head of our list, getting -[2,3,4,5]. -

        - -

        -Because a single breadcrumb here is just the element, we don’t really have to -put it inside a data type, like we did when we made the -Crumb data type for tree zippers: -

        - -
        
        -type ListZipper a = ([a],[a])
        -
        - -

        -The first list represents the list that we’re focusing on and the second list -is the list of breadcrumbs. Let’s make functions that go forward and back -into lists: -

        - -
        
        -goForward :: ListZipper a -> ListZipper a
        +[].

        +

        Let’s make a zipper for lists. To change the focus on sub-lists of a +list, we move either forward or back (whereas with trees we moved either +up or left or right). The focused part will be a subtree and along with +that we’ll leave breadcrumbs as we move forward. Now what would a single +breadcrumb for a list consist of? When we were dealing with binary +trees, we said that a breadcrumb has to hold the element in the root of +the parent node along with all the subtrees that we didn’t choose. It +also had to remember if we went left or right. So, it had to have all +the information that a node has except for the subtree that we chose to +focus on.

        +

        Lists are simpler than trees, so we don’t have to remember if we went +left or right, because there’s only one way to go deeper into a list. +Because there’s only one subtree to each node, we don’t have to remember +the paths that we didn’t take either. It seems that all we have to +remember is the previous element. If we have a list like +[3,4,5] and we know that the previous element was +2, we can go back by just putting that element at the head +of our list, getting [2,3,4,5].

        +

        Because a single breadcrumb here is just the element, we don’t really +have to put it inside a data type, like we did when we made the +Crumb data type for tree zippers:

        +
        type ListZipper a = ([a],[a])
        +

        The first list represents the list that we’re focusing on and the +second list is the list of breadcrumbs. Let’s make functions that go +forward and back into lists:

        +
        goForward :: ListZipper a -> ListZipper a
         goForward (x:xs, bs) = (xs, x:bs)
         
         goBack :: ListZipper a -> ListZipper a
        -goBack (xs, b:bs) = (b:xs, bs)
        -
        - -

        -When we’re going forward, we focus on the tail of the current list and leave the -head element as a breadcrumb. When we’re moving backwards, we take the latest -breadcrumb and put it at the beginning of the list. -

        - -

        -Here are these two functions in action: -

        - -
        
        -ghci> let xs = [1,2,3,4]
        +goBack (xs, b:bs) = (b:xs, bs)
        +

        When we’re going forward, we focus on the tail of the current list +and leave the head element as a breadcrumb. When we’re moving backwards, +we take the latest breadcrumb and put it at the beginning of the +list.

        +

        Here are these two functions in action:

        +
        ghci> let xs = [1,2,3,4]
         ghci> goForward (xs,[])
         ([2,3,4],[1])
         ghci> goForward ([2,3,4],[1])
        @@ -684,67 +423,40 @@ 

        Focusing on lists

        ghci> goForward ([3,4],[2,1]) ([4],[3,2,1]) ghci> goBack ([4],[3,2,1]) -([3,4],[2,1]) -
        - -

        -We see that the breadcrumbs in the case of lists are nothing more but a reversed -part of our list. The element that we move away from always goes into the head -of the breadcrumbs, so it’s easy to move back by just taking that element from -the head of the breadcrumbs and making it the head of our focus. -

        - -

        -This also makes it easier to see why we call this a zipper, because this really -looks like the slider of a zipper moving up and down. -

        - -

        -If you were making a text editor, you could use a list of strings to represent -the lines of text that are currently opened and you could then use a zipper so -that you know which line the cursor is currently focused on. By using a zipper, -it would also make it easier to insert new lines anywhere in the text or delete -existing ones. -

        - - +([3,4],[2,1])
        +

        We see that the breadcrumbs in the case of lists are nothing more but +a reversed part of our list. The element that we move away from always +goes into the head of the breadcrumbs, so it’s easy to move back by just +taking that element from the head of the breadcrumbs and making it the +head of our focus.

        +

        This also makes it easier to see why we call this a zipper, because +this really looks like the slider of a zipper moving up and down.

        +

        If you were making a text editor, you could use a list of strings to +represent the lines of text that are currently opened and you could then +use a zipper so that you know which line the cursor is currently focused +on. By using a zipper, it would also make it easier to insert new lines +anywhere in the text or delete existing ones.

        A very simple file system

        - -

        -Now that we know how zippers work, let’s use trees to represent a very simple -file system and then make a zipper for that file system, which will allow us to -move between folders, just like we usually do when jumping around our file -system. -

        - -

        -If we take a simplistic view of the average hierarchical file system, we see -that it’s mostly made up of files and folders. Files are units of data and come -with a name, whereas folders are used to organize those files and can contain -files or other folders. So let’s say that an item in a file system is either a -file, which comes with a name and some data, or a folder, which has a name and -then a bunch of items that are either files or folders themselves. Here’s a data -type for this and some type synonyms so we know what’s what: -

        - -
        
        -type Name = String
        +

        Now that we know how zippers work, let’s use trees to represent a +very simple file system and then make a zipper for that file system, +which will allow us to move between folders, just like we usually do +when jumping around our file system.

        +

        If we take a simplistic view of the average hierarchical file system, +we see that it’s mostly made up of files and folders. Files are units of +data and come with a name, whereas folders are used to organize those +files and can contain files or other folders. So let’s say that an item +in a file system is either a file, which comes with a name and some +data, or a folder, which has a name and then a bunch of items that are +either files or folders themselves. Here’s a data type for this and some +type synonyms so we know what’s what:

        +
        type Name = String
         type Data = String
        -data FSItem = File Name Data | Folder Name [FSItem] deriving (Show)
        -
        - -

        -A file comes with two strings, which represent its name and the data it holds. A -folder comes with a string that is its name and a list of items. If that list is -empty, then we have an empty folder. -

        - -

        -Here’s a folder with some files and sub-folders: -

        - -
        
        -myDisk :: FSItem
        +data FSItem = File Name Data | Folder Name [FSItem] deriving (Show)
        +

        A file comes with two strings, which represent its name and the data +it holds. A folder comes with a string that is its name and a list of +items. If that list is empty, then we have an empty folder.

        +

        Here’s a folder with some files and sub-folders:

        +
        myDisk :: FSItem
         myDisk =
             Folder "root"
                 [ File "goat_yelling_like_man.wmv" "baaaaaa"
        @@ -764,97 +476,56 @@ 

        A very simple file system

        , File "random.hs" "main = print 4" ] ] - ] -
        - -

        -That’s actually what my disk contains right now. -

        - + ]
        +

        That’s actually what my disk contains right now.

        A zipper for our file system

        - -spongedisk - -

        -Now that we have a file system, all we need is a zipper so we can zip and zoom -around it and add, modify and remove files as well as folders. Like with binary -trees and lists, we’re going to be leaving breadcrumbs that contain info about -all the stuff that we chose not to visit. Like we said, a single breadcrumb -should be kind of like a node, only it should contain everything except the -subtree that we’re currently focusing on. It should also note where the hole -is so that once we move back up, we can plug our previous focus into the hole. -

        - -

        -In this case, a breadcrumb should be like a folder, only it should be missing -the folder that we currently chose. Why not like a file, you ask? Well, because -once we’re focusing on a file, we can’t move deeper into the file system, so it -doesn’t make sense to leave a breadcrumb that says that we came from a file. A -file is sort of like an empty tree. -

        - -

        -If we’re focusing on the folder "root" and we then -focus on the file "dijon_poupon.doc", what should the -breadcrumb that we leave look like? Well, it should contain the name of its -parent folder along with the items that come before the file that we’re focusing -on and the items that come after it. So all we need is a +spongedisk +

        Now that we have a file system, all we need is a zipper so we can zip +and zoom around it and add, modify and remove files as well as folders. +Like with binary trees and lists, we’re going to be leaving breadcrumbs +that contain info about all the stuff that we chose not to visit. Like +we said, a single breadcrumb should be kind of like a node, only it +should contain everything except the subtree that we’re currently +focusing on. It should also note where the hole is so that once we move +back up, we can plug our previous focus into the hole.

        +

        In this case, a breadcrumb should be like a folder, only it should be +missing the folder that we currently chose. Why not like a file, you +ask? Well, because once we’re focusing on a file, we can’t move deeper +into the file system, so it doesn’t make sense to leave a breadcrumb +that says that we came from a file. A file is sort of like an empty +tree.

        +

        If we’re focusing on the folder "root" and we then focus +on the file "dijon_poupon.doc", what should the breadcrumb +that we leave look like? Well, it should contain the name of its parent +folder along with the items that come before the file that we’re +focusing on and the items that come after it. So all we need is a Name and two lists of items. By keeping separate lists for -the items that come before the item that we’re focusing and for the items that -come after it, we know exactly where to place it once we move back up. So this -way, we know where the hole is. -

        - -

        -Here’s our breadcrumb type for the file system: -

        - -
        
        -data FSCrumb = FSCrumb Name [FSItem] [FSItem] deriving (Show)
        -
        - -

        -And here’s a type synonym for our zipper: -

        - -
        
        -type FSZipper = (FSItem, [FSCrumb])
        -
        - -

        -Going back up in the hierarchy is very simple. We just take the latest -breadcrumb and assemble a new focus from the current focus and breadcrumb. -Like so: -

        - -
        
        -fsUp :: FSZipper -> FSZipper
        -fsUp (item, FSCrumb name ls rs:bs) = (Folder name (ls ++ [item] ++ rs), bs)
        -
        - -

        -Because our breadcrumb knew what the parent folder’s name was, as well as the -items that came before our focused item in the folder (that’s -ls) and the ones that came after (that’s -rs), moving up was easy. -

        - -

        -How about going deeper into the file system? If we’re in the +the items that come before the item that we’re focusing and for the +items that come after it, we know exactly where to place it once we move +back up. So this way, we know where the hole is.

        +

        Here’s our breadcrumb type for the file system:

        +
        data FSCrumb = FSCrumb Name [FSItem] [FSItem] deriving (Show)
        +

        And here’s a type synonym for our zipper:

        +
        type FSZipper = (FSItem, [FSCrumb])
        +

        Going back up in the hierarchy is very simple. We just take the +latest breadcrumb and assemble a new focus from the current focus and +breadcrumb. Like so:

        +
        fsUp :: FSZipper -> FSZipper
        +fsUp (item, FSCrumb name ls rs:bs) = (Folder name (ls ++ [item] ++ rs), bs)
        +

        Because our breadcrumb knew what the parent folder’s name was, as +well as the items that came before our focused item in the folder +(that’s ls) and the ones that came after (that’s +rs), moving up was easy.

        +

        How about going deeper into the file system? If we’re in the "root" and we want to focus on -"dijon_poupon.doc", the breadcrumb that we leave is going to -include the name "root" along with the items that -precede "dijon_poupon.doc" and the ones that come -after it. -

        - -

        -Here’s a function that, given a name, focuses on a file of folder that’s located -in the current focused folder: -

        - -
        
        -import Data.List (break)
        +"dijon_poupon.doc", the breadcrumb that we leave is going
        +to include the name "root" along with the items that
        +precede "dijon_poupon.doc" and the ones that come after
        +it.

        +

        Here’s a function that, given a name, focuses on a file of folder +that’s located in the current focused folder:

        +
        import Data.List (break)
         
         fsTo :: Name -> FSZipper -> FSZipper
         fsTo name (Folder folderName items, bs) =
        @@ -863,309 +534,185 @@ 

        A zipper for our file system

        nameIs :: Name -> FSItem -> Bool nameIs name (Folder folderName _) = name == folderName -nameIs name (File fileName _) = name == fileName -
        - -

        -fsTo takes a Name and a -FSZipper -and returns a new FSZipper that focuses on the file -with the given name. That file has to be in the current focused folder. This -function doesn’t search all over the place, it just looks at the current folder. -

        - -wow cool great - -

        -First we use break to break the list of items in a -folder into those that precede the file that we’re searching for and those that -come after it. If you remember, break takes a -predicate and a list and returns a pair of lists. The first list in the pair -holds items for which the predicate returns False. +nameIs name (File fileName _) = name == fileName

        +

        fsTo takes a Name and a +FSZipper and returns a new FSZipper that +focuses on the file with the given name. That file has to be in the +current focused folder. This function doesn’t search all over the place, +it just looks at the current folder.

        +wow cool great +

        First we use break to break the list of items in a +folder into those that precede the file that we’re searching for and +those that come after it. If you remember, break takes a +predicate and a list and returns a pair of lists. The first list in the +pair holds items for which the predicate returns False. Then, once the predicate returns True for an item, it -places that item and the rest of the list in the second item of the pair. We -made an auxilliary function called nameIs that takes -a name and a file system item and returns True if -the names match. -

        - -

        -So now, ls is a list that contains the items that -precede the item that we’re searching for, item is -that very item and rs is the list of items that come -after it in its folder. Now that we have this, we just present the item that we -got from break as the focus and build a breadcrumb -that has all the data it needs. -

        - -

        -Note that if the name we’re looking for isn’t in the folder, the pattern -item:rs will try to match on an empty list and we’ll -get an error. Also, if our current focus isn’t a folder at all but a file, we -get an error as well and the program crashes. -

        - -

        -Now we can move up and down our file system. Let’s start at the root and walk to -the file "skull_man(scary).bmp": -

        - -
        
        -ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsTo "skull_man(scary).bmp"
        -
        - -

        -newFocus is now a zipper that’s focused on the -"skull_man(scary).bmp" file. Let’s get the first -component of the zipper (the focus itself) and see if that’s really true: -

        - -
        
        -ghci> fst newFocus
        -File "skull_man(scary).bmp" "Yikes!"
        -
        - -

        -Let’s move up and then focus on its neighboring file -"watermelon_smash.gif": -

        - -
        
        -ghci> let newFocus2 = newFocus -: fsUp -: fsTo "watermelon_smash.gif"
        +places that item and the rest of the list in the second item of the
        +pair. We made an auxilliary function called nameIs that
        +takes a name and a file system item and returns True if the
        +names match.

        +

        So now, ls is a list that contains the items that +precede the item that we’re searching for, item is that +very item and rs is the list of items that come after it in +its folder. Now that we have this, we just present the item that we got +from break as the focus and build a breadcrumb that has all +the data it needs.

        +

        Note that if the name we’re looking for isn’t in the folder, the +pattern item:rs will try to match on an empty list and +we’ll get an error. Also, if our current focus isn’t a folder at all but +a file, we get an error as well and the program crashes.

        +

        Now we can move up and down our file system. Let’s start at the root +and walk to the file "skull_man(scary).bmp":

        +
        ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsTo "skull_man(scary).bmp"
        +

        newFocus is now a zipper that’s focused on the +"skull_man(scary).bmp" file. Let’s get the first component +of the zipper (the focus itself) and see if that’s really true:

        +
        ghci> fst newFocus
        +File "skull_man(scary).bmp" "Yikes!"
        +

        Let’s move up and then focus on its neighboring file +"watermelon_smash.gif":

        +
        ghci> let newFocus2 = newFocus -: fsUp -: fsTo "watermelon_smash.gif"
         ghci> fst newFocus2
        -File "watermelon_smash.gif" "smash!!"
        -
        - +File "watermelon_smash.gif" "smash!!"

        Manipulating our file system

        - -

        -Now that we know how to navigate our file system, manipulating it is easy. -Here’s a function that renames the currently focused file or folder: -

        - -
        
        -fsRename :: Name -> FSZipper -> FSZipper
        +

        Now that we know how to navigate our file system, manipulating it is +easy. Here’s a function that renames the currently focused file or +folder:

        +
        fsRename :: Name -> FSZipper -> FSZipper
         fsRename newName (Folder name items, bs) = (Folder newName items, bs)
        -fsRename newName (File name dat, bs) = (File newName dat, bs)
        -
        - -

        -Now we can rename our "pics" folder to -"cspi": -

        - -
        
        -ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsRename "cspi" -: fsUp
        -
        - -

        -We descended to the "pics" folder, renamed it and -then moved back up. -

        - -

        -How about a function that makes a new item in the current folder? Behold: -

        - -
        
        -fsNewFile :: FSItem -> FSZipper -> FSZipper
        +fsRename newName (File name dat, bs) = (File newName dat, bs)
        +

        Now we can rename our "pics" folder to +"cspi":

        +
        ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsRename "cspi" -: fsUp
        +

        We descended to the "pics" folder, renamed it and then +moved back up.

        +

        How about a function that makes a new item in the current folder? +Behold:

        +
        fsNewFile :: FSItem -> FSZipper -> FSZipper
         fsNewFile item (Folder folderName items, bs) =
        -    (Folder folderName (item:items), bs)
        -
        - -

        -Easy as pie. Note that this would crash if we tried to add an item but weren’t -focusing on a folder, but were focusing on a file instead. -

        - -

        -Let’s add a file to our "pics" folder and then move -back up to the root: -

        - -
        
        -ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsNewFile (File "heh.jpg" "lol") -: fsUp
        -
        - -

        -What’s really cool about all this is that when we modify our file system, it -doesn’t actually modify it in place but it returns a whole new file system. That way, we -have access to our old file system (in this case, myDisk) -as well as the new one (the first component of newFocus). -So by using zippers, we get versioning for free, meaning that we can always -refer to older versions of data structures even after we’ve changed them, so to -speak. This isn’t unique to zippers, but is a property of Haskell because its -data structures are immutable. With zippers however, we get the ability to -easily and efficiently walk around our data structures, so the persistence of -Haskell’s data structures really begins to shine. -

        - - + (Folder folderName (item:items), bs)
        +

        Easy as pie. Note that this would crash if we tried to add an item +but weren’t focusing on a folder, but were focusing on a file +instead.

        +

        Let’s add a file to our "pics" folder and then move back +up to the root:

        +
        ghci> let newFocus = (myDisk,[]) -: fsTo "pics" -: fsNewFile (File "heh.jpg" "lol") -: fsUp
        +

        What’s really cool about all this is that when we modify our file +system, it doesn’t actually modify it in place but it returns a whole +new file system. That way, we have access to our old file system (in +this case, myDisk) as well as the new one (the first +component of newFocus). So by using zippers, we get +versioning for free, meaning that we can always refer to older versions +of data structures even after we’ve changed them, so to speak. This +isn’t unique to zippers, but is a property of Haskell because its data +structures are immutable. With zippers however, we get the ability to +easily and efficiently walk around our data structures, so the +persistence of Haskell’s data structures really begins to shine.

        Watch your step

        - -

        -So far, while walking through our data structures, whether they were binary -trees, lists or file systems, we didn’t really care if we took a step too far -and fell off. For instance, our goLeft function takes -a zipper of a binary tree and moves the focus to its left subtree: -

        - -
        
        -goLeft :: Zipper a -> Zipper a
        -goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
        -
        - -falling for you - -

        -But what if the tree we’re stepping off from is an empty tree? That is, what if -it’s not a Node, but an Empty? -In this case, we’d get a runtime error because the pattern match would fail and -we have made no pattern to handle an empty tree, which doesn’t have any -subtrees at all. So far, we just assumed that we’d never try to focus on the -left subtree of an empty tree as its left subtree doesn’t exist at all. But -going to the left subtree of an empty tree doesn’t make much sense, and so far -we’ve just conveniently ignored this. -

        - -

        -Or what if we were already at the root of some tree and didn’t have any -breadcrumbs but still tried to move up? The same thing would happen. It seems -that when using zippers, any step could be our last (cue ominous music). In -other words, any move can result in a success, but it can also result in a -failure. Does that remind you of something? Of course, monads! More -specifically, the Maybe monad which adds a context of -possible failure to normal values. -

        - -

        -So let’s use the Maybe monad to add a context of -possible failure to our movements. We’re going to take the functions that work -on our binary tree zipper and we’re going to make them into monadic functions. -First, let’s take care of possible failure in goLeft -and goRight. So far, the failure of functions that -could fail was always reflected in their result, and this time is no -different. So here are goLeft and -goRight with an added possibility of failure: -

        - -
        
        -goLeft :: Zipper a -> Maybe (Zipper a)
        +

        So far, while walking through our data structures, whether they were +binary trees, lists or file systems, we didn’t really care if we took a +step too far and fell off. For instance, our goLeft +function takes a zipper of a binary tree and moves the focus to its left +subtree:

        +
        goLeft :: Zipper a -> Zipper a
        +goLeft (Node x l r, bs) = (l, LeftCrumb x r:bs)
        +falling for you +

        But what if the tree we’re stepping off from is an empty tree? That +is, what if it’s not a Node, but an Empty? In +this case, we’d get a runtime error because the pattern match would fail +and we have made no pattern to handle an empty tree, which doesn’t have +any subtrees at all. So far, we just assumed that we’d never try to +focus on the left subtree of an empty tree as its left subtree doesn’t +exist at all. But going to the left subtree of an empty tree doesn’t +make much sense, and so far we’ve just conveniently ignored this.

        +

        Or what if we were already at the root of some tree and didn’t have +any breadcrumbs but still tried to move up? The same thing would happen. +It seems that when using zippers, any step could be our last (cue +ominous music). In other words, any move can result in a success, but it +can also result in a failure. Does that remind you of something? Of +course, monads! More specifically, the Maybe monad which +adds a context of possible failure to normal values.

        +

        So let’s use the Maybe monad to add a context of +possible failure to our movements. We’re going to take the functions +that work on our binary tree zipper and we’re going to make them into +monadic functions. First, let’s take care of possible failure in +goLeft and goRight. So far, the failure of +functions that could fail was always reflected in their result, and this +time is no different. So here are goLeft and +goRight with an added possibility of failure:

        +
        goLeft :: Zipper a -> Maybe (Zipper a)
         goLeft (Node x l r, bs) = Just (l, LeftCrumb x r:bs)
         goLeft (Empty, _) = Nothing
         
         goRight :: Zipper a -> Maybe (Zipper a)
         goRight (Node x l r, bs) = Just (r, RightCrumb x l:bs)
        -goRight (Empty, _) = Nothing
        -
        - -

        -Cool, now if we try to take a step to the left of an empty tree, we get a -Nothing! -

        - -
        
        -ghci> goLeft (Empty, [])
        +goRight (Empty, _) = Nothing
        +

        Cool, now if we try to take a step to the left of an empty tree, we +get a Nothing!

        +
        ghci> goLeft (Empty, [])
         Nothing
         ghci> goLeft (Node 'A' Empty Empty, [])
        -Just (Empty,[LeftCrumb 'A' Empty])
        -
        - -

        -Looks good! How about going up? The problem before happened if we tried to go up but -we didn’t have any more breadcrumbs, which meant that we were already in the -root of the tree. This is the goUp function that -throws an error if we don’t keep within the bounds of our tree: -

        - -
        
        -goUp :: Zipper a -> Zipper a
        +Just (Empty,[LeftCrumb 'A' Empty])
        +

        Looks good! How about going up? The problem before happened if we +tried to go up but we didn’t have any more breadcrumbs, which meant that +we were already in the root of the tree. This is the goUp +function that throws an error if we don’t keep within the bounds of our +tree:

        +
        goUp :: Zipper a -> Zipper a
         goUp (t, LeftCrumb x r:bs) = (Node x t r, bs)
        -goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
        -
        - -

        -Now let’s modify it to fail gracefully: -

        - -
        
        -goUp :: Zipper a -> Maybe (Zipper a)
        +goUp (t, RightCrumb x l:bs) = (Node x l t, bs)
        +

        Now let’s modify it to fail gracefully:

        +
        goUp :: Zipper a -> Maybe (Zipper a)
         goUp (t, LeftCrumb x r:bs) = Just (Node x t r, bs)
         goUp (t, RightCrumb x l:bs) = Just (Node x l t, bs)
        -goUp (_, []) = Nothing
        -
        - -

        -If we have breadcrumbs, everything is okay and we return a successful new focus, -but if we don’t, then we return a failure. -

        - -

        -Before, these functions took zippers and returned zippers, which meant that we -could chain them like this to walk around: -

        - -
        
        -gchi> let newFocus = (freeTree,[]) -: goLeft -: goRight
        -
        - -

        -But now, instead of returning Zipper a, they return -Maybe (Zipper a), so chaining functions like this -won’t work. We had a similar problem when we -were dealing with our tightrope -walker in the chapter about monads. He also walked one step at a time and -each of his steps could result in failure because a bunch of birds could land on -one side of his balancing pole and make him fall. -

        - -

        -Now, the joke’s on us because -we’re the ones doing the walking, and we’re traversing a labyrinth of our own -devising. Luckily, we can learn from the tightrope walker and just do what he -did, which is to exchange normal -function application for using >>=, which takes -a value with a context (in our case, the Maybe (Zipper a), -which has a context of possible failure) and feeds it into a function while -making sure that the context is taken care of. So just like our tightrope walker, we’re -going to trade in all our -: operators for ->>=. Alright, we can chain our functions again! -Watch: -

        - -
        
        -ghci> let coolTree = Node 1 Empty (Node 3 Empty Empty)
        +goUp (_, []) = Nothing
        +

        If we have breadcrumbs, everything is okay and we return a successful +new focus, but if we don’t, then we return a failure.

        +

        Before, these functions took zippers and returned zippers, which +meant that we could chain them like this to walk around:

        +
        gchi> let newFocus = (freeTree,[]) -: goLeft -: goRight
        +

        But now, instead of returning Zipper a, they return +Maybe (Zipper a), so chaining functions like this won’t +work. We had a similar problem when we were dealing with our tightrope +walker in the chapter about monads. He also walked one step at a +time and each of his steps could result in failure because a bunch of +birds could land on one side of his balancing pole and make him +fall.

        +

        Now, the joke’s on us because we’re the ones doing the walking, and +we’re traversing a labyrinth of our own devising. Luckily, we can learn +from the tightrope walker and just do what he did, which is to exchange +normal function application for using >>=, which +takes a value with a context (in our case, the +Maybe (Zipper a), which has a context of possible failure) +and feeds it into a function while making sure that the context is taken +care of. So just like our tightrope walker, we’re going to trade in all +our -: operators for >>=. Alright, we +can chain our functions again! Watch:

        +
        ghci> let coolTree = Node 1 Empty (Node 3 Empty Empty)
         ghci> return (coolTree,[]) >>= goRight
         Just (Node 3 Empty Empty,[RightCrumb 1 Empty])
         ghci> return (coolTree,[]) >>= goRight >>= goRight
         Just (Empty,[RightCrumb 3 Empty,RightCrumb 1 Empty])
         ghci> return (coolTree,[]) >>= goRight >>= goRight >>= goRight
        -Nothing
        -
        - -

        -We used return to put a zipper in a -Just and then used >>= to -feed that to our goRight function. First, we made a -tree that has on its left an empty subtree and on its right a node that has two -empty subtrees. When we try to go right once, the result is a success, because -the operation makes sense. Going right twice is okay too; we end up with the -focus on an empty subtree. But going right three times wouldn’t make sense, -because we can’t go to the right of an empty subtree, which is why the -result is a Nothing. -

        - -

        -Now we’ve equipped our trees with a safety-net that will catch us should -we fall off. Wow, I nailed this metaphor. -

        - -

        -Our file system also has a lot of cases where an operation could fail, such as -trying to focus on a file or folder that doesn’t exist. As an exercise, you can -equip our file system with functions that fail gracefully by using the -Maybe monad. -

        +Nothing
        +

        We used return to put a zipper in a Just +and then used >>= to feed that to our +goRight function. First, we made a tree that has on its +left an empty subtree and on its right a node that has two empty +subtrees. When we try to go right once, the result is a success, because +the operation makes sense. Going right twice is okay too; we end up with +the focus on an empty subtree. But going right three times wouldn’t make +sense, because we can’t go to the right of an empty subtree, which is +why the result is a Nothing.

        +

        Now we’ve equipped our trees with a safety-net that will catch us +should we fall off. Wow, I nailed this metaphor.

        +

        Our file system also has a lot of cases where an operation could +fail, such as trying to focus on a file or folder that doesn’t exist. As +an exercise, you can equip our file system with functions that fail +gracefully by using the Maybe monad.