From 5aa90a5ccb2cd4f6d819ae4727ceda344c689ea8 Mon Sep 17 00:00:00 2001 From: Oliver Webb Date: Mon, 23 Dec 2024 09:49:34 -0600 Subject: [PATCH 1/2] re-generate html, concatenate sed commands, remove trailing whitespace --- markdown/generate.sh | 42 + ...tors-applicative-functors-and-monoids.html | 2414 +++++++++++++++++ .../higher-order-functions.html | 992 +++++++ markdown/generated_html/introduction.html | 200 ++ .../making-our-own-types-and-typeclasses.html | 1906 +++++++++++++ markdown/generated_html/starting-out.html | 801 ++++++ .../generated_html/types-and-typeclasses.html | 445 +++ 7 files changed, 6800 insertions(+) create mode 100644 markdown/generated_html/functors-applicative-functors-and-monoids.html create mode 100644 markdown/generated_html/higher-order-functions.html create mode 100644 markdown/generated_html/introduction.html create mode 100644 markdown/generated_html/making-our-own-types-and-typeclasses.html create mode 100644 markdown/generated_html/starting-out.html create mode 100644 markdown/generated_html/types-and-typeclasses.html diff --git a/markdown/generate.sh b/markdown/generate.sh index 4048c47..437f9c3 100755 --- a/markdown/generate.sh +++ b/markdown/generate.sh @@ -30,9 +30,19 @@ cp source_md/chapters_head.md $chapterfile # Generate temporary TOC file ($chapterfile) for i in "${!filename[@]}" do +<<<<<<< HEAD sourcemd=$IN/${filename[$i]}.md title[i]=$(sed -n '/^# /s/# //p;' "$sourcemd" | sed 's/{.*//' | sed 's/ *$//g') +||||||| parent of c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) + sourcemd=source_md/${filename[$i]}.md + + title[$i]=$(sed -n '/^# /s/# //p;' $sourcemd | sed 's/{.*//' | sed 's/ *$//g') +======= + sourcemd=source_md/${filename[$i]}.md + + title[$i]=$(sed -n '/^# /s/# //p;' $sourcemd | sed 's/{.*//; s/ *$//g') +>>>>>>> c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) chnum=$((i + 1)) if [[ $chnum -ge 10 ]]; @@ -42,11 +52,25 @@ do sp=" " fi +<<<<<<< HEAD sed -n '/^#\{1,2\} /p' "$sourcemd" \ | sed "s/^# *\(.*[^ ]\) *{.*/$chnum.${sp}[\1](${filename[$i]}.html)/" \ | sed "s/^# *\(.*[^ ]\) */$chnum.${sp}[\1](${filename[$i]}.html)/" \ | sed "s/^## *\(.*[^ ]\) *{ *#\(.*\)}/ * [\1](${filename[$i]}.html\#\2)/" \ >>$chapterfile +||||||| parent of c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) + sed -n '/^#\{1,2\} /p' $sourcemd \ + | sed "s/^# *\(.*[^ ]\) *{.*/$chnum.$sp[\1](${filename[$i]}.html)/" \ + | sed "s/^# *\(.*[^ ]\) */$chnum.$sp[\1](${filename[$i]}.html)/" \ + | sed "s/^## *\(.*[^ ]\) *{ *#\(.*\)}/ * [\1](${filename[$i]}.html\#\2)/" \ + >>$chapterfile +======= + grep '^#\{1,2\} ' $sourcemd \ + | sed "s/^# *\(.*[^ ]\) *{.*/$chnum.$sp[\1](${filename[$i]}.html)/; + s/^# *\(.*[^ ]\) */$chnum.$sp[\1](${filename[$i]}.html)/; + s/^## *\(.*[^ ]\) *{ *#\(.*\)}/ * [\1](${filename[$i]}.html\#\2)/" \ + >> $chapterfile +>>>>>>> c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) done # For every input MD file produce and HTML file @@ -71,16 +95,34 @@ do next_title="${title[$next]}" next_filename=${filename[$next]} fi +<<<<<<< HEAD $PANDOC \ +||||||| parent of c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) + + pandoc -d config/pandoc-defaults.yml --template=config/template.html \ +======= + + pandoc -d config/pandoc-defaults.yml --template=config/template.html \ +>>>>>>> c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) -V footdiv=true -V title="${title[$i]}" \ --metadata title="${title[$i]}$titlesuffix" \ +<<<<<<< HEAD -V prev_title="$prev_title" -V prev_filename="$prev_filename" \ -V next_title="$next_title" -V next_filename="$next_filename" \ -o "$OUT"/"${filename[$i]}".html "$IN"/"${filename[$i]}".md sed '/

\(]*\) />

#\1># } s# />#>#' -i "$OUT"/"${filename[$i]}".html +||||||| parent of c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) + -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 +======= + -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 +>>>>>>> c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) done cat "$IN"/chapters_foot.md >>$chapterfile diff --git a/markdown/generated_html/functors-applicative-functors-and-monoids.html b/markdown/generated_html/functors-applicative-functors-and-monoids.html new file mode 100644 index 0000000..93a501b --- /dev/null +++ b/markdown/generated_html/functors-applicative-functors-and-monoids.html @@ -0,0 +1,2414 @@ + + + +Functors, Applicative Functors and Monoids - Learn You a Haskell for Great Good! + + + + + + + + + + +
+
+ +

Functors, Applicative +Functors and Monoids

+

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

+

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

+

Functors redux

+

+

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

+

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

+
+

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

+
+

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

+

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

+

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

+

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

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

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

+

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

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

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

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

w00ooOoooOO

+

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

+

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

+

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

+

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

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

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

+

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

+
+

We usually mark functions that take anything and return anything as +a -> b. r -> a is the same thing, we +just used different letters for the type variables.

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

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

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

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

+

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

+

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

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

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

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

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

+

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

+

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

+

+

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

+

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

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

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

+
+

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

+
+

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

+

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

+

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

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

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

+

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

+

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

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

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

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

We imagine that id plays the role of the f +parameter in the implementation. We see that if we 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"
+ghci> :t CNothing
+CNothing :: CMaybe a
+ghci> :t CJust 0 "haha"
+CJust 0 "haha" :: CMaybe [Char]
+ghci> CJust 100 [1,2,3]
+CJust 100 [1,2,3]
+

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

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

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

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

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

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

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

+

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

+

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

+

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

+

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

+

Applicative functors

+

+

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

+

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

+

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

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

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

+

We see how by mapping “multi-parameter” functions over functors, we +get functors that contain functions inside them. So now what can we do +with them? Well for one, we can map functions that take these functions +as parameters over them, because whatever is inside a functor will be +given to the function that we’re mapping over it as a parameter.

+
ghci> let a = fmap (*) [1,2,3,4]
+ghci> :t a
+a :: [Integer -> Integer]
+ghci> fmap (\f -> f 9) a
+[9,18,27,36]
+

But what if we have a functor value of Just (3 *) and a +functor value of Just 5 and we want to take out the +function from Just (3 *) and map it over +Just 5? With normal functors, we’re out of luck, because +all they support is just mapping normal functions over existing +functors. Even when we mapped \f -> f 9 over a functor +that contained functions inside it, we were just mapping a normal +function over it. But we can’t map a function that’s inside a functor +over another functor with what fmap offers us. We could +pattern-match against the Just constructor to get the +function out of it and then map it over Just 5, but we’re +looking for a more general and abstract way of doing that, which works +across functors.

+

Meet the Applicative typeclass. It lies in the +Control.Applicative module and it defines two methods, +pure and <*>. It doesn’t provide a +default implementation for any of them, so we have to define them both +if we want something to be an applicative functor. The class is defined +like so:

+
class (Functor f) => Applicative f where
+    pure :: a -> f a
+    (<*>) :: f (a -> b) -> f a -> f b
+

This simple three line class definition tells us a lot! Let’s start +at the first line. It starts the definition of the +Applicative class and it also introduces a class +constraint. It says that if we want to make a type constructor part of +the Applicative typeclass, it has to be in +Functor first. That’s why if we know that if a type +constructor is part of the Applicative typeclass, it’s also +in Functor, so we can use fmap on it.

+

The first method it defines is called pure. Its type +declaration is pure :: a -> f a. f plays +the role of our applicative functor instance here. Because Haskell has a +very good type system and because everything a function can do is take +some parameters and return some value, we can tell a lot from a type +declaration and this is no exception. pure should take a +value of any type and return an applicative functor with that value +inside it. When we say inside it, we’re using the box analogy +again, even though we’ve seen that it doesn’t always stand up to +scrutiny. But the a -> f a type declaration is still +pretty descriptive. We take a value and we wrap it in an applicative +functor that has that value as the result inside it.

+

A better way of thinking about pure would be to say that +it takes a value and puts it in some sort of default (or pure) context—a +minimal context that still yields that value.

+

The <*> function is really interesting. It has a +type declaration of f (a -> b) -> f a -> f b. Does +this remind you of anything? Of course, +fmap :: (a -> b) -> f a -> f b. It’s a sort of 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.

+

OK cool great. Let’s give this a whirl.

+
ghci> Just (+3) <*> Just 9
+Just 12
+ghci> pure (+3) <*> Just 10
+Just 13
+ghci> pure (+3) <*> Just 9
+Just 12
+ghci> Just (++"hahah") <*> Nothing
+Nothing
+ghci> Nothing <*> Just "woot"
+Nothing
+

We see how doing pure (+3) and Just (+3) is +the same in this case. Use pure if you’re dealing with +Maybe values in an applicative context (i.e. using them +with <*>), otherwise stick to Just. The +first four input lines demonstrate how the function is extracted and +then mapped, but in this case, they could have been achieved by just +mapping unwrapped functions over functors. The last line is interesting, +because we try to extract a function from a Nothing and +then map it over something, which of course results in a +Nothing.

+

With normal functors, you can just map a function over a functor and +then you can’t get the result out in any general way, even if the result +is a partially applied function. Applicative functors, on the other +hand, allow you to operate on several functors with a single function. +Check out this piece of code:

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

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
+    pure x = [x]
+    fs <*> xs = [f x | f <- fs, x <- xs]
+

Earlier, we said that pure takes a value and puts it in +a default context. Or in other words, a minimal context that still +yields that value. The minimal context for lists would be the empty +list, [], but the empty list represents the lack of a +value, so it can’t hold in itself the value that we used +pure on. That’s why pure takes a value and +puts it in a singleton list. Similarly, the minimal context for the +Maybe applicative functor would be a Nothing, +but it represents the lack of a value instead of a value, so +pure is implemented as Just in the instance +implementation for Maybe.

+
ghci> pure "Hey" :: [String]
+["Hey"]
+ghci> pure "Hey" :: Maybe String
+Just "Hey"
+

What about <*>? If we look at what +<*>’s type would be if it were limited only to lists, +we get (<*>) :: [a -> b] -> [a] -> [b]. It’s +implemented with a list comprehension. +<*> has to somehow extract the function out of its +left parameter and then map it over the right parameter. But the thing +here is that the left list can have zero functions, one function, or +several functions inside it. The right list can also hold several +values. That’s why we use a list comprehension to draw from both lists. +We apply every possible function from the left list to every possible +value from the right list. The resulting list has every possible +combination of applying a function from the left list to a value in the +right one.

+
ghci> [(*0),(+100),(^2)] <*> [1,2,3]
+[0,0,0,101,102,103,1,4,9]
+

The left list has three functions and the right list has three +values, so the resulting list will have nine elements. Every function in +the left list is applied to every function in the right one. If we have +a list of functions that take two parameters, we can apply those +functions between two lists.

+
ghci> [(+),(*)] <*> [1,2] <*> [3,4]
+[4,5,5,6,3,4,6,8]
+

Because <*> is left-associative, +[(+),(*)] <*> [1,2] happens first, resulting in a +list that’s the same as [(1+),(2+),(1*),(2*)], because +every function on the left gets applied to every value on the right. +Then, [(1+),(2+),(1*),(2*)] <*> [3,4] happens, which +produces the final result.

+

Using the applicative style with lists is fun! Watch:

+
ghci> (++) <$> ["ha","heh","hmm"] <*> ["?","!","."]
+["ha?","ha!","ha.","heh?","heh!","heh.","hmm?","hmm!","hmm."]
+

Again, see how we used a normal function that takes two strings +between two applicative functors of strings just by inserting the +appropriate applicative operators.

+

You can view lists as non-deterministic computations. A value like +100 or "what" can be viewed as a deterministic +computation that has only one result, whereas a list like +[1,2,3] can be viewed as a computation that can’t decide on +which result it wants to have, so it presents us with all of the +possible results. So when you do something like +(+) <$> [1,2,3] <*> [4,5,6], you can think of +it as adding together two non-deterministic computations with ++, only to produce another non-deterministic computation +that’s even less sure about its result.

+

Using the applicative style on lists is often a good replacement for +list comprehensions. In the second chapter, we wanted to see all the +possible products of [2,5,10] and [8,10,11], +so we did this:

+
ghci> [ x*y | x <- [2,5,10], y <- [8,10,11]]
+[16,20,22,40,50,55,80,100,110]
+

We’re just drawing from two lists and applying a function between +every combination of elements. This can be done in the applicative style +as well:

+
ghci> (*) <$> [2,5,10] <*> [8,10,11]
+[16,20,22,40,50,55,80,100,110]
+

This seems clearer to me, because it’s easier to see that we’re just +calling * between two non-deterministic computations. If we +wanted all possible products of those two lists that are more than 50, +we’d just do:

+
ghci> filter (>50) $ (*) <$> [2,5,10] <*> [8,10,11]
+[55,80,100,110]
+

It’s easy to see how pure f <*> xs equals +fmap f xs with lists. pure f is just +[f] and [f] <*> xs will apply every +function in the left list to every value in the right one, but there’s +just one function in the left list, so it’s like mapping.

+

Another instance of Applicative that we’ve already +encountered is IO. This is how the instance is +implemented:

+
instance Applicative IO where
+    pure = return
+    a <*> b = do
+        f <- a
+        x <- b
+        return (f x)
+

ahahahah!

+

Since pure is all about putting a value in a minimal +context that still holds it as its result, it makes sense that +pure is just return, because +return does exactly that; it makes an I/O action that +doesn’t do anything, it just yields some value as its result, but it +doesn’t really do any I/O operations like printing to the terminal or +reading from a file.

+

If <*> were specialized for IO it +would have a type of +(<*>) :: IO (a -> b) -> IO a -> IO b. It +would take an I/O action that yields a function as its result and +another I/O action and create a new I/O action from those two that, when +performed, first performs the first one to get the function and then +performs the second one to get the value and then it would yield that +function applied to the value as its result. We used do syntax +to implement it here. Remember, do syntax is about taking +several I/O actions and gluing them into one, which is exactly what we +do here.

+

With Maybe and [], we could think of +<*> as simply extracting a function from its left +parameter and then sort of applying it over the right one. With +IO, extracting is still in the game, but now we also have a +notion of sequencing, because we’re taking two I/O actions and +we’re sequencing, or gluing, them into one. We have to extract the +function from the first I/O action, but to extract a result from an I/O +action, it has to be performed.

+

Consider this:

+
myAction :: IO String
+myAction = do
+    a <- getLine
+    b <- getLine
+    return $ a ++ b
+

This is an I/O action that will prompt the user for two lines and +yield as its result those two lines concatenated. We achieved it by +gluing together two getLine I/O actions and a +return, because we wanted our new glued I/O action to hold +the result of a ++ b. Another way of writing this would be +to use the applicative style.

+
myAction :: IO String
+myAction = (++) <$> getLine <*> getLine
+

What we were doing before was making an I/O action that applied a +function between the results of two other I/O actions, and this is the +same thing. Remember, getLine is an I/O action with the +type getLine :: IO String. When we use +<*> between two applicative functors, the result is +an applicative functor, so this all makes sense.

+

If we regress to the box analogy, we can imagine getLine +as a box that will go out into the real world and fetch us a string. +Doing (++) <$> getLine <*> getLine makes a new, +bigger box that sends those two boxes out to fetch lines from the +terminal and then presents the concatenation of those two lines as its +result.

+

The type of the expression +(++) <$> getLine <*> getLine is +IO String, which means that this expression is a completely +normal I/O action like any other, which also holds a result value inside +it, just like other I/O actions. That’s why we can do stuff like:

+
main = do
+    a <- (++) <$> getLine <*> getLine
+    putStrLn $ "The two lines concatenated turn out to be: " ++ a
+

If you ever find yourself binding some I/O actions to names and then +calling some function on them and presenting that as the result by using +return, consider using the applicative style because it’s +arguably a bit more concise and terse.

+

Another instance of Applicative is +(->) r, so functions. They are rarely used with the +applicative style outside of code golf, but they’re still interesting as +applicatives, so let’s take a look at how the function instance is +implemented.

+
+

If you’re confused about what (->) r means, check out +the previous section where we explain how (->) r is a +functor.

+
+
instance Applicative ((->) r) where
+    pure x = (\_ -> x)
+    f <*> g = \x -> f x (g x)
+

When we wrap a value into an applicative functor with +pure, the result it yields always has to be that value. A +minimal default context that still yields that value as a result. That’s +why in the function instance implementation, pure takes a +value and creates a function that ignores its parameter and always +returns that value. If we look at the type for pure, but +specialized for the (->) r instance, it’s +pure :: a -> (r -> a).

+
ghci> (pure 3) "blah"
+3
+

Because of currying, function application is left-associative, so we +can omit the parentheses.

+
ghci> pure 3 "blah"
+3
+

The instance implementation for <*> is a bit +cryptic, so it’s best if we just take a look at how to use functions as +applicative functors in the applicative style.

+
ghci> :t (+) <$> (+3) <*> (*100)
+(+) <$> (+3) <*> (*100) :: (Num a) => a -> a
+ghci> (+) <$> (+3) <*> (*100) $ 5
+508
+

Calling <*> with two applicative functors results +in an applicative functor, so if we use it on two functions, we get back +a function. So what goes on here? When we do +(+) <$> (+3) <*> (*100), we’re making a +function that will use + on the results of +(+3) and (*100) and return that. To +demonstrate on a real example, when we did +(+) <$> (+3) <*> (*100) $ 5, the 5 +first got applied to (+3) and (*100), +resulting in 8 and 500. Then, + +gets called with 8 and 500, resulting in +508.

+
ghci> (\x y z -> [x,y,z]) <$> (+3) <*> (*2) <*> (/2) $ 5
+[8.0,10.0,2.5]
+

SLAP

+

Same here. We create a function that will call the function +\x y z -> [x,y,z] with the eventual results from +(+3), (*2) and (/2). The +5 gets fed to each of the three functions and then +\x y z -> [x, y, z] gets called with those results.

+

You can think of functions as boxes that contain their eventual +results, so doing k <$> f <*> g creates a +function that will call k with the eventual results from +f and g. When we do something like +(+) <$> Just 3 <*> Just 5, we’re using ++ on values that might or might not be there, which also +results in a value that might or might not be there. When we do +(+) <$> (+10) <*> (+5), we’re using ++ on the future return values of (+10) and +(+5) and the result is also something that will produce a +value only when called with a parameter.

+

We don’t often use functions as applicatives, but this is still +really interesting. It’s not very important that you get how the +(->) r instance for Applicative works, so +don’t despair if you’re not getting this right now. Try playing with the +applicative style and functions to build up an intuition for functions +as applicatives.

+

An instance of Applicative that we haven’t encountered +yet is ZipList, and it lives in +Control.Applicative.

+

It turns out there are actually more ways for lists to be applicative +functors. One way is the one we already covered, which says that calling +<*> with a list of functions and a list of values +results in a list which has all the possible combinations of applying +functions from the left list to the values in the right list. If we do +[(+3),(*2)] <*> [1,2], (+3) will be +applied to both 1 and 2 and (*2) +will also be applied to both 1 and 2, +resulting in a list that has four elements, namely +[4,5,2,4].

+

However, [(+3),(*2)] <*> [1,2] could also work in +such a way that the first function in the left list gets applied to the +first value in the right one, the second function gets applied to the +second value, and so on. That would result in a list with two values, +namely [4,4]. You could look at it as +[1 + 3, 2 * 2].

+

Because one type can’t have two instances for the same typeclass, the +ZipList a type was introduced, which has one constructor +ZipList that has just one field, and that field is a list. +Here’s the instance:

+
instance Applicative ZipList where
+        pure x = ZipList (repeat x)
+        ZipList fs <*> ZipList xs = ZipList (zipWith (\f x -> f x) fs xs)
+

<*> does just what we said. It applies the first +function to the first value, the second function to the second value, +etc. This is done with zipWith (\f x -> f x) fs xs. +Because of how zipWith works, the resulting list will be as +long as the shorter of the two lists.

+

pure is also interesting here. It takes a value and puts +it in a list that just has that value repeating indefinitely. +pure "haha" results in +ZipList (["haha","haha","haha".... This might be a bit +confusing since we said that pure should put a value in a +minimal context that still yields that value. And you might be thinking +that an infinite list of something is hardly minimal. But it makes sense +with zip lists, because it has to produce the value on every position. +This also satisfies the law that pure f <*> xs should +equal fmap f xs. If pure 3 just returned +ZipList [3], +pure (*2) <*> ZipList [1,5,10] would result in +ZipList [2], because the resulting list of two zipped lists +has the length of the shorter of the two. If we zip a finite list with +an infinite list, the length of the resulting list will always be equal +to the length of the finite list.

+

So how do zip lists work in an applicative style? Let’s see. Oh, the +ZipList a type doesn’t have a Show instance, +so we have to use the getZipList +function to extract a raw list out of a zip list.

+
ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100,100]
+[101,102,103]
+ghci> getZipList $ (+) <$> ZipList [1,2,3] <*> ZipList [100,100..]
+[101,102,103]
+ghci> getZipList $ max <$> ZipList [1,2,3,4,5,3] <*> ZipList [5,3,1,2]
+[5,3,3,4]
+ghci> getZipList $ (,,) <$> ZipList "dog" <*> ZipList "cat" <*> ZipList "rat"
+[('d','c','r'),('o','a','a'),('g','t','t')]
+
+

The (,,) function is the same as +\x y z -> (x,y,z). Also, the (,) function +is the same as \x y -> (x,y).

+
+

Aside from zipWith, the standard library has functions +such as zipWith3, zipWith4, all the way up to +7. zipWith takes a function that takes two parameters and +zips two lists with it. zipWith3 takes a function that +takes three parameters and zips three lists with it, and so on. By using +zip lists with an applicative style, we don’t have to have a separate +zip function for each number of lists that we want to zip together. We +just use the applicative style to zip together an arbitrary amount of +lists with a function, and that’s pretty cool.

+

Control.Applicative defines a function that’s called +liftA2, which has a type of +liftA2 :: (Applicative f) => (a -> b -> c) -> f a -> f b -> f c +. It’s defined like this:

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

Nothing special, it just applies a function between two applicatives, +hiding the applicative style that we’ve become familiar with. The reason +we’re looking at it is because it clearly showcases why applicative +functors are more powerful than just ordinary functors. With ordinary +functors, we can just map functions over one functor. But with +applicative functors, we can apply a function between several functors. +It’s also interesting to look at this function’s type as +(a -> b -> c) -> (f a -> f b -> f c). When +we look at it like this, we can say that liftA2 takes a +normal binary function and promotes it to a function that operates on +two functors.

+

Here’s an interesting concept: we can take two applicative functors +and combine them into one applicative functor that has inside it the +results of those two applicative functors in a list. For instance, we +have Just 3 and Just 4. Let’s assume that the +second one has a singleton list inside it, because that’s really easy to +achieve:

+
ghci> fmap (\x -> [x]) (Just 4)
+Just [4]
+

OK, so let’s say we have Just 3 and +Just [4]. How do we get Just [3,4]? Easy.

+
ghci> liftA2 (:) (Just 3) (Just [4])
+Just [3,4]
+ghci> (:) <$> Just 3 <*> Just [4]
+Just [3,4]
+

Remember, : is a function that takes an element and a +list and returns a new list with that element at the beginning. Now that +we have Just [3,4], could we combine that with +Just 2 to produce Just [2,3,4]? Of course we +could. It seems that we can combine any amount of applicatives into one +applicative that has a list of the results of those applicatives inside +it. Let’s try implementing a function that takes a list of applicatives +and returns an applicative that has a list as its result value. We’ll +call it sequenceA.

+
sequenceA :: (Applicative f) => [f a] -> f [a]
+sequenceA [] = pure []
+sequenceA (x:xs) = (:) <$> x <*> sequenceA xs
+

Ah, recursion! First, we look at the type. It will transform a list +of applicatives into an applicative with a list. From that, we can lay +some groundwork for an edge condition. If we want to turn an empty list +into an applicative with a list of results, well, we just put an empty +list in a default context. Now comes the recursion. If we have a list +with a head and a tail (remember, x is an applicative and +xs is a list of them), we call sequenceA on +the tail, which results in an applicative with a list. Then, we just +prepend the value inside the applicative x into that +applicative with a list, and that’s it!

+

So if we do sequenceA [Just 1, Just 2], that’s +(:) <$> Just 1 <*> sequenceA [Just 2]. That +equals +(:) <$> Just 1 <*> ((:) <$> Just 2 <*> sequenceA []). +Ah! We know that sequenceA [] ends up as being +Just [], so this expression is now +(:) <$> Just 1 <*> ((:) <$> Just 2 <*> Just []), +which is (:) <$> Just 1 <*> Just [2], which is +Just [1,2]!

+

Another way to implement sequenceA is with a fold. +Remember, pretty much any function where we go over a list element by +element and accumulate a result along the way can be implemented with a +fold.

+
sequenceA :: (Applicative f) => [f a] -> f [a]
+sequenceA = foldr (liftA2 (:)) (pure [])
+

We approach the list from the right and start off with an accumulator +value of pure []. We do liftA2 (:) between the +accumulator and the last element of the list, which results in an +applicative that has a singleton in it. Then we do +liftA2 (:) with the now last element and the current +accumulator and so on, until we’re left with just the accumulator, which +holds a list of the results of all the applicatives.

+

Let’s give our function a whirl on some applicatives.

+
ghci> sequenceA [Just 3, Just 2, Just 1]
+Just [3,2,1]
+ghci> sequenceA [Just 3, Nothing, Just 1]
+Nothing
+ghci> sequenceA [(+3),(+2),(+1)] 3
+[6,5,4]
+ghci> sequenceA [[1,2,3],[4,5,6]]
+[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
+ghci> sequenceA [[1,2,3],[4,5,6],[3,4,4],[]]
+[]
+

Ah! Pretty cool. When used on Maybe values, +sequenceA creates a Maybe value with all the +results inside it as a list. If one of the values was +Nothing, then the result is also a Nothing. +This is cool when you have a list of Maybe values and +you’re interested in the values only if none of them is a +Nothing.

+

When used with functions, sequenceA takes a list of +functions and returns a function that returns a list. In our example, we +made a function that took a number as a parameter and applied it to each +function in the list and then returned a list of results. +sequenceA [(+3),(+2),(+1)] 3 will call (+3) +with 3, (+2) with 3 and +(+1) with 3 and present all those results as a +list.

+

Doing (+) <$> (+3) <*> (*2) will create a +function that takes a parameter, feeds it to both (+3) and +(*2) and then calls + with those two results. +In the same vein, it makes sense that sequenceA [(+3),(*2)] +makes a function that takes a parameter and feeds it to all of the +functions in the list. Instead of calling + with the +results of the functions, a combination of : and +pure [] is used to gather those results in a list, which is +the result of that function.

+

Using sequenceA is cool when we have a list of functions +and we want to feed the same input to all of them and then view the list +of results. For instance, we have a number and we’re wondering whether +it satisfies all of the predicates in a list. One way to do that would +be like so:

+
ghci> map (\f -> f 7) [(>4),(<10),odd]
+[True,True,True]
+ghci> and $ map (\f -> f 7) [(>4),(<10),odd]
+True
+

Remember, and takes a list of booleans and returns +True if they’re all True. Another way to +achieve the same thing would be with sequenceA:

+
ghci> sequenceA [(>4),(<10),odd] 7
+[True,True,True]
+ghci> and $ sequenceA [(>4),(<10),odd] 7
+True
+

sequenceA [(>4),(<10),odd] creates a function that +will take a number and feed it to all of the predicates in +[(>4),(<10),odd] and return a list of booleans. It +turns a list with the type (Num a) => [a -> Bool] +into a function with the type (Num a) => a -> [Bool]. +Pretty neat, huh?

+

Because lists are homogenous, all the functions in the list have to +be functions of the same type, of course. You can’t have a list like +[ord, (+3)], because ord takes a character and +returns a number, whereas (+3) takes a number and returns a +number.

+

When used with [], sequenceA takes a list +of lists and returns a list of lists. Hmm, interesting. It actually +creates lists that have all possible combinations of their elements. For +illustration, here’s the above done with sequenceA and then +done with a list comprehension:

+
ghci> sequenceA [[1,2,3],[4,5,6]]
+[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
+ghci> [[x,y] | x <- [1,2,3], y <- [4,5,6]]
+[[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]
+ghci> sequenceA [[1,2],[3,4]]
+[[1,3],[1,4],[2,3],[2,4]]
+ghci> [[x,y] | x <- [1,2], y <- [3,4]]
+[[1,3],[1,4],[2,3],[2,4]]
+ghci> sequenceA [[1,2],[3,4],[5,6]]
+[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
+ghci> [[x,y,z] | x <- [1,2], y <- [3,4], z <- [5,6]]
+[[1,3,5],[1,3,6],[1,4,5],[1,4,6],[2,3,5],[2,3,6],[2,4,5],[2,4,6]]
+

This might be a bit hard to grasp, but if you play with it for a +while, you’ll see how it works. Let’s say that we’re doing +sequenceA [[1,2],[3,4]]. To see how this happens, let’s use +the +sequenceA (x:xs) = (:) <$> x <*> sequenceA xs +definition of sequenceA and the edge condition +sequenceA [] = pure []. You don’t have to follow this +evaluation, but it might help you if have trouble imagining how +sequenceA works on lists of lists, because it can be a bit +mind-bending.

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

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

+

The newtype keyword

+

why_ so serious?

+

So far, we’ve learned how to make our own algebraic data types by +using the data keyword. We’ve also learned how to give +existing types synonyms with the type keyword. In this +section, we’ll be taking a look at how to make new types out of existing +data types by using the newtype keyword and why we’d +want to do that in the first place.

+

In the previous section, we saw that there are actually more ways for +the list type to be an applicative functor. One way is to have +<*> take every function out of the list that is its +left parameter and apply it to every value in the list that is on the +right, resulting in every possible combination of applying a function +from the left list to a value in the right list.

+
ghci> [(+1),(*100),(*5)] <*> [1,2,3]
+[2,3,4,100,200,300,5,10,15]
+

The second way is to take the first function on the left side of +<*> and apply it to the first value on the right, +then take the second function from the list on the left side and apply +it to the second value on the right, and so on. Ultimately, it’s kind of +like zipping the two lists together. But lists are already an instance +of Applicative, so how did we also make lists an instance +of Applicative in this second way? If you remember, we said +that the ZipList a type was introduced for this reason, +which has one value constructor, ZipList, that has just one +field. We put the list that we’re wrapping in that field. Then, +ZipList was made an instance of Applicative, +so that when we want to use lists as applicatives in the zipping manner, +we just wrap them with the ZipList constructor and then +once we’re done, unwrap them with getZipList:

+
ghci> getZipList $ ZipList [(+1),(*100),(*5)] <*> ZipList [1,2,3]
+[2,200,15]
+

So, what does this have to do with this newtype keyword? +Well, think about how we might write the data declaration for our +ZipList a type. One way would be to do it like so:

+
data ZipList a = ZipList [a]
+

A type that has just one value constructor and that value constructor +has just one field that is a list of things. We might also want to use +record syntax so that we automatically get a function that extracts a +list from a ZipList:

+
data ZipList a = ZipList { getZipList :: [a] }
+

This looks fine and would actually work pretty well. We had two ways +of making an existing type an instance of a type class, so we used the +data keyword to just wrap that type into another type and made +the other type an instance in the second way.

+

The newtype keyword in Haskell is made exactly for these +cases when we want to just take one type and wrap it in something to +present it as another type. In the actual libraries, +ZipList a is defined like this:

+
newtype ZipList a = ZipList { getZipList :: [a] }
+

Instead of the data keyword, the newtype keyword is +used. Now why is that? Well for one, newtype is faster. If you +use the data keyword to wrap a type, there’s some overhead to +all that wrapping and unwrapping when your program is running. But if +you use newtype, Haskell knows that you’re just using it to +wrap an existing type into a new type (hence the name), because you want +it to be the same internally but have a different type. With that in +mind, Haskell can get rid of the wrapping and unwrapping once it +resolves which value is of what type.

+

So why not just use newtype all the time instead of +data then? Well, when you make a new type from an existing type +by using the newtype keyword, you can only have one value +constructor and that value constructor can only have one field. But with +data, you can make data types that have several value +constructors and each constructor can have zero or more fields:

+
data Profession = Fighter | Archer | Accountant
+
+data Race = Human | Elf | Orc | Goblin
+
+data PlayerCharacter = PlayerCharacter Race Profession
+

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

+

We can also use the deriving keyword with newtype +just like we would with data. We can derive instances for +Eq, Ord, Enum, +Bounded, Show and Read. If we +derive the instance for a type class, the type that we’re wrapping has +to be in that type class to begin with. It makes sense, because +newtype just wraps an existing type. So now if we do the +following, we can print and equate values of our new type:

+
newtype CharList = CharList { getCharList :: [Char] } deriving (Eq, Show)
+

Let’s give that a go:

+
ghci> CharList "this will be shown!"
+CharList {getCharList = "this will be shown!"}
+ghci> CharList "benny" == CharList "benny"
+True
+ghci> CharList "benny" == CharList "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
+

So we just start out with:

+
instance Functor Maybe where
+

And then implement fmap. All the type parameters add up +because the Maybe takes the place of f in the +definition of the Functor type class and so if we look at +fmap like it only worked on Maybe, it ends up +behaving like:

+
fmap :: (a -> b) -> Maybe a -> Maybe b
+

wow, very evil

+

Isn’t that just peachy? Now what if we wanted to make the tuple an +instance of Functor in such a way that when we +fmap a function over a tuple, it gets applied to the first +component of the tuple? That way, doing fmap (+3) (1,1) +would result in (4,1). It turns out that writing the +instance for that is kind of hard. With Maybe, we just say +instance Functor Maybe where because only type constructors +that take exactly one parameter can be made an instance of +Functor. But it seems like there’s no way to do something +like that with (a,b) so that the type parameter +a ends up being the one that changes when we use +fmap. To get around this, we can newtype our tuple +in such a way that the second type parameter represents the type of the +first component in the tuple:

+
newtype Pair b a = Pair { getPair :: (a,b) }
+

And now, we can make it an instance of Functor so that +the function is mapped over the first component:

+
instance Functor (Pair c) where
+    fmap f (Pair (x,y)) = Pair (f x, y)
+

As you can see, we can pattern match on types defined with +newtype. We pattern match to get the underlying tuple, then we +apply the function f to the first component in the tuple +and then we use the Pair value constructor to convert the +tuple back to our Pair b a. If we imagine what the type +fmap would be if it only worked on our new pairs, it would +be:

+
fmap :: (a -> b) -> Pair c a -> Pair c b
+

Again, we said instance Functor (Pair c) where and so +Pair c took the place of the f in the type +class definition for Functor:

+
class Functor f where
+    fmap :: (a -> b) -> f a -> f b
+

So now, if we convert a tuple into a Pair b a, we can +use fmap over it and the function will be mapped over the +first component:

+
ghci> getPair $ fmap (*100) (Pair (2,3))
+(200,3)
+ghci> getPair $ fmap reverse (Pair ("london calling", 3))
+("gnillac nodnol",3)
+

On newtype laziness

+

We mentioned that newtype is usually faster than +data. The only thing that can be done with newtype is +turning an existing type into a new type, so internally, Haskell can +represent the values of types defined with newtype just like +the original ones, only it has to keep in mind that their types are now +distinct. This fact means that not only is newtype faster, it’s +also lazier. Let’s take a look at what this means.

+

Like we’ve said before, Haskell is lazy by default, which means that +only when we try to actually print the results of our functions will any +computation take place. Furthermore, only those computations that are +necessary for our function to tell us the result will get carried out. +The undefined value in Haskell represents an erroneous +computation. If we try to evaluate it (that is, force Haskell to +actually compute it) by printing it to the terminal, Haskell will throw +a hissy fit (technically referred to as an exception):

+
ghci> undefined
+*** Exception: Prelude.undefined
+

However, if we make a list that has some undefined +values in it but request only the head of the list, which is not +undefined, everything will go smoothly because Haskell +doesn’t really need to evaluate any other elements in a list if we only +want to see what the first element is:

+
ghci> head [3,4,5,undefined,2,undefined]
+3
+

Now consider the following type:

+
data CoolBool = CoolBool { getCoolBool :: Bool }
+

It’s your run-of-the-mill algebraic data type that was defined with +the data keyword. It has one value constructor, which has one +field whose type is Bool. Let’s make a function that +pattern matches on a CoolBool and returns the value +"hello" regardless of whether the Bool inside +the CoolBool was True or +False:

+
helloMe :: CoolBool -> String
+helloMe (CoolBool _) = "hello"
+

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

+
ghci> helloMe undefined
+"*** Exception: Prelude.undefined
+

Yikes! An exception! Now why did this exception happen? Types defined +with the data keyword can have multiple value constructors +(even though CoolBool only has one). So in order to see if +the value given to our function conforms to the +(CoolBool _) pattern, Haskell has to evaluate the value +just enough to see which value constructor was used when we made the +value. And when we try to evaluate an undefined value, even +a little, an exception is thrown.

+

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

+
newtype CoolBool = CoolBool { getCoolBool :: Bool }
+

We don’t have to change our helloMe function, because +the pattern matching syntax is the same if you use newtype or +data to define your type. Let’s do the same thing here and +apply helloMe to an undefined value:

+
ghci> helloMe undefined
+"hello"
+

+

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

+

+

Type classes in Haskell are used to present an interface for types +that have some behavior in common. We started out with simple type +classes like Eq, which is for types whose values can be +equated, and Ord, which is for things that can be put in an +order and then moved on to more interesting ones, like +Functor and Applicative.

+

When we make a type, we think about which behaviors it supports, +i.e. what it can act like and then based on that we decide which type +classes to make it an instance of. If it makes sense for values of our +type to be equated, we make it an instance of the Eq type +class. If we see that our type is some kind of functor, we make it an +instance of Functor, and so on.

+

Now consider the following: * is a function that takes +two numbers and multiplies them. If we multiply some number with a +1, the result is always equal to that number. It doesn’t +matter if we do 1 * x or x * 1, the result is +always x. Similarly, ++ is also a function +which takes two things and returns a third. Only instead of multiplying +numbers, it takes two lists and concatenates them. And much like +*, it also has a certain value which doesn’t change the +other one when used with ++. That value is the empty list: +[].

+
ghci> 4 * 1
+4
+ghci> 1 * 9
+9
+ghci> [1,2,3] ++ []
+[1,2,3]
+ghci> [] ++ [0.5, 2.5]
+[0.5,2.5]
+

It seems that both * together with 1 and +++ along with [] share some common +properties:

+
    +
  • 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)
+240
+ghci> 3 * (2 * (8 * 5))
+240
+ghci> "la" ++ ("di" ++ "da")
+"ladida"
+ghci> ("la" ++ "di") ++ "da"
+"ladida"
+

We call this property associativity. * is +associative, and so is ++, but -, for example, +is not. The expressions (5 - 3) - 4 and +5 - (3 - 4) result in different numbers.

+

By noticing and writing down these properties, we have chanced upon +monoids! A monoid is when you have an associative binary +function and a value which acts as an identity with respect to that +function. When something acts as an identity with respect to a function, +it means that when called with that function and some other value, the +result is always equal to that other value. 1 is the +identity with respect to * and [] is the +identity with respect to ++. There are a lot of other +monoids to be found in the world of Haskell, which is why the +Monoid type class exists. It’s for types which can act like +monoids. Let’s see how the type class is defined:

+
class Monoid m where
+    mempty :: m
+    mappend :: m -> m -> m
+    mconcat :: [m] -> m
+    mconcat = foldr mappend mempty
+

woof dee do!!!

+

The Monoid type class is defined in +import Data.Monoid. Let’s take some time and get properly +acquainted with it.

+

First of all, we see that only concrete types can be made instances +of Monoid, because the m in the type class +definition doesn’t take any type parameters. This is different from +Functor and Applicative, which require their +instances to be type constructors which take one parameter.

+

The first function is mempty. It’s not really a +function, since it doesn’t take parameters, so it’s a polymorphic +constant, kind of like minBound from Bounded. +mempty represents the identity value for a particular +monoid.

+

Next up, we have mappend, which, as you’ve probably +guessed, is the binary function. It takes two values of the same type +and returns a value of that type as well. It’s worth noting that the +decision to name mappend as it’s named was kind of +unfortunate, because it implies that we’re appending two things in some +way. While ++ does take two lists and append one to the +other, * doesn’t really do any appending, it just +multiplies two numbers together. When we meet other instances of +Monoid, we’ll see that most of them don’t append values +either, so avoid thinking in terms of appending and just think in terms +of mappend being a binary function that takes two monoid +values and returns a third.

+

The last function in this type class definition is +mconcat. It takes a list of monoid values and reduces them +to a single value by doing mappend between the list’s +elements. It has a default implementation, which just takes +mempty as a starting value and folds the list from the +right with mappend. Because the default implementation is +fine for most instances, we won’t concern ourselves with +mconcat too much from now on. When making a type an +instance of Monoid, it suffices to just implement +mempty and mappend. The reason +mconcat is there at all is because for some instances, +there might be a more efficient way to implement mconcat, +but for most instances the default implementation is just fine.

+

Before moving on to specific instances of Monoid, let’s +take a brief look at the monoid laws. We mentioned that there has to be +a value that acts as the identity with respect to the binary function +and that the binary function has to be associative. It’s possible to +make instances of Monoid that don’t follow these rules, but +such instances are of no use to anyone because when using the +Monoid type class, we rely on its instances acting like +monoids. Otherwise, what’s the point? That’s why when making instances, +we have to make sure they follow these laws:

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

+

Lists are monoids

+

Yes, lists are monoids! Like we’ve seen, the ++ function +and the empty list [] form a monoid. The instance is very +simple:

+
instance Monoid [a] where
+    mempty = []
+    mappend = (++)
+

Lists are an instance of the Monoid type class +regardless of the type of the elements they hold. Notice that we wrote +instance Monoid [a] and not +instance Monoid [], because Monoid requires a +concrete type for an instance.

+

Giving this a test run, we encounter no surprises:

+
ghci> [1,2,3] `mappend` [4,5,6]
+[1,2,3,4,5,6]
+ghci> ("one" `mappend` "two") `mappend` "tree"
+"onetwotree"
+ghci> "one" `mappend` ("two" `mappend` "tree")
+"onetwotree"
+ghci> "one" `mappend` "two" `mappend` "tree"
+"onetwotree"
+ghci> "pang" `mappend` mempty
+"pang"
+ghci> mconcat [[1,2],[3,6],[9]]
+[1,2,3,6,9]
+ghci> mempty :: [a]
+[]
+

smug as hell

+

Notice that in the last line, we had to write an explicit type +annotation, because if we just did mempty, GHCi wouldn’t +know which instance to use, so we had to say we want the list instance. +We were able to use the general type of [a] (as opposed to +specifying [Int] or [String]) because the +empty list can act as if it contains any type.

+

Because mconcat has a default implementation, we get it +for free when we make something an instance of Monoid. In +the case of the list, mconcat turns out to be just +concat. It takes a list of lists and flattens it, because +that’s the equivalent of doing ++ between all the 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.

+

Product and Sum

+

We already examined one way for numbers to be considered monoids. +Just have the binary function be * and the identity value +1. It turns out that that’s not the only way for numbers to +be monoids. Another way is to have the binary function be + +and the identity value 0:

+
ghci> 0 + 4
+4
+ghci> 5 + 0
+5
+ghci> (1 + 3) + 5
+9
+ghci> 1 + (3 + 5)
+9
+

The monoid laws hold, because if you add 0 to any number, the result +is that number. And addition is also associative, so we get no problems +there. So now that there are two equally valid ways for numbers to be +monoids, which way do choose? Well, we don’t have to. Remember, when +there are several ways for some type to be an instance of the same type +class, we can wrap that type in a newtype and then make the new +type an instance of the type class in a different way. We can have our +cake and eat it too.

+

The Data.Monoid module exports two types for this, +namely Product and Sum. Product +is defined like this:

+
newtype Product a =  Product { getProduct :: a }
+    deriving (Eq, Ord, Read, Show, Bounded)
+

Simple, just a newtype wrapper with one type parameter along +with some derived instances. Its instance for Monoid goes a +little something like this:

+
instance Num a => Monoid (Product a) where
+    mempty = Product 1
+    Product x `mappend` Product y = Product (x * y)
+

mempty is just 1 wrapped in a +Product constructor. mappend pattern matches +on the Product constructor, multiplies the two numbers and +then wraps the resulting number back. As you can see, there’s a +Num a class constraint. So this means that +Product a is an instance of Monoid for all +a’s that are already an instance of Num. To +use Producta a as a monoid, we have to do some +newtype wrapping and unwrapping:

+
ghci> getProduct $ Product 3 `mappend` Product 9
+27
+ghci> getProduct $ Product 3 `mappend` mempty
+3
+ghci> getProduct $ Product 3 `mappend` Product 4 `mappend` Product 2
+24
+ghci> getProduct . mconcat . map Product $ [3,4,2]
+24
+

This is nice as a showcase of the Monoid type class, but +no one in their right mind would use this way of multiplying numbers +instead of just writing 3 * 9 and 3 * 1. But a +bit later, we’ll see how these Monoid instances that may +seem trivial at this time can come in handy.

+

Sum is defined like Product and the +instance is similar as well. We use it in the same way:

+
ghci> getSum $ Sum 2 `mappend` Sum 9
+11
+ghci> getSum $ mempty `mappend` Sum 3
+3
+ghci> getSum . mconcat . map Sum $ [1,2,3]
+6
+

Any and All

+

Another type which can act like a monoid in two distinct but equally +valid ways is Bool. The first way is to have the +or function || act as the binary function along +with False as the identity value. The way or works +in logic is that if any of its two parameters is True, it +returns True, otherwise it returns False. So +if we use False as the identity value, it will return +False when or-ed with False and +True when or-ed with True. The +Any newtype constructor is an instance of +Monoid in this fashion. It’s defined like this:

+
newtype Any = Any { getAny :: Bool }
+    deriving (Eq, Ord, Read, Show, Bounded)
+

Its instance looks goes like so:

+
instance Monoid Any where
+        mempty = Any False
+        Any x `mappend` Any y = Any (x || y)
+

The reason it’s called Any is because +x `mappend` y will be True if any one +of those two is True. Even if three or more +Any wrapped Bools are mappended +together, the result will hold True if any of them are +True:

+
ghci> getAny $ Any True `mappend` Any False
+True
+ghci> getAny $ mempty `mappend` Any True
+True
+ghci> getAny . mconcat . map Any $ [False, False, False, True]
+True
+ghci> getAny $ mempty `mappend` mempty
+False
+

The other way for Bool to be an instance of +Monoid is to kind of do the opposite: have +&& be the binary function and then make +True the identity value. Logical and will return +True only if both of its parameters are True. +This is the newtype declaration, nothing fancy:

+
newtype All = All { getAll :: Bool }
+        deriving (Eq, Ord, Read, Show, Bounded)
+

And this is the instance:

+
instance Monoid All where
+        mempty = All True
+        All x `mappend` All y = All (x && y)
+

When we mappend values of the All type, the +result will be True only if all the values used in +the mappend operations are True:

+
ghci> getAll $ mempty `mappend` All True
+True
+ghci> getAll $ mempty `mappend` All False
+False
+ghci> getAll . mconcat . map All $ [True, True, True]
+True
+ghci> getAll . mconcat . map All $ [True, True, False]
+False
+

Just like with multiplication and addition, we usually explicitly +state the binary functions instead of wrapping them in newtypes +and then using mappend and mempty. +mconcat seems useful for Any and +All, but usually it’s easier to use the or and +and functions, which take lists of Bools and +return True if any of them are True or if all +of them are True, respectively.

+

The Ordering monoid

+

Hey, remember the Ordering type? It’s used as the result +when comparing things and it can have three values: LT, +EQ and GT, which stand for less than, +equal and greater than respectively:

+
ghci> 1 `compare` 2
+LT
+ghci> 2 `compare` 2
+EQ
+ghci> 3 `compare` 2
+GT
+

With lists, numbers and boolean values, finding monoids was just a +matter of looking at already existing commonly used functions and seeing +if they exhibit some sort of monoid behavior. With +Ordering, we have to look a bit harder to recognize a +monoid, but it turns out that its Monoid instance is just +as intuitive as the ones we’ve met so far and also quite useful:

+
instance Monoid Ordering where
+    mempty = EQ
+    LT `mappend` _ = LT
+    EQ `mappend` y = y
+    GT `mappend` _ = GT
+

+

The instance is set up like this: when we mappend two +Ordering values, the one on the left is kept, unless the +value on the left is EQ, in which case the right one is the +result. The identity is EQ. At first, this may seem kind of +arbitrary, but it actually resembles the way we alphabetically compare +words. We compare the first two letters and if they differ, we can +already decide which word would go first in a dictionary. However, if +the first two letters are equal, then we move on to comparing the next +pair of letters and repeat the process.

+

For instance, if we were to alphabetically compare the words +"ox" and "on", we’d first compare the first +two letters of each word, see that they are equal and then move on to +comparing the second letter of each word. We see that 'x' +is alphabetically greater than 'n', and so we know how the +words compare. To gain some intuition for EQ being the +identity, we can notice that if we were to cram the same letter in the +same position in both words, it wouldn’t change their alphabetical +ordering. "oix" is still alphabetically greater than and +"oin".

+

It’s important to note that in the Monoid instance for +Ordering, x `mappend` y doesn’t equal +y `mappend` x. Because the first parameter is kept unless +it’s EQ, LT `mappend` GT will result in +LT, whereas GT `mappend` LT will result in +GT:

+
ghci> LT `mappend` GT
+LT
+ghci> GT `mappend` LT
+GT
+ghci> mempty `mappend` LT
+LT
+ghci> mempty `mappend` GT
+GT
+

OK, so how is this monoid useful? Let’s say you were writing a +function that takes two strings, compares their lengths, and returns an +Ordering. But if the strings are of the same length, then +instead of returning EQ right away, we want to compare them +alphabetically. One way to write this would be like so:

+
lengthCompare :: String -> String -> Ordering
+lengthCompare x y = let a = length x `compare` length y
+                        b = x `compare` y
+                    in  if a == EQ then b else a
+

We name the result of comparing the lengths a and the +result of the alphabetical comparison b and then if it +turns out that the lengths were equal, we return their alphabetical +ordering.

+

But by employing our understanding of how Ordering is a +monoid, we can rewrite this function in a much simpler manner:

+
import Data.Monoid
+
+lengthCompare :: String -> String -> Ordering
+lengthCompare x y = (length x `compare` length y) `mappend`
+                    (x `compare` y)
+

We can try this out:

+
ghci> lengthCompare "zen" "ants"
+LT
+ghci> lengthCompare "zen" "ant"
+GT
+

Remember, when we use mappend, its left parameter is +always kept unless it’s EQ, in which case the right one is +kept. That’s why we put the comparison that we consider to be the first, +more important criterion as the first parameter. If we wanted to expand +this function to also compare for the number of vowels and set this to +be the second most important criterion for comparison, we’d just modify +it like this:

+
import Data.Monoid
+
+lengthCompare :: String -> String -> Ordering
+lengthCompare x y = (length x `compare` length y) `mappend`
+                    (vowels x `compare` vowels y) `mappend`
+                    (x `compare` y)
+    where vowels = length . filter (`elem` "aeiou")
+

We made a helper function, which takes a string and tells us how many +vowels it has by first filtering it only for letters that are in the +string "aeiou" and then applying length to +that.

+
ghci> lengthCompare "zen" "anna"
+LT
+ghci> lengthCompare "zen" "ana"
+LT
+ghci> lengthCompare "zen" "ann"
+GT
+

Very cool. Here, we see how in the first example the lengths are +found to be different and so LT is returned, because the +length of "zen" is less than the length of +"anna". In the second example, the lengths are the same, +but the second string has more vowels, so LT is returned +again. In the third example, they both have the same length and the same +number of vowels, so they’re compared alphabetically and +"zen" wins.

+

The Ordering monoid is very cool because it allows us to +easily compare things by many different criteria and put those criteria +in an order themselves, ranging from the most important to the +least.

+

Maybe the monoid

+

Let’s take a look at the various ways that Maybe a can +be made an instance of Monoid and what those instances are +useful for.

+

One way is to treat Maybe a as a monoid only if its type +parameter a is a monoid as well and then implement +mappend in such a way that it uses the mappend +operation of the values that are wrapped with Just. We use +Nothing as the identity, and so if one of the two values +that we’re mappending is Nothing, we keep the +other value. Here’s the instance declaration:

+
instance Monoid a => Monoid (Maybe a) where
+    mempty = Nothing
+    Nothing `mappend` m = m
+    m `mappend` Nothing = m
+    Just m1 `mappend` Just m2 = Just (m1 `mappend` m2)
+

Notice the class constraint. It says that Maybe a is an +instance of Monoid only if a is an instance of +Monoid. If we mappend something with a +Nothing, the result is that something. If we +mappend two Just values, the contents of the +Justs get mappended and then wrapped back in a +Just. We can do this because the class constraint ensures +that the type of what’s inside the Just is an instance of +Monoid.

+
ghci> Nothing `mappend` Just "andy"
+Just "andy"
+ghci> Just LT `mappend` Nothing
+Just LT
+ghci> Just (Sum 3) `mappend` Just (Sum 4)
+Just (Sum {getSum = 7})
+

This comes in use when you’re dealing with monoids as results of +computations that may have failed. Because of this instance, we don’t +have to check if the computations have failed by seeing if they’re a +Nothing or Just value; we can just continue to +treat them as normal monoids.

+

But what if the type of the contents of the Maybe aren’t +an instance of Monoid? Notice that in the previous instance +declaration, the only case where we have to rely on the contents being +monoids is when both parameters of mappend are +Just values. But if we don’t know if the contents are +monoids, we can’t use mappend between them, so what are we +to do? Well, one thing we can do is to just discard the second value and +keep the first one. For this, the First a type exists and +this is its definition:

+
newtype First a = First { getFirst :: Maybe a }
+    deriving (Eq, Ord, Read, Show)
+

We take a Maybe a and we wrap it with a +newtype. The Monoid instance is as follows:

+
instance Monoid (First a) where
+    mempty = First Nothing
+    First (Just x) `mappend` _ = First (Just x)
+    First Nothing `mappend` x = x
+

Just like we said. mempty is just a Nothing +wrapped with the First newtype constructor. If +mappend’s first parameter is a Just value, we +ignore the second one. If the first one is a Nothing, then +we present the second parameter as a result, regardless of whether it’s +a Just or a Nothing:

+
ghci> getFirst $ First (Just 'a') `mappend` First (Just 'b')
+Just 'a'
+ghci> getFirst $ First Nothing `mappend` First (Just 'b')
+Just 'b'
+ghci> getFirst $ First (Just 'a') `mappend` First Nothing
+Just 'a'
+

First is useful when we have a bunch of +Maybe values and we just want to know if any of them is a +Just. The mconcat function comes in handy:

+
ghci> getFirst . mconcat . map First $ [Nothing, Just 9, Just 10]
+Just 9
+

If we want a monoid on Maybe a such that the second +parameter is kept if both parameters of mappend are +Just values, Data.Monoid provides a 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 +functions whose names clash with the ones from the Prelude, +it’s best imported qualified (and served with basil):

+
import qualified Foldable as F
+

To save ourselves precious keystrokes, we’ve chosen to import it +qualified as F. Alright, so what are some of the functions +that this type class defines? Well, among them are foldr, +foldl, foldr1 and foldl1. Huh? +But we already know these functions, what’s so new about this? Let’s +compare the types of Foldable’s foldr and the +foldr from the Prelude to see how they +differ:

+
ghci> :t foldr
+foldr :: (a -> b -> b) -> b -> [a] -> b
+ghci> :t F.foldr
+F.foldr :: (F.Foldable t) => (a -> b -> b) -> b -> t a -> b
+

Ah! So whereas foldr takes a list and folds it up, the +foldr from Data.Foldable accepts any type that +can be folded up, not just lists! As expected, both foldr +functions do the same for lists:

+
ghci> foldr (*) 1 [1,2,3]
+6
+ghci> F.foldr (*) 1 [1,2,3]
+6
+

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

+
ghci> F.foldl (+) 2 (Just 9)
+11
+ghci> F.foldr (||) False (Just True)
+True
+

But folding over a Maybe value isn’t terribly +interesting, because when it comes to folding, it just acts like a list +with one element if it’s a Just value and as an empty list +if it’s Nothing. So let’s examine a data structure that’s a +little more complex then.

+

Remember the tree data structure from the Making +Our Own Types and Typeclasses chapter? We defined it like this:

+
data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)
+

We said that a tree is either an empty tree that doesn’t hold any +values or it’s a node that holds one value and also two other trees. +After defining it, we made it an instance of Functor and +with that we gained the ability to fmap functions over it. +Now, we’re going to make it an instance of Foldable so that +we get the ability to fold it up. One way to make a type constructor an +instance of Foldable is to just directly implement +foldr for it. But another, often much easier way, is to +implement the foldMap function, which is also a part of the +Foldable type class. The foldMap function has +the following type:

+
foldMap :: (Monoid m, Foldable t) => (a -> m) -> t a -> m
+

Its first parameter is a function that takes a value of the type that +our foldable structure contains (denoted here with a) and +returns a monoid value. Its second parameter is a foldable structure +that contains values of type a. It maps that function over +the foldable structure, thus producing a foldable structure that +contains monoid values. Then, by doing mappend between +those monoid values, it joins them all into a single monoid value. This +function may sound kind of odd at the moment, but we’ll see that it’s +very easy to implement. What’s also cool is that implementing this +function is all it takes for our type to be made an instance of +Foldable. So if we just implement foldMap for +some type, we get foldr and foldl on that type +for free!

+

This is how we make Tree an instance of +Foldable:

+
instance F.Foldable Tree where
+    foldMap f Empty = mempty
+    foldMap f (Node x l r) = F.foldMap f l `mappend`
+                             f x           `mappend`
+                             F.foldMap f r
+

+

We think like this: if we are provided with a function that takes an +element of our tree and returns a monoid value, how do we reduce our +whole tree down to one single monoid value? When we were doing +fmap over our tree, we applied the function that we were +mapping to a node and then we recursively mapped the function over the +left subtree as well as the right one. Here, we’re tasked with not only +mapping a function, but with also joining up the results into a single +monoid value by using mappend. First we consider the case +of the empty tree — a sad and lonely tree that has no values or +subtrees. It doesn’t hold any value that we can give to our +monoid-making function, so we just say that if our tree is empty, the +monoid value it becomes is mempty.

+

The case of a non-empty node is a bit more interesting. It contains +two subtrees as well as a value. In this case, we recursively +foldMap the same function f over the left and +the right subtrees. Remember, our foldMap results in a +single monoid value. We also apply our function f to the +value in the node. Now we have three monoid values (two from our +subtrees and one from applying f to the value in the node) +and we just have to bang them together into a single value. For this +purpose we use mappend, and naturally the left subtree +comes first, then the node value and then the right subtree.

+

Notice that we didn’t have to provide the function that takes a value +and returns a monoid value. We receive that function as a parameter to +foldMap and all we have to decide is where to apply that +function and how to join up the resulting monoids from it.

+

Now that we have a Foldable instance for our tree type, +we get foldr and foldl for free! Consider this +tree:

+
testTree = Node 5
+            (Node 3
+                (Node 1 Empty Empty)
+                (Node 6 Empty Empty)
+            )
+            (Node 9
+                (Node 8 Empty Empty)
+                (Node 10 Empty Empty)
+            )
+

It has 5 at its root and then its left node 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.

+ +
+ + + + +
+ + + + diff --git a/markdown/generated_html/higher-order-functions.html b/markdown/generated_html/higher-order-functions.html new file mode 100644 index 0000000..f0023f8 --- /dev/null +++ b/markdown/generated_html/higher-order-functions.html @@ -0,0 +1,992 @@ + + + +Higher Order Functions - Learn You a Haskell for Great Good! + + + + + + + + + + +
+
+
+ +
+

Higher Order +Functions

+

sun

+

Haskell functions can take functions as parameters and return +functions as return values. A function that does either of those is +called a higher order function. Higher order functions aren’t just a +part of the Haskell experience, they pretty much are the Haskell +experience. It turns out that if you want to define computations by +defining what stuff is instead of defining steps that change +some state and maybe looping them, higher order functions are +indispensable. They’re a really powerful way of solving problems and +thinking about programs.

+

Curried functions

+

Every function in Haskell officially only takes one parameter. So how +is it possible that we defined and used several functions that take more +than one parameter so far? Well, it’s a clever trick! All the functions +that accepted several parameters so far have been +curried functions. What does that mean? You’ll +understand it best on an example. Let’s take our good friend, the +max function. It looks like it takes two parameters and +returns the one that’s bigger. Doing max 4 5 first creates +a function that takes a parameter and returns either 4 or +that parameter, depending on which is bigger. Then, 5 is +applied to that function and that function produces our desired result. +That sounds like a mouthful but it’s actually a really cool concept. The +following two calls are equivalent:

+
ghci> max 4 5
+5
+ghci> (max 4) 5
+5
+

haskell curry

+

Putting a space between two things is simply function +application. The space is sort of like an operator and it has +the highest precedence. Let’s examine the type of max. It’s +max :: (Ord a) => a -> a -> a. That can also be +written as max :: (Ord a) => a -> (a -> a). That +could be read as: max takes an a and returns +(that’s the ->) a function that takes an a +and returns an a. That’s why the return type and the +parameters of functions are all simply separated with arrows.

+

So how is that beneficial to us? Simply speaking, if we call a +function with too few parameters, we get back a partially +applied function, meaning a function that takes as many +parameters as we left out. Using partial application (calling functions +with too few parameters, if you will) is a neat way to create functions +on the fly so we can pass them to another function or to seed them with +some data.

+

Take a look at this offensively simple function:

+
multThree :: (Num a) => a -> a -> a -> a
+multThree x y z = x * y * z
+

What really happens when we do multThree 3 5 9 or +((multThree 3) 5) 9? First, 3 is applied to +multThree, because they’re separated by a space. That +creates a function that takes one parameter and returns a function. So +then 5 is applied to that, which creates a function that +will take a parameter and multiply it by 15. 9 is applied +to that function and the result is 135 or something. Remember that this +function’s type could also be written as +multThree :: (Num a) => a -> (a -> (a -> a)). +The thing before the -> is the parameter that a function +takes and the thing after it is what it returns. So our function takes +an a and returns a function of type +(Num a) => a -> (a -> a). Similarly, this function +takes an a and returns a function of type +(Num a) => a -> a. And this function, finally, just +takes an a and returns an a. Take a look at +this:

+
ghci> let multTwoWithNine = multThree 9
+ghci> multTwoWithNine 2 3
+54
+ghci> let multWithEighteen = multTwoWithNine 2
+ghci> multWithEighteen 10
+180
+

By calling functions with too few parameters, so to speak, we’re +creating new functions on the fly. What if we wanted to create a +function that takes a number and compares it to 100? We +could do something like this:

+
compareWithHundred :: (Num a, Ord a) => a -> Ordering
+compareWithHundred x = compare 100 x
+

If we call it with 99, it returns a GT. +Simple stuff. Notice that the x is on the right-hand side +on both sides of the equation. Now let’s think about what +compare 100 returns. It returns a function that takes a +number and compares it with 100. Wow! Isn’t that the +function we wanted? We can rewrite this as:

+
compareWithHundred :: (Num a, Ord a) => a -> Ordering
+compareWithHundred = compare 100
+

The type declaration stays the same, because compare 100 +returns a function. Compare has a type of +(Ord a) => a -> (a -> Ordering) and calling it +with 100 returns a +(Num a, Ord a) => a -> Ordering. The additional class +constraint sneaks up there because 100 is also part of the +Num typeclass.

+
+

Yo! Make sure you really understand how curried +functions and partial application work because they’re really +important!

+
+

Infix functions can also be partially applied by using sections. To +section an infix function, simply surround it with parentheses and only +supply a parameter on one side. That creates a function that takes one +parameter and then applies it to the side that’s missing an operand. An +insultingly trivial function:

+
divideByTen :: (Floating a) => a -> a
+divideByTen = (/10)
+

Calling, say, divideByTen 200 is equivalent to doing +200 / 10, as is doing (/10) 200. A function +that checks if a character supplied to it is an uppercase letter:

+
isUpperAlphanum :: Char -> Bool
+isUpperAlphanum = (`elem` ['A'..'Z'])
+

The only special thing about sections is using -. From +the definition of sections, (-4) would result in a function +that takes a number and subtracts 4 from it. However, for convenience, +(-4) means minus four. So if you want to make a function +that subtracts 4 from the number it gets as a parameter, partially apply +the subtract function like so: +(subtract 4).

+

What happens if we try to just do multThree 3 4 in GHCI +instead of binding it to a name with a let or passing it to +another function?

+
ghci> multThree 3 4
+<interactive>:1:1: error: [GHC-39999]
+    • No instance for ‘Show (a0 -> a0)’ arising from a use of ‘print’
+        (maybe you haven't applied a function to enough arguments?)
+    • In a stmt of an interactive GHCi command: print it
+

GHCI is telling us that the expression produced a function of type +a -> a but it doesn’t know how to print it to the +screen. Functions aren’t instances of the Show typeclass, +so we can’t get a neat string representation of a function. When we do, +say, 1 + 1 at the GHCI prompt, it first calculates that to +2 and then calls show on 2 to get +a textual representation of that number. And the textual representation +of 2 is just the string "2", which then gets +printed to our screen.

+

Some higher-orderism is in order

+

Functions can take functions as parameters and also return functions. +To illustrate this, we’re going to make a function that takes a function +and then applies it twice to something!

+
applyTwice :: (a -> a) -> a -> a
+applyTwice f x = f (f x)
+

rocktopus

+

First of all, notice the type declaration. Before, we didn’t need +parentheses because -> is naturally right-associative. +However, here, they’re mandatory. They indicate that the first parameter +is a function that takes something and returns that same thing. The +second parameter is something of that type also and the return value is +also of the same type. We could read this type declaration in the +curried way, but to save ourselves a headache, we’ll just say that this +function takes two parameters and returns one thing. The first parameter +is a function (of type a -> a) and the second is that +same a. The function can also be Int -> Int +or String -> String or whatever. But then, the second +parameter to also has to be of that type.

+
+

Note: From now on, we’ll say that functions take +several parameters despite each function actually taking only one +parameter and returning partially applied functions until we reach a +function that returns a solid value. So for simplicity’s sake, we’ll say +that a -> a -> a takes two parameters, even though we +know what’s really going on under the hood.

+
+

The body of the function is pretty simple. We just use the parameter +f as a function, applying x to it by +separating them with a space and then applying the result to +f again. Anyway, playing around with the function:

+
ghci> applyTwice (+3) 10
+16
+ghci> applyTwice (++ " HAHA") "HEY"
+"HEY HAHA HAHA"
+ghci> applyTwice ("HAHA " ++) "HEY"
+"HAHA HAHA HEY"
+ghci> applyTwice (multThree 2 2) 9
+144
+ghci> applyTwice (3:) [1]
+[3,3,1]
+

The awesomeness and usefulness of partial application is evident. If +our function requires us to pass it a function that takes only one +parameter, we can just partially apply a function to the point where it +takes only one parameter and then pass it.

+

Now we’re going to use higher order programming to implement a really +useful function that’s in the standard library. It’s called +zipWith. It takes a function and two lists as parameters +and then joins the two lists by applying the function between +corresponding elements. Here’s how we’ll implement it:

+
zipWith' :: (a -> b -> c) -> [a] -> [b] -> [c]
+zipWith' _ [] _ = []
+zipWith' _ _ [] = []
+zipWith' f (x:xs) (y:ys) = f x y : zipWith' f xs ys
+

Look at the type declaration. The first parameter is a function that +takes two things and produces a third thing. They don’t have to be of +the same type, but they can. The second and third parameter are lists. +The result is also a list. The first has to be a list of +a’s, because the joining function takes a’s as +its first argument. The second has to be a list of b’s, +because the second parameter of the joining function is of type +b. The result is a list of c’s. If the type +declaration of a function says it accepts an +a -> b -> c function as a parameter, it will also +accept an a -> a -> a function, but not the other way +around! Remember that when you’re making functions, especially higher +order ones, and you’re unsure of the type, you can just try omitting the +type declaration and then checking what Haskell infers it to be by using +:t.

+

The action in the function is pretty similar to the normal +zip. The edge conditions are the same, only there’s an +extra argument, the joining function, but that argument doesn’t matter +in the edge conditions, so we just use a _ for it. And +function body at the last pattern is also similar to zip, +only it doesn’t do (x,y), but f x y. A single +higher order function can be used for a multitude of different tasks if +it’s general enough. Here’s a little demonstration of all the different +things our zipWith' function can do:

+
ghci> zipWith' (+) [4,2,5,6] [2,6,2,3]
+[6,8,7,9]
+ghci> zipWith' max [6,3,2,1] [7,3,1,5]
+[7,3,2,5]
+ghci> zipWith' (++) ["foo ", "bar ", "baz "] ["fighters", "hoppers", "aldrin"]
+["foo fighters","bar hoppers","baz aldrin"]
+ghci> zipWith' (*) (replicate 5 2) [1..]
+[2,4,6,8,10]
+ghci> zipWith' (zipWith' (*)) [[1,2,3],[3,5,6],[2,3,4]] [[3,2,2],[3,4,5],[5,4,3]]
+[[3,4,6],[9,20,30],[10,12,12]]
+

As you can see, a single higher order function can be used in very +versatile ways. Imperative programming usually uses stuff like for +loops, while loops, setting something to a variable, checking its state, +etc. to achieve some behavior and then wrap it around an interface, like +a function. Functional programming uses higher order functions to +abstract away common patterns, like examining two lists in pairs and +doing something with those pairs or getting a set of solutions and +eliminating the ones you don’t need.

+

We’ll implement another function that’s already in the standard +library, called flip. Flip simply takes a function and +returns a function that is like our original function, only the first +two arguments are flipped. We can implement it like so:

+
flip' :: (a -> b -> c) -> (b -> a -> c)
+flip' f = g
+    where g x y = f y x
+

Reading the type declaration, we say that it takes a function that +takes an a and a b and returns a function that +takes a b and an a. But because functions are +curried by default, the second pair of parentheses is really +unnecessary, because -> is right associative by default. +(a -> b -> c) -> (b -> a -> c) is the same +as (a -> b -> c) -> (b -> (a -> c)), which +is the same as (a -> b -> c) -> b -> a -> c. +We wrote that g x y = f y x. If that’s true, then +f y x = g x y must also hold, right? Keeping that in mind, +we can define this function in an even simpler manner.

+
flip' :: (a -> b -> c) -> b -> a -> c
+flip' f y x = f x y
+

Here, we take advantage of the fact that functions are curried. When +we call flip' f without the parameters y and +x, it will return an f that takes those two +parameters but calls them flipped. Even though flipped functions are +usually passed to other functions, we can take advantage of currying +when making higher-order functions by thinking ahead and writing what +their end result would be if they were called fully applied.

+
ghci> flip' zip [1,2,3,4,5] "hello"
+[('h',1),('e',2),('l',3),('l',4),('o',5)]
+ghci> zipWith (flip' div) [2,2..] [10,8,6,4,2]
+[5,4,3,2,1]
+

Maps and filters

+

map takes a function and a list +and applies that function to every element in the list, producing a new +list. Let’s see what its type signature is and how it’s defined.

+
map :: (a -> b) -> [a] -> [b]
+map _ [] = []
+map f (x:xs) = f x : map f xs
+

The type signature says that it takes a function that takes an +a and returns a b, a list of a’s +and returns a list of b’s. It’s interesting that just by +looking at a function’s type signature, you can sometimes tell what it +does. map is one of those really versatile higher-order +functions that can be used in millions of different ways. Here it is in +action:

+
ghci> map (+3) [1,5,3,1,6]
+[4,8,6,4,9]
+ghci> map (++ "!") ["BIFF", "BANG", "POW"]
+["BIFF!","BANG!","POW!"]
+ghci> map (replicate 3) [3..6]
+[[3,3,3],[4,4,4],[5,5,5],[6,6,6]]
+ghci> map (map (^2)) [[1,2],[3,4,5,6],[7,8]]
+[[1,4],[9,16,25,36],[49,64]]
+ghci> map fst [(1,2),(3,5),(6,3),(2,6),(2,5)]
+[1,3,6,2,2]
+

You’ve probably noticed that each of these could be achieved with a +list comprehension. map (+3) [1,5,3,1,6] is the same as +writing [x+3 | x <- [1,5,3,1,6]]. However, using +map is much more readable for cases where you only apply +some function to the elements of a list, especially once you’re dealing +with maps of maps and then the whole thing with a lot of brackets can +get a bit messy.

+

filter is a function that takes a +predicate (a predicate is a function that tells whether something is +true or not, so in our case, a function that returns a boolean value) +and a list and then returns the list of elements that satisfy the +predicate. The type signature and implementation go like this:

+
filter :: (a -> Bool) -> [a] -> [a]
+filter _ [] = []
+filter p (x:xs)
+    | p x       = x : filter p xs
+    | otherwise = filter p xs
+

Pretty simple stuff. If p x evaluates to +True, the element gets included in the new list. If it +doesn’t, it stays out. Some usage examples:

+
ghci> filter (>3) [1,5,3,2,1,6,4,3,2,1]
+[5,6,4]
+ghci> filter (==3) [1,2,3,4,5]
+[3]
+ghci> filter even [1..10]
+[2,4,6,8,10]
+ghci> let notNull x = not (null x) in filter notNull [[1,2,3],[],[3,4,5],[2,2],[],[],[]]
+[[1,2,3],[3,4,5],[2,2]]
+ghci> filter (`elem` ['a'..'z']) "u LaUgH aT mE BeCaUsE I aM diFfeRent"
+"uagameasadifeent"
+ghci> filter (`elem` ['A'..'Z']) "i Laugh At you Because u R All The Same"
+"LABRATS"
+

All of this could also be achieved with list comprehensions by the +use of predicates. There’s no set rule for when to use map +and filter versus using list comprehension, you just have +to decide what’s more readable depending on the code and the context. +The filter equivalent of applying several predicates in a +list comprehension is either filtering something several times or +joining the predicates with the logical && +function.

+

Remember our quicksort function from the previous chapter? We used list comprehensions +to filter out the list elements that are smaller than (or equal to) and +larger than the pivot. We can achieve the same functionality in a more +readable way by using filter:

+
quicksort :: (Ord a) => [a] -> [a]
+quicksort [] = []
+quicksort (x:xs) =
+    let smallerSorted = quicksort (filter (<=x) xs)
+        biggerSorted = quicksort (filter (>x) xs)
+    in  smallerSorted ++ [x] ++ biggerSorted
+

map

+

Mapping and filtering is the bread and butter of every functional +programmer’s toolbox. Uh. It doesn’t matter if you do it with the +map and filter functions or list +comprehensions. Recall how we solved the problem of finding right +triangles with a certain circumference. With imperative programming, we +would have solved it by nesting three loops and then testing if the +current combination satisfies a right triangle and if it has the right +perimeter. If that’s the case, we would have printed it out to the +screen or something. In functional programming, that pattern is achieved +with mapping and filtering. You make a function that takes a value and +produces some result. We map that function over a list of values and +then we filter the resulting list out for the results that satisfy our +search. Thanks to Haskell’s laziness, even if you map something over a +list several times and filter it several times, it will only pass over +the list once.

+

Let’s find the largest number under 100,000 that’s divisible +by 3829. To do that, we’ll just filter a set of possibilities +in which we know the solution lies.

+
largestDivisible :: (Integral a) => a
+largestDivisible = head (filter p [100000,99999..])
+    where p x = x `mod` 3829 == 0
+

We first make a list of all numbers lower than 100,000, descending. +Then we filter it by our predicate and because the numbers are sorted in +a descending manner, the largest number that satisfies our predicate is +the first element of the filtered list. We didn’t even need to use a +finite list for our starting set. That’s laziness in action again. +Because we only end up using the head of the filtered list, it doesn’t +matter if the filtered list is finite or infinite. The evaluation stops +when the first adequate solution is found.

+

Next up, we’re going to find the sum of all odd squares that +are smaller than 10,000. But first, because we’ll be using it +in our solution, we’re going to introduce the takeWhile function. It takes a predicate +and a list and then goes from the beginning of the list and returns its +elements while the predicate holds true. Once an element is found for +which the predicate doesn’t hold, it stops. If we wanted to get the +first word of the string "elephants know how to party", we +could do takeWhile (/=' ') "elephants know how to party" +and it would return "elephants". Okay. The sum of all odd +squares that are smaller than 10,000. First, we’ll begin by mapping the +(^2) function to the infinite list [1..]. Then +we filter them so we only get the odd ones. And then, we’ll take +elements from that list while they are smaller than 10,000. Finally, +we’ll get the sum of that list. We don’t even have to define a function +for that, we can do it in one line in GHCI:

+
ghci> sum (takeWhile (<10000) (filter odd (map (^2) [1..])))
+166650
+

Awesome! We start with some initial data (the infinite list of all +natural numbers) and then we map over it, filter it and cut it until it +suits our needs and then we just sum it up. We could have also written +this using list comprehensions:

+
ghci> sum (takeWhile (<10000) [n^2 | n <- [1..], odd (n^2)])
+166650
+

It’s a matter of taste as to which one you find prettier. Again, +Haskell’s property of laziness is what makes this possible. We can map +over and filter an infinite list, because it won’t actually map and +filter it right away, it’ll delay those actions. Only when we force +Haskell to show us the sum does the sum function say to the +takeWhile that it needs those numbers. +takeWhile forces the filtering and mapping to occur, but +only until a number greater than or equal to 10,000 is encountered.

+

For our next problem, we’ll be dealing with Collatz sequences. We +take a natural number. If that number is even, we divide it by two. If +it’s odd, we multiply it by 3 and then add 1 to that. We take the +resulting number and apply the same thing to it, which produces a new +number and so on. In essence, we get a chain of numbers. It is thought +that for all starting numbers, the chains finish at the number 1. So if +we take the starting number 13, we get this sequence: 13, 40, 20, +10, 5, 16, 8, 4, 2, 1. 13*3 + 1 equals 40. 40 divided by 2 is 20, +etc. We see that the chain has 10 terms.

+

Now what we want to know is this: for all starting numbers +between 1 and 100, how many chains have a length greater than +15? First off, we’ll write a function that produces a +chain:

+
chain :: (Integral a) => a -> [a]
+chain 1 = [1]
+chain n
+    | even n =  n:chain (n `div` 2)
+    | odd n  =  n:chain (n*3 + 1)
+

Because the chains end at 1, that’s the edge case. This is a pretty +standard recursive function.

+
ghci> chain 10
+[10,5,16,8,4,2,1]
+ghci> chain 1
+[1]
+ghci> chain 30
+[30,15,46,23,70,35,106,53,160,80,40,20,10,5,16,8,4,2,1]
+

Yay! It seems to be working correctly. And now, the function that +tells us the answer to our question:

+
numLongChains :: Int
+numLongChains = length (filter isLong (map chain [1..100]))
+    where isLong xs = length xs > 15
+

We map the chain function to [1..100] to +get a list of chains, which are themselves represented as lists. Then, +we filter them by a predicate that just checks whether a list’s length +is longer than 15. Once we’ve done the filtering, we see how many chains +are left in the resulting list.

+
+

Note: This function has a type of +numLongChains :: Int because length returns an +Int instead of a Num a for historical reasons. +If we wanted to return a more general Num a, we could have +used fromIntegral on the resulting length.

+
+

Using map, we can also do stuff like +map (*) [0..], if not for any other reason than to +illustrate how currying works and how (partially applied) functions are +real values that you can pass around to other functions or put into +lists (you just can’t turn them to strings). So far, we’ve only mapped +functions that take one parameter over lists, like +map (*2) [0..] to get a list of type +(Num a) => [a], but we can also do +map (*) [0..] without a problem. What happens here is that +the number in the list is applied to the function *, which +has a type of (Num a) => a -> a -> a. Applying +only one parameter to a function that takes two parameters returns a +function that takes one parameter. If we map * over the +list [0..], we get back a list of functions that only take +one parameter, so (Num a) => [a -> a]. +map (*) [0..] produces a list like the one we’d get by +writing [(0*),(1*),(2*),(3*),(4*),(5*)...

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

+

Lambdas

+

lambda

+

Lambdas are basically anonymous functions that are used because we +need some functions only once. Normally, we make a lambda with the sole +purpose of passing it to a higher-order function. To make a lambda, we +write a \ (because it kind of looks like the greek letter +lambda if you squint hard enough) and then we write the parameters, +separated by spaces. After that comes a -> and then the +function body. We usually surround them by parentheses, because +otherwise they extend all the way to the right.

+

If you look about 5 inches up, you’ll see that we used a +where binding in our numLongChains function to +make the isLong function for the sole purpose of passing it +to filter. Well, instead of doing that, we can use a +lambda:

+
numLongChains :: Int
+numLongChains = length (filter (\xs -> length xs > 15) (map chain [1..100]))
+

Lambdas are expressions, that’s why we can just pass them like that. +The expression (\xs -> length xs > 15) returns a +function that tells us whether the length of the list passed to it is +greater than 15.

+

lamb

+

People who are not well acquainted with how currying and partial +application works often use lambdas where they don’t need to. For +instance, the expressions map (+3) [1,6,3,2] and +map (\x -> x + 3) [1,6,3,2] are equivalent since both +(+3) and (\x -> x + 3) are functions that +take a number and add 3 to it. Needless to say, making a lambda in this +case is stupid since using partial application is much more +readable.

+

Like normal functions, lambdas can take any number of parameters:

+
ghci> zipWith (\a b -> (a * 30 + 3) / b) [5,4,3,2,1] [1,2,3,4,5]
+[153.0,61.5,31.0,15.75,6.6]
+

And like normal functions, you can pattern match in lambdas. The only +difference is that you can’t define several patterns for one parameter, +like making a [] and a (x:xs) pattern for the +same parameter and then having values fall through. If a pattern +matching fails in a lambda, a runtime error occurs, so be careful when +pattern matching in lambdas!

+
ghci> map (\(a,b) -> a + b) [(1,2),(3,5),(6,3),(2,6),(2,5)]
+[3,8,9,8,7]
+

Lambdas are normally surrounded by parentheses unless we mean for +them to extend all the way to the right. Here’s something interesting: +due to the way functions are curried by default, these two are +equivalent:

+
addThree :: (Num a) => a -> a -> a -> a
+addThree x y z = x + y + z
+
addThree :: (Num a) => a -> a -> a -> a
+addThree = \x -> \y -> \z -> x + y + z
+

If we define a function like this, it’s obvious why the type +declaration is what it is. There are three ->’s in both +the type declaration and the equation. But of course, the first way to +write functions is far more readable, the second one is pretty much a +gimmick to illustrate currying.

+

However, there are times when using this notation is cool. I think +that the flip function is the most readable when defined +like so:

+
flip' :: (a -> b -> c) -> b -> a -> c
+flip' f = \x y -> f y x
+

Even though that’s the same as writing +flip' f x y = f y x, we make it obvious that this will be +used for producing a new function most of the time. The most common use +case with flip is calling it with just the function +parameter and then passing the resulting function on to a map or a +filter. So use lambdas in this way when you want to make it explicit +that your function is mainly meant to be partially applied and passed on +to a function as a parameter.

+

Only folds and horses

+

folded bird

+

Back when we were dealing with recursion, we noticed a theme +throughout many of the recursive functions that operated on lists. +Usually, we’d have an edge case for the empty list. We’d introduce the +x:xs pattern and then we’d do some action that involves a +single element and the rest of the list. It turns out this is a very +common pattern, so a couple of very useful functions were introduced to +encapsulate it. These functions are called folds. They’re sort of like +the map function, only they reduce the list to some single +value.

+

A fold takes a binary function, a starting value (I like to call it +the accumulator) and a list to fold up. The binary function itself takes +two parameters. The binary function is called with the accumulator and +the first (or last) element and produces a new accumulator. Then, the +binary function is called again with the new accumulator and the now new +first (or last) element, and so on. Once we’ve walked over the whole +list, only the accumulator remains, which is what we’ve reduced the list +to.

+

First let’s take a look at the foldl function, also called the left fold. +It folds the list up from the left side. The binary function is applied +between the starting value and the head of the list. That produces a new +accumulator value and the binary function is called with that value and +the next element, etc.

+

Let’s implement sum again, only this time, we’ll use a +fold instead of explicit recursion.

+
sum' :: (Num a) => [a] -> a
+sum' xs = foldl (\acc x -> acc + x) 0 xs
+

Testing, one two three:

+
ghci> sum' [3,5,2,1]
+11
+

foldl

+

Let’s take an in-depth look into how this fold happens. +\acc x -> acc + x is the binary function. 0 +is the starting value and xs is the list to be folded up. +Now first, 0 is used as the acc parameter to +the binary function and 3 is used as the x (or +the current element) parameter. 0 + 3 produces a +3 and it becomes the new accumulator value, so to speak. +Next up, 3 is used as the accumulator value and +5 as the current element and 8 becomes the new +accumulator value. Moving forward, 8 is the accumulator +value, 2 is the current element, the new accumulator value +is 10. Finally, that 10 is used as the +accumulator value and 1 as the current element, producing +an 11. Congratulations, you’ve done a fold!

+

This professional diagram on the left illustrates how a fold happens, +step by step (day by day!). The greenish brown number is the accumulator +value. You can see how the list is sort of consumed up from the left +side by the accumulator. Om nom nom nom! If we take into account that +functions are curried, we can write this implementation ever more +succinctly, like so:

+
sum' :: (Num a) => [a] -> a
+sum' = foldl (+) 0
+

The lambda function (\acc x -> acc + x) is the same +as (+). We can omit the xs as the parameter +because calling foldl (+) 0 will return a function that +takes a list. Generally, if you have a function like +foo a = bar b a, you can rewrite it as +foo = bar b, because of currying.

+

Anyhoo, let’s implement another function with a left fold before +moving on to right folds. I’m sure you all know that elem +checks whether a value is part of a list so I won’t go into that again +(whoops, just did!). Let’s implement it with a left fold.

+
elem' :: (Eq a) => a -> [a] -> Bool
+elem' y ys = foldl (\acc x -> if x == y then True else acc) False ys
+

Well, well, well, what do we have here? The starting value and +accumulator here is a boolean value. The type of the accumulator value +and the end result is always the same when dealing with folds. Remember +that if you ever don’t know what to use as a starting value, it’ll give +you some idea. We start off with False. It makes sense to +use False as a starting value. We assume it isn’t there. +Also, if we call a fold on an empty list, the result will just be the +starting value. Then we check the current element is the element we’re +looking for. If it is, we set the accumulator to True. If +it’s not, we just leave the accumulator unchanged. If it was +False before, it stays that way because this current +element is not it. If it was True, we leave it at that.

+

The right fold, foldr works in a +similar way to the left fold, only the accumulator eats up the values +from the right. Also, the left fold’s binary function has the +accumulator as the first parameter and the current value as the second +one (so \acc x -> ...), the right fold’s binary function +has the current value as the first parameter and the accumulator as the +second one (so \x acc -> ...). It kind of makes sense +that the right fold has the accumulator on the right, because it folds +from the right side.

+

The accumulator value (and hence, the result) of a fold can be of any +type. It can be a number, a boolean or even a new list. We’ll be +implementing the map function with a right fold. The accumulator will be +a list, we’ll be accumulating the mapped list element by element. From +that, it’s obvious that the starting element will be an empty list.

+
map' :: (a -> b) -> [a] -> [b]
+map' f xs = foldr (\x acc -> f x : acc) [] xs
+

If we’re mapping (+3) to [1,2,3], we +approach the list from the right side. We take the last element, which +is 3 and apply the function to it, which ends up being +6. Then, we prepend it to the accumulator, which is +[]. 6:[] is [6] and that’s now +the accumulator. We apply (+3) to 2, that’s +5 and we prepend (:) it to the accumulator, so +the accumulator is now [5,6]. We apply (+3) to +1 and prepend that to the accumulator and so the end value +is [4,5,6].

+

Of course, we could have implemented this function with a left fold +too. It would be +map' f xs = foldl (\acc x -> acc ++ [f x]) [] xs, but +the thing is that the ++ function is much more expensive +than :, so we usually use right folds when we’re building +up new lists from a list.

+

fold this up!

+

If you reverse a list, you can do a right fold on it just like you +would have done a left fold and vice versa. Sometimes you don’t even +have to do that. The sum function can be implemented pretty +much the same with a left and right fold. One big difference is that +right folds work on infinite lists, whereas left ones don’t! To put it +plainly, if you take an infinite list at some point and you fold it up +from the right, you’ll eventually reach the beginning of the list. +However, if you take an infinite list at a point and you try to fold it +up from the left, you’ll never reach an end!

+

Folds can be used to implement any function where you +traverse a list once, element by element, and then return something +based on that. Whenever you want to traverse a list to return something, +chances are you want a fold. That’s why folds are, along with +maps and filters, one of the most useful types of functions in +functional programming.

+

The foldl1 and foldr1 functions work much like +foldl and foldr, only you don’t need to +provide them with an explicit starting value. They assume the first (or +last) element of the list to be the starting value and then start the +fold with the element next to it. With that in mind, the +sum function can be implemented like so: +sum = foldl1 (+). Because they depend on the lists they +fold up having at least one element, they cause runtime errors if called +with empty lists. foldl and foldr, on the +other hand, work fine with empty lists. When making a fold, think about +how it acts on an empty list. If the function doesn’t make sense when +given an empty list, you can probably use a foldl1 or +foldr1 to implement it.

+

Just to show you how powerful folds are, we’re going to implement a +bunch of standard library functions by using folds:

+
maximum' :: (Ord a) => [a] -> a
+maximum' = foldr1 (\x acc -> if x > acc then x else acc)
+
+reverse' :: [a] -> [a]
+reverse' = foldl (\acc x -> x : acc) []
+
+product' :: (Num a) => [a] -> a
+product' = foldr1 (*)
+
+filter' :: (a -> Bool) -> [a] -> [a]
+filter' p = foldr (\x acc -> if p x then x : acc else acc) []
+
+head' :: [a] -> a
+head' = foldr1 (\x _ -> x)
+
+last' :: [a] -> a
+last' = foldl1 (\_ x -> x)
+

head is better implemented by pattern matching, but this +just goes to show, you can still achieve it by using folds. Our +reverse' definition is pretty clever, I think. We take a +starting value of an empty list and then approach our list from the left +and just prepend to our accumulator. In the end, we build up a reversed +list. \acc x -> x : acc kind of looks like the +: function, only the parameters are flipped. That’s why we +could have also written our reverse as +foldl (flip (:)) [].

+

Another way to picture right and left folds is like this: say we have +a right fold and the binary function is f and the starting +value is z. If we’re right folding over the list +[3,4,5,6], we’re essentially doing this: +f 3 (f 4 (f 5 (f 6 z))). f is called with the +last element in the list and the accumulator, that value is given as the +accumulator to the next to last value and so on. If we take +f to be + and the starting accumulator value +to be 0, that’s 3 + (4 + (5 + (6 + 0))). Or if +we write + as a prefix function, that’s +(+) 3 ((+) 4 ((+) 5 ((+) 6 0))). Similarly, doing a left +fold over that list with g as the binary function and +z as the accumulator is the equivalent of +g (g (g (g z 3) 4) 5) 6. If we use flip (:) as +the binary function and [] as the accumulator (so we’re +reversing the list), then that’s the equivalent of +flip (:) (flip (:) (flip (:) (flip (:) [] 3) 4) 5) 6. And +sure enough, if you evaluate that expression, you get +[6,5,4,3].

+

scanl and scanr are like foldl and +foldr, only they report all the intermediate accumulator +states in the form of a list. There are also scanl1 and +scanr1, which are analogous to foldl1 and +foldr1.

+
ghci> scanl (+) 0 [3,5,2,1]
+[0,3,8,10,11]
+ghci> scanr (+) 0 [3,5,2,1]
+[11,8,3,1,0]
+ghci> scanl1 (\acc x -> if x > acc then x else acc) [3,4,5,3,7,9,2,1]
+[3,4,5,5,7,9,9,9]
+ghci> scanl (flip (:)) [] [3,2,1]
+[[],[3],[2,3],[1,2,3]]
+

When using a scanl, the final result will be in the last +element of the resulting list while a scanr will place the +result in the head.

+

Scans are used to monitor the progression of a function that can be +implemented as a fold. Let’s answer us this question: How many +elements does it take for the sum of the 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.

+

Function application with $

+

Alright, next up, we’ll take a look at the $ function, +also called function application. First of all, let’s check out +how it’s defined:

+
($) :: (a -> b) -> a -> b
+f $ x = f x
+

dollar

+

What the heck? What is this useless operator? It’s just function +application! Well, almost, but not quite! Whereas normal function +application (putting a space between two things) has a really high +precedence, the $ function has the lowest precedence. +Function application with a space is left-associative (so +f a b c is the same as ((f a) b) c)), function +application with $ is right-associative.

+

That’s all very well, but how does this help us? Most of the time, +it’s a convenience function so that we don’t have to write so many +parentheses. Consider the expression +sum (map sqrt [1..130]). Because $ has such a +low precedence, we can rewrite that expression as +sum $ map sqrt [1..130], saving ourselves precious +keystrokes! When a $ is encountered, the expression on its +right is applied as the parameter to the function on its left. How about +sqrt 3 + 4 + 9? This adds together 9, 4 and the square root +of 3. If we want to get the square root of 3 + 4 + 9, we’d have +to write sqrt (3 + 4 + 9) or if we use $ we +can write it as sqrt $ 3 + 4 + 9 because $ has +the lowest precedence of any operator. That’s why you can imagine a +$ being sort of the equivalent of writing an opening +parenthesis and then writing a closing one on the far right side of the +expression.

+

How about sum (filter (> 10) (map (*2) [2..10]))? +Well, because $ is right-associative, +f (g (z x)) is equal to f $ g $ z x. And so, +we can rewrite sum (filter (> 10) (map (*2) [2..10])) as +sum $ filter (> 10) $ map (*2) [2..10].

+

But apart from getting rid of parentheses, $ means that +function application can be treated just like another function. That +way, we can, for instance, map function application over a list of +functions.

+
ghci> map ($ 3) [(4+), (10*), (^2), sqrt]
+[7.0,30.0,9.0,1.7320508075688772]
+

Function composition

+

In mathematics, function composition is defined like this: , 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
+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/markdown/generated_html/introduction.html b/markdown/generated_html/introduction.html new file mode 100644 index 0000000..e3a34db --- /dev/null +++ b/markdown/generated_html/introduction.html @@ -0,0 +1,200 @@ + + + +Introduction - Learn You a Haskell for Great Good! + + + + + + + + + +
+
+
+ +
+

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. If IRC it not your cup of tea, https://discourse.haskell.org +is a popular community forum with a section for learners.

+

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.

+

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.

+
+ +
+
+ + + + +
+ + + + diff --git a/markdown/generated_html/making-our-own-types-and-typeclasses.html b/markdown/generated_html/making-our-own-types-and-typeclasses.html new file mode 100644 index 0000000..7dfa5d5 --- /dev/null +++ b/markdown/generated_html/making-our-own-types-and-typeclasses.html @@ -0,0 +1,1906 @@ + + + +Making Our Own Types and Typeclasses - Learn You a Haskell for Great Good! + + + + + + + + + + +
+
+
+ +
+

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

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

+

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.

+

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
+
+lastName :: Person -> String
+lastName (Person _ lastname _ _ _ _) = lastname
+
+age :: Person -> Int
+age (Person _ _ age _ _ _) = age
+
+height :: Person -> Float
+height (Person _ _ _ height _ _) = height
+
+phoneNumber :: Person -> String
+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"
+ghci> firstName guy
+"Buddy"
+ghci> height guy
+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.

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

+

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

+

Let’s play around with the Maybe type.

+
ghci> Just "Haha"
+Just "Haha"
+ghci> Just 84
+Just 84
+ghci> :t Just "Haha"
+Just "Haha" :: Maybe [Char]
+ghci> :t Just 84
+Just 84 :: (Num t) => Maybe t
+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
+               , 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.

+
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"
+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)
+
+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)
+
+vectMult :: (Num t) => Vector t -> t -> Vector t
+(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 12 7 16
+ghci> Vector 3 5 8 `vplus` Vector 9 2 8 `vplus` Vector 0 2 3
+Vector 12 9 19
+ghci> Vector 3 9 7 `vectMult` 10
+Vector 30 90 70
+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
+

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.

+

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.

+
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}
+ghci> let adRock = Person {firstName = "Adam", lastName = "Horovitz", age = 41}
+ghci> let mca = Person {firstName = "Adam", lastName = "Yauch", age = 44}
+ghci> mca == adRock
+False
+ghci> mikeD == adRock
+False
+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]
+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
+                     , 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.

+
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
+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
+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
+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
+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
+Tuesday
+ghci> pred Saturday
+Friday
+ghci> [Thursday .. Sunday]
+[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].

+
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")
+    ,("isabella","493-2928")
+    ,("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.

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

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
+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
+
+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
+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
+lockers = Map.fromList
+    [(100,(Taken,"ZD39I"))
+    ,(101,(Free,"JAH3I"))
+    ,(103,(Free,"IQSA9"))
+    ,(105,(Free,"QOTSA"))
+    ,(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
+Left "Locker 100 is already taken!"
+ghci> lockerLookup 102 lockers
+Left "Locker number 102 doesn't exist!"
+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.

+

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

+

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
+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
+(:-:) 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  ++
+(++) :: [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  .++
+(.++) :: 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. 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
+treeInsert x EmptyTree = singleton x
+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
+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.

+

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
+

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

+

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
+    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
+    (==) :: 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
+    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
+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
+    (==) :: 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
+    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
+    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.

+

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

+
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
+    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 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"
+True
+ghci> yesno ""
+False
+ghci> yesno $ Just 0
+True
+ghci> yesno True
+True
+ghci> yesno EmptyTree
+False
+ghci> yesno []
+False
+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!"
+"NO!"
+ghci> yesnoIf [2,3,4] "YEAH!" "NO!"
+"YEAH!"
+ghci> yesnoIf True "YEAH!" "NO!"
+"YEAH!"
+ghci> yesnoIf (Just 500) "YEAH!" "NO!"
+"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.

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

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

+
+

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.

+

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.

+

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

+
+ +
+
+ + + + +
+ + + + diff --git a/markdown/generated_html/starting-out.html b/markdown/generated_html/starting-out.html new file mode 100644 index 0000000..3a6471f --- /dev/null +++ b/markdown/generated_html/starting-out.html @@ -0,0 +1,801 @@ + + + +Starting Out - Learn You a Haskell for Great Good! + + + + + + + + + + +
+
+ +

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
+17
+ghci> 49 * 100
+4900
+ghci> 1892 - 1472
+420
+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
+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
+False
+ghci> True && True
+True
+ghci> False || True
+True
+ghci> not False
+True
+ghci> not (True && True)
+False
+

Testing for equality is done like so.

+
ghci> 5 == 5
+True
+ghci> 1 == 0
+False
+ghci> 5 /= 5
+False
+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!

+
<interactive>:1:1: error: [GHC-39999]
+    • No instance for ‘Num String’ arising from the literal ‘5’
+    • In the first argument of ‘(+)’, namely ‘5’
+      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.

+
+

Note: GHC errors are all assigned unique identifiers +such as GHC-39999 above. Whenever you are stuck with a +stubborn error, you can look it up at https://errors.haskell.org/ to +learn typical causes and solutions.

+
+

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

+

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

+

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]
+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]
+[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"
+"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
+'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]]
+ghci> b ++ [[1,1,1,1]]
+[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]
+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]
+True
+ghci> [3,2,1] > [2,10,100]
+True
+ghci> [3,4,2] > [3,4]
+True
+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]
+

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.

+
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]
+False
+ghci> null []
+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.

+
ghci> take 3 [5,4,3,2,1]
+[5,4,3]
+ghci> take 1 [3,9,3]
+[3]
+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.

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

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

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.

+

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])
+[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].

+

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 . 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"]
+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!"
+"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]]
+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:

+
<interactive>:1:8: error: [GHC-83865]
+    • 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)
+8
+ghci> fst ("Wow", False)
+"Wow"
+

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]
+[(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]
+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/markdown/generated_html/types-and-typeclasses.html b/markdown/generated_html/types-and-typeclasses.html new file mode 100644 index 0000000..dee3ef2 --- /dev/null +++ b/markdown/generated_html/types-and-typeclasses.html @@ -0,0 +1,445 @@ + + + +Types and Typeclasses - Learn You a Haskell for Great Good! + + + + + + + + + + +
+
+ +

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'
+'a' :: Char
+ghci> :t True
+True :: Bool
+ghci> :t "HELLO!"
+"HELLO!" :: [Char]
+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.

+

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

+

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.

+

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?

+
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
+True
+ghci> 5 /= 5
+False
+ghci> 'a' == 'a'
+True
+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
+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
+"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
+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"
+<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
+5
+ghci> read "5" :: Float
+5.0
+ghci> (read "5" :: Float) * 4
+20.0
+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']
+"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
+-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
+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. 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.

+ +
+ + + + +
+ + + + From dd138a0dee7c36a1130b9701551fd9ff26c98347 Mon Sep 17 00:00:00 2001 From: Oliver Webb Date: Mon, 23 Dec 2024 10:59:17 -0600 Subject: [PATCH 2/2] Most variables don't need the `$` in `$((shell math))` --- markdown/generate.sh | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/markdown/generate.sh b/markdown/generate.sh index 437f9c3..d46a4fa 100755 --- a/markdown/generate.sh +++ b/markdown/generate.sh @@ -45,7 +45,14 @@ do >>>>>>> c1fa241 (re-generate html, concatenate sed commands, remove trailing whitespace) chnum=$((i + 1)) +<<<<<<< HEAD + if [[ $chnum -ge 10 ]]; +||||||| parent of 56bfa42 (Most variables don't need the `$` in `$((shell math))`) + chnum=$(($i + 1)) if [[ $chnum -ge 10 ]]; +======= + if ((chnum >= 10)) +>>>>>>> 56bfa42 (Most variables don't need the `$` in `$((shell math))`) then sp=" " else @@ -76,7 +83,12 @@ done # For every input MD file produce and HTML file for i in "${!filename[@]}" do +<<<<<<< HEAD # back/next-links business +||||||| parent of 56bfa42 (Most variables don't need the `$` in `$((shell math))`) + if (($i <= 0)) +======= +>>>>>>> 56bfa42 (Most variables don't need the `$` in `$((shell math))`) if ((i <= 0)) then prev_title=