diff --git a/part1.html b/part1.html index 6e321d2..fb32297 100644 --- a/part1.html +++ b/part1.html @@ -15,6 +15,7 @@ pre > code.sourceCode { white-space: pre; position: relative; } pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } pre > code.sourceCode > span:empty { height: 1.2em; } +.sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } div.sourceCode { margin: 1em 0; } pre.sourceCode { margin: 0; } @@ -227,29 +228,29 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni

1.3.1 Features

Here’s a showcase of some of the Haskell’s cool features:

Higher-order functions – functions can take functions as arguments:

-
map length ["abc","abcdef"]
+
map length ["abc","abcdef"]

This results in [3,6].

Anonymous functions aka lambdas – you can define single-use helper functions without giving them a name

-
filter (\x -> length x > 1) ["abc","d","ef"]
+
filter (\x -> length x > 1) ["abc","d","ef"]

This results in ["abc","ef"].

Partial application – you can define new functions by giving another function only some of the arguments it needs. For example this multiplies all elements in a list by 3:

-
map (*3) [1,2,3]
+
map (*3) [1,2,3]

Algebraic datatypes – a syntax for defining datatypes that can contain a number of different cases:

-
data Shape = Point | Rectangle Double Double | Circle Double
+
data Shape = Point | Rectangle Double Double | Circle Double

Now the type Shape can have values like Point, Rectangle 3 6 and Circle 5

Pattern matching – defining functions based on cases that correspond to your data definitions:

-
area Point = 0
-area (Rectangle width height) = width * height
-area (Circle radius) = 2 * pi * radius
+
area Point = 0
+area (Rectangle width height) = width * height
+area (Circle radius) = 2 * pi * radius

Lists – Unlike many languages, Haskell has a concise built-in syntax for lists. Lists can be built from other lists using list comprehensions. Here’s a snippet that generates names of even length from a set of options for first and last names:

-
[whole | first <- ["Eva", "Mike"],
-         last <- ["Smith", "Wood", "Odd"],
-         let whole = first ++ last,
-         even (length whole)]
+
[whole | first <- ["Eva", "Mike"],
+         last <- ["Smith", "Wood", "Odd"],
+         let whole = first ++ last,
+         even (length whole)]

This results in ["EvaSmith","EvaOdd","MikeWood"]. Thanks to the laziness of Haskell, we can even create so-called infinite lists:

-
primes = [ n | n <- [2..] , all (\k -> n `mod` k /= 0) [2..n `div` 2] ]
+
primes = [ n | n <- [2..] , all (\k -> n `mod` k /= 0) [2..n `div` 2] ]

The first ten prime numbers can be then obtained by evaluating

-
take 10 primes
+
take 10 primes

This evaluates to [2,3,5,7,11,13,17,19,23,29].

Parameterized types – you can define types that are parameterized by other types. For example [Int] is a list of Ints and [Bool] is a list of booleans. You can define typed functions that work on all kinds of lists, for example reverse has the type [a] -> [a] which means it takes a list containing any type a, and returns a list of the same type.

Type classes – another form of polymorphism where you can give a function a different implementation depending on the arguments’ types. For example the Show type class defines the function show that can convert values of various types to strings. The Num type class defines arithmetic operators like + that work on all number types (Int, Double, Complex, …).

@@ -484,122 +485,122 @@ Leaving GHCi.

Looks a bit weird, right? We’ll get back to this as well.

1.6.3 Note About Misleading Types

Sometimes, the types you see in GHCi are a bit different than what you’d assume. Here are two common cases.

-
Prelude> :t 1+1
-1+1 :: Num a => a
+
Prelude> :t 1+1
+1+1 :: Num a => a

For now, you should read the type Num a => a as “any number type”. In Haskell, number literals are overloaded which means that they can be interpreted as any number type (e.g. Int or Double). We’ll get back to what Num a actually means when we talk about type classes later.

-
Prelude> :t "asdf"
-"asdf" :: [Char]
+
Prelude> :t "asdf"
+"asdf" :: [Char]

The type String is just an alias for the type [Char] which means “list of characters”. We’ll get back to lists on the next lecture! In any case, you can use String and [Char] interchangeably, but GHCi will mostly use [Char] when describing types to you.

1.7 The Structure of a Haskell Program

Here’s a simple Haskell program that does some arithmetic and prints some values.

-
module Gold where
-
--- The golden ratio
-phi :: Double
-phi = (sqrt 5 + 1) / 2
-
-polynomial :: Double -> Double
-polynomial x = x^2 - x - 1
-
-f x = polynomial (polynomial x)
-
-main = do
-  print (polynomial phi)
-  print (f phi)
+
module Gold where
+
+-- The golden ratio
+phi :: Double
+phi = (sqrt 5 + 1) / 2
+
+polynomial :: Double -> Double
+polynomial x = x^2 - x - 1
+
+f x = polynomial (polynomial x)
+
+main = do
+  print (polynomial phi)
+  print (f phi)

If you put this in a file called Gold.hs and run it with (for example) stack runhaskell Gold.hs, you should see this output

0.0
 -1.0

Let’s walk through the file.

-
module Gold where
+
module Gold where

There is one Haskell module per source file. A module consists of definitions.

-
-- The golden ratio
+
-- The golden ratio

This is a comment. Comments are not part of the actual program, but text for human readers of the program.

-
phi :: Double
-phi = (sqrt 5 + 1) / 2
+
phi :: Double
+phi = (sqrt 5 + 1) / 2

This is a definition of the constant phi, with an accompanying type annotation (also known as a type signature) phi :: Double. The type annotation means that phi has type Double. The line with a equals sign (=) is called an equation. The left hand side of the = is the expression we are defining, and the right hand side of the = is the definition.

In general a definition (of a function or constant) consists of an optional type annotation and one or more equations

-
polynomial :: Double -> Double
-polynomial x = x^2 - x - 1
+
polynomial :: Double -> Double
+polynomial x = x^2 - x - 1

This is the definition of a function called polynomial. It has a type annotation and an equation. Note how an equation for a function differs from the equation of a constant by the presence of a parameter x left of the = sign. Note also that ^ is the power operator in Haskell, not bitwise xor like in many other languages.

-
f x = polynomial (polynomial x)
+
f x = polynomial (polynomial x)

This is the definition of a function called f. Note the lack of type annotation. What is the type of f?

-
main = do
-  print (polynomial phi)
-  print (f phi)
+
main = do
+  print (polynomial phi)
+  print (f phi)

This is a description of what happens when you run the program. It uses do-syntax and the IO Monad. We’ll get back to those in part 2 of the course.

1.8 Working with Examples

When you see an example definition like this

-
polynomial :: Double -> Double
-polynomial x = x^2 - x - 1
+
polynomial :: Double -> Double
+polynomial x = x^2 - x - 1

you should usually play around with it. Start by running it. There are a couple of ways to do this.

If a definition fits on one line, you can just define it in GHCi using let:

Prelude> let polynomial x = x^2 - x - 1
 Prelude> polynomial 3.0
 5.0

For a multi-line definition, you can either use ; to separate lines, or use the special :{ :} syntax to paste a block of code into GHCi:

-
Prelude> :{
-Prelude| polynomial :: Double -> Double
-Prelude| polynomial x = x^2 - x - 1
-Prelude| :}
-Prelude> polynomial 3.0
-5.0
+
Prelude> :{
+Prelude| polynomial :: Double -> Double
+Prelude| polynomial x = x^2 - x - 1
+Prelude| :}
+Prelude> polynomial 3.0
+5.0

Finally, you can paste the code into a new or existing .hs file, and then :load it into GHCi. If the file has already been loaded, you can also use :reload.

-
-- first copy and paste the definition into Example.hs, then run GHCi
-Prelude> :load Example.hs
-[1 of 1] Compiling Main             ( Example.hs, interpreted )
-Ok, one module loaded.
-*Main> polynomial 3.0
-5.0
--- now you can edit the definition
-*Main> :reload
-[1 of 1] Compiling Main             ( Example.hs, interpreted )
-Ok, one module loaded.
-*Main> polynomial 3
-3.0
+
-- first copy and paste the definition into Example.hs, then run GHCi
+Prelude> :load Example.hs
+[1 of 1] Compiling Main             ( Example.hs, interpreted )
+Ok, one module loaded.
+*Main> polynomial 3.0
+5.0
+-- now you can edit the definition
+*Main> :reload
+[1 of 1] Compiling Main             ( Example.hs, interpreted )
+Ok, one module loaded.
+*Main> polynomial 3
+3.0

After you’ve run the example, try modifying it, or making another function that is similar but different. You learn programming by programming, not by reading!

1.8.1 Dealing with Errors

Since Haskell is a typed language, you’ll pretty quickly bump into type errors. Here’s an example of an error during a GHCi session:

-
Prelude> "string" ++ True
-
-<interactive>:1:13: error:
-    • Couldn't match expected type ‘[Char]’ with actual type ‘Bool’
-    • In the second argument of ‘(++)’, namely ‘True’
-      In the expression: "string" ++ True
-      In an equation for ‘it’: it = "string" ++ True
+
Prelude> "string" ++ True
+
+<interactive>:1:13: error:
+    • Couldn't match expected type ‘[Char]’ with actual type ‘Bool’
+    • In the second argument of ‘(++)’, namely ‘True’
+      In the expression: "string" ++ True
+      In an equation for ‘it’: it = "string" ++ True

This is the most common type error, “Couldn’t match expected type”. Even though the error looks long and scary, it’s pretty simple if you just read through it.

There are also others types of errors.

-
Prelude> True + 1
-
-<interactive>:6:1: error:
-    • No instance for (Num Bool) arising from a use of ‘+’
-    • In the expression: True + 1
-      In an equation for ‘it’: it = True + 1
+
Prelude> True + 1
+
+<interactive>:6:1: error:
+    • No instance for (Num Bool) arising from a use of ‘+’
+    • In the expression: True + 1
+      In an equation for ‘it’: it = True + 1

This is the kind of error you get when you try to use a numeric function like + on something that’s not a number.

The hardest error to track down is usually this:

-
Prelude> True +
-
-<interactive>:10:7: error:
-    parse error (possibly incorrect indentation or mismatched brackets)
+
Prelude> True +
+
+<interactive>:10:7: error:
+    parse error (possibly incorrect indentation or mismatched brackets)

There are many ways to cause it. Probably you’re missing some characters somewhere. We’ll get back to indentation later in this lecture.

1.8.2 Arithmetic

There’s one thing in Haskell arithmetic that often trips up beginners, and that’s division.

In Haskell there are two division functions, the / operator and the div function. The div function does integer division:

-
Prelude> 7 `div` 2
-3
+
Prelude> 7 `div` 2
+3

The / operator performs the usual division:

-
Prelude> 7.0 / 2.0
-3.5
+
Prelude> 7.0 / 2.0
+3.5

However, you can only use div on whole number types like Int and Integer, and you can only use / on decimal types like Double. Here’s an example of what happens if you try to mix them up:

-
halve :: Int -> Int
-halve x = x / 2
+
halve :: Int -> Int
+halve x = x / 2
error:
     • No instance for (Fractional Int) arising from a use of ‘/’
     • In the expression: x / 2
@@ -616,149 +617,149 @@ Prelude> polynomial 3.0
 

1.9.1 Conditionals

In other languages, if is a statement. It doesn’t have a value, it just conditionally executes other statements.

In Haskell, if is an expression. It has a value. It selects between two other expressions. It corresponds to the ?: operator in C or Java.

-
// Java
-int price = product.equals("milk") ? 1 : 2;
+
// Java
+int price = product.equals("milk") ? 1 : 2;

Python’s conditional expressions are quite close to haskell’s if:

-
# Python
-price = 1 if product == "milk" else 2
+
# Python
+price = 1 if product == "milk" else 2

This is how the same example looks in Haskell:

-
price = if product == "milk" then 1 else 2
+
price = if product == "milk" then 1 else 2

Because Haskell’s if returns a value, you always need an else!

1.9.1.1 Functions Returning Bool

In order to write if expressions, you need to know how to get values of type Bool. The most common way is comparisons. The usual ==, <, <=, > and >= operators work in Haskell. You can do ordered comparisons (<, >) on all sorts of numbers, and equality comparisons (==) on almost anything:

-
Prelude> "foo" == "bar"
-False
-Prelude> 5.0 <= 7.2
-True
-Prelude> 1 == 1
-True
+
Prelude> "foo" == "bar"
+False
+Prelude> 5.0 <= 7.2
+True
+Prelude> 1 == 1
+True

One oddity of Haskell is that the not-equals operator is written /= instead of the usual !=:

-
Prelude> 2 /= 3
-True
-Prelude> "bike" /= "bike"
-False
+
Prelude> 2 /= 3
+True
+Prelude> "bike" /= "bike"
+False

Remember that in addition to these comparisons, you can get Bool values out of other Bool values by using the && (“and”) and || (“or”) operators, and the not function.

1.9.1.2 Examples

-
checkPassword password = if password == "swordfish"
-                         then "You're in."
-                         else "ACCESS DENIED!"
-
absoluteValue n = if n < 0 then -n else n
-
login user password = if user == "unicorn73"
-                      then if password == "f4bulous!"
-                           then "unicorn73 logged in"
-                           else "wrong password"
-                      else "unknown user"
+
checkPassword password = if password == "swordfish"
+                         then "You're in."
+                         else "ACCESS DENIED!"
+
absoluteValue n = if n < 0 then -n else n
+
login user password = if user == "unicorn73"
+                      then if password == "f4bulous!"
+                           then "unicorn73 logged in"
+                           else "wrong password"
+                      else "unknown user"

1.9.2 Local Definitions

Haskell has two different ways for creating local definitions: let...in and where.

where adds local definitions to a definition:

-
circleArea :: Double -> Double
-circleArea r = pi * rsquare
-    where pi = 3.1415926
-          rsquare = r * r
+
circleArea :: Double -> Double
+circleArea r = pi * rsquare
+    where pi = 3.1415926
+          rsquare = r * r

let...in is an expression:

-
circleArea r = let pi = 3.1415926
-                   rsquare = r * r
-               in pi * rsquare
+
circleArea r = let pi = 3.1415926
+                   rsquare = r * r
+               in pi * rsquare

Local definitions can also be functions:

-
circleArea r = pi * square r
-    where pi = 3.1415926
-          square x = x * x
-
circleArea r = let pi = 3.1415926
-                   square x = x * x
-               in pi * square r
+
circleArea r = pi * square r
+    where pi = 3.1415926
+          square x = x * x
+
circleArea r = let pi = 3.1415926
+                   square x = x * x
+               in pi * square r

We’ll get back to the differences between let and where, but mostly you can use which ever you like.

1.9.3 A Word About Immutability

Even though things like pi above are often called variables, I’ve chosen to call them definitions here. This is because unlike variables in Python or Java, the values of these definitions can’t be changed. Haskell variables aren’t boxes into which you can put new values, Haskell variables name a value (or rather, an expression) and that’s it.

We’ll talk about immutability again later on this course, but for now it’s enough to know that things like this don’t work.

-
increment x = let x = x+1
-              in x
+
increment x = let x = x+1
+              in x

This is just an infinite loop, because it tries to define a new variable x with the property x = x+1. Thus when evaluating x, Haskell just keeps computing 1+1+1+1+... indefinitely.

-
compute x = let a = x+1
-                a = a*2
-            in a
+
compute x = let a = x+1
+                a = a*2
+            in a
error:
     Conflicting definitions for ‘a’
     Bound at: <interactive>:14:17
               <interactive>:15:17

Here we get a straightforward error when we’re trying to “update” the value of a.

As a remark, local definitions can shadow the names of variables defined elsewhere. Shadowing is not a side-effect. Instead, shadowing creates a new variable within a more restricted scope that uses the same name as some variable in the outer scope. For example, all of the functions f, g, and h below are legal:

-
x :: Int
-x = 5
-
-f :: Int -> Int
-f x = 2 * x
-
-g :: Int -> Int
-g y = x where x = 6
-
-h :: Int -> Int
-h x = x where x = 3
+
x :: Int
+x = 5
+
+f :: Int -> Int
+f x = 2 * x
+
+g :: Int -> Int
+g y = x where x = 6
+
+h :: Int -> Int
+h x = x where x = 3

If we apply them to the global constant x, we see the effects of shadowing:

-
f 1 ==> 2
-g 1 ==> 6
-h 1 ==> 3
-
-f x ==> 10
-g x ==> 6
-h x ==> 3
+
f 1 ==> 2
+g 1 ==> 6
+h 1 ==> 3
+
+f x ==> 10
+g x ==> 6
+h x ==> 3

It is best to always choose new names for local variables, so that shadowing never happens. That way, the reader of the code will understand where the variables that are used in an expression come from. Note that in the following example, f and g don’t shadow each others’ arguments:

-
f :: Int -> Int
-f x = 2 * x + 1
-
-g :: Int -> Int
-g x = x - 2
+
f :: Int -> Int
+f x = 2 * x + 1
+
+g :: Int -> Int
+g x = x - 2

1.9.4 Pattern Matching

A definition (of a function) can consist of multiple equations. The equations are matched in order against the arguments until a suitable one is found. This is called pattern matching.

Pattern matching in Haskell is very powerful, and we’ll keep learning new things about it along this course, but here are a couple of first examples:

-
greet :: String -> String -> String
-greet "Finland" name = "Hei, " ++ name
-greet "Italy"   name = "Ciao, " ++ name
-greet "England" name = "How do you do, " ++ name
-greet _         name = "Hello, " ++ name
+
greet :: String -> String -> String
+greet "Finland" name = "Hei, " ++ name
+greet "Italy"   name = "Ciao, " ++ name
+greet "England" name = "How do you do, " ++ name
+greet _         name = "Hello, " ++ name

The function greet generates a greeting given a country and a name (both Strings). It has special cases for three countries, and a default case. This is how it works:

-
Prelude> greet "Finland" "Pekka"
-"Hei, Pekka"
-Prelude> greet "England" "Bob"
-"How do you do, Bob"
-Prelude> greet "Italy" "Maria"
-"Ciao, Maria"
-Prelude> greet "Greenland" "Jan"
-"Hello, Jan"
+
Prelude> greet "Finland" "Pekka"
+"Hei, Pekka"
+Prelude> greet "England" "Bob"
+"How do you do, Bob"
+Prelude> greet "Italy" "Maria"
+"Ciao, Maria"
+Prelude> greet "Greenland" "Jan"
+"Hello, Jan"

The special pattern _ matches anything. It’s usually used for default cases. Because patterns are matched in order, it’s important to (usually) put the _ case last. Consider:

-
brokenGreet _         name = "Hello, " ++ name
-brokenGreet "Finland" name = "Hei, " ++ name
+
brokenGreet _         name = "Hello, " ++ name
+brokenGreet "Finland" name = "Hei, " ++ name

Now the first case gets selected for all inputs.

-
Prelude> brokenGreet "Finland" "Varpu"
-"Hello, Varpu"
-Prelude> brokenGreet "Sweden" "Ole"
-"Hello, Ole"
+
Prelude> brokenGreet "Finland" "Varpu"
+"Hello, Varpu"
+Prelude> brokenGreet "Sweden" "Ole"
+"Hello, Ole"

GHC even gives you a warning about this code:

<interactive>:1:1: warning: [-Woverlapping-patterns]
     Pattern match is redundant
     In an equation for ‘brokenGreet’: brokenGreet "Finland" name = ...

Some more examples follow. But first let’s introduce the standard library function show that can turn (almost!) anything into a string:

-
Prelude> show True
-"True"
-Prelude> show 3
-"3"
+
Prelude> show True
+"True"
+Prelude> show 3
+"3"

So, here’s an example of a function with pattern matching and a default case that actually uses the value (instead of just ignoring it with _):

-
describe :: Integer -> String
-describe 0 = "zero"
-describe 1 = "one"
-describe 2 = "an even prime"
-describe n = "the number " ++ show n
+
describe :: Integer -> String
+describe 0 = "zero"
+describe 1 = "one"
+describe 2 = "an even prime"
+describe n = "the number " ++ show n

This is how it works:

-
Prelude> describe 0
-"zero"
-Prelude> describe 2
-"an even prime"
-Prelude> describe 7
-"the number 7"
+
Prelude> describe 0
+"zero"
+Prelude> describe 2
+"an even prime"
+Prelude> describe 7
+"the number 7"

You can even pattern match on multiple arguments. Again, the equations are tried in order. Here’s a reimplementation of the login function from earlier:

-
login :: String -> String -> String
-login "unicorn73" "f4bulous!" = "unicorn73 logged in"
-login "unicorn73" _           = "wrong password"
-login _           _           = "unknown user"
+
login :: String -> String -> String
+login "unicorn73" "f4bulous!" = "unicorn73 logged in"
+login "unicorn73" _           = "wrong password"
+login _           _           = "unknown user"

1.9.5 Recursion

In Haskell, all sorts of loops are implemented with recursion. Function calls are very efficient, so you don’t need to worry about performance. (We’ll talk about performance later).

Learning how to do simple things with recursion in Haskell will help you use recursion on more complex problems later. Recursion is also often a useful way for thinking about solving harder problems.

@@ -767,39 +768,39 @@ Prelude> polynomial 3.0

n! = n * (n-1) * … * 1

For example, 4! = 4*3*2*1 = 24. Well anyway, here’s the Haskell implementation of factorial:

-
factorial :: Int -> Int
-factorial 1 = 1
-factorial n = n * factorial (n-1)
+
factorial :: Int -> Int
+factorial 1 = 1
+factorial n = n * factorial (n-1)

This is how it works. We use ==> to mean “evaluates to”.

-
factorial 3
-  ==> 3 * factorial (3-1)
-  ==> 3 * factorial 2
-  ==> 3 * 2 * factorial 1
-  ==> 3 * 2 * 1
-  ==> 6
+
factorial 3
+  ==> 3 * factorial (3-1)
+  ==> 3 * factorial 2
+  ==> 3 * 2 * factorial 1
+  ==> 3 * 2 * 1
+  ==> 6

What happens when you evaluate factorial (-1)?

Here’s another example:

-
-- compute the sum 1^2+2^2+3^2+...+n^2
-squareSum 0 = 0
-squareSum n = n^2 + squareSum (n-1)
+
-- compute the sum 1^2+2^2+3^2+...+n^2
+squareSum 0 = 0
+squareSum n = n^2 + squareSum (n-1)

A function can call itself recursively multiple times. As an example let’s consider the Fibonacci sequence from mathematics. The Fibonacci sequence is a sequence of integers with the following definition.

The sequence starts with 1, 1. To get the next element of the sequence, sum the previous two elements of the sequence.

The first elements of the Fibonacci sequence are 1, 1, 2, 3, 5, 8, 13 and so on. Here’s a function fibonacci which computes the nth element in the Fibonacci sequence. Note how it mirrors the mathematical definition.

-
-- Fibonacci numbers, slow version
-fibonacci 1 = 1
-fibonacci 2 = 1
-fibonacci n = fibonacci (n-2) + fibonacci (n-1)
+
-- Fibonacci numbers, slow version
+fibonacci 1 = 1
+fibonacci 2 = 1
+fibonacci n = fibonacci (n-2) + fibonacci (n-1)

Here’s how fibonacci 5 evaluates:

-
fibonacci 5
-  ==> fibonacci 3                 + fibonacci 4
-  ==> (fibonacci 1 + fibonacci 2) + fibonacci 4
-  ==> (    1       +       1    ) + fibonacci 4
-  ==> (    1       +       1    ) + (fibonacci 2 + fibonacci 3)
-  ==> (    1       +       1    ) + (fibonacci 2 + (fibonacci 1 + fibonacci 2))
-  ==> (    1       +       1    ) + (    1       + (    1       +     1      ))
-  ==> 5
+
fibonacci 5
+  ==> fibonacci 3                 + fibonacci 4
+  ==> (fibonacci 1 + fibonacci 2) + fibonacci 4
+  ==> (    1       +       1    ) + fibonacci 4
+  ==> (    1       +       1    ) + (fibonacci 2 + fibonacci 3)
+  ==> (    1       +       1    ) + (fibonacci 2 + (fibonacci 1 + fibonacci 2))
+  ==> (    1       +       1    ) + (    1       + (    1       +     1      ))
+  ==> 5

Note how fibonacci 3 gets evaluated twice and fibonacci 2 three times. This is not the most efficient implementation of the fibonacci function. We’ll get back to this in the next lecture. Another way to think about the evaluation of the fibonacci function is to visualize it as a tree (we abbreviate fibonacci as fib):

@@ -855,34 +856,34 @@ fib 5 ==> | |
  • if the number is odd, triple it and add one
  • As an example, the Collatz sequence for 3 is: 3, 10, 5, 16, 8, 4, 2, 1, 4, 2, 1, 4, 2, 1 … As you can see, once the number reaches 1, it gets caught in a loop.

    -
    module Collatz where
    -
    --- one step of the Collatz sequence
    -step :: Integer -> Integer
    -step x = if even x then down else up
    -  where down = div x 2
    -        up = 3*x+1
    -
    --- collatz x computes how many steps it takes for the Collatz sequence
    --- to reach 1 when starting from x
    -collatz :: Integer -> Integer
    -collatz 1 = 0
    -collatz x = 1 + collatz (step x)
    -
    --- longest finds the number with the longest Collatz sequence for initial values
    --- between 0 and upperBound
    -longest :: Integer -> Integer
    -longest upperBound = longest' 0 0 upperBound
    -
    --- helper function for longest
    -longest' :: Integer -> Integer -> Integer -> Integer
    --- end of recursion, return longest length found
    -longest' number _ 0 = number
    --- recursion step: check if n has a longer Collatz sequence than the current known longest
    -longest' number maxlength n = if length > maxlength
    -               then longest' n length (n-1)
    -               else longest' number maxlength (n-1)
    -  where length = collatz n
    +
    module Collatz where
    +
    +-- one step of the Collatz sequence
    +step :: Integer -> Integer
    +step x = if even x then down else up
    +  where down = div x 2
    +        up = 3*x+1
    +
    +-- collatz x computes how many steps it takes for the Collatz sequence
    +-- to reach 1 when starting from x
    +collatz :: Integer -> Integer
    +collatz 1 = 0
    +collatz x = 1 + collatz (step x)
    +
    +-- longest finds the number with the longest Collatz sequence for initial values
    +-- between 0 and upperBound
    +longest :: Integer -> Integer
    +longest upperBound = longest' 0 0 upperBound
    +
    +-- helper function for longest
    +longest' :: Integer -> Integer -> Integer -> Integer
    +-- end of recursion, return longest length found
    +longest' number _ 0 = number
    +-- recursion step: check if n has a longer Collatz sequence than the current known longest
    +longest' number maxlength n = if length > maxlength
    +               then longest' n length (n-1)
    +               else longest' number maxlength (n-1)
    +  where length = collatz n

    We can load the program in GHCi and play with it.

    $ stack ghci
     GHCi, version 8.2.2: http://www.haskell.org/ghc/  :? for help
    @@ -919,48 +920,48 @@ Ok, one module loaded.
     

    While you can get away with using tabs, it is highly recommended to use spaces for all indenting.

    Some examples are in order.

    These all are ok:

    -
    i x = let y = x+x+x+x+x+x in div y 5
    -
    --- let and in are grouped together, an expression is split
    -j x = let y = x+x+x
    -              +x+x+x
    -      in div y 5
    -
    --- the definitions of a and b are grouped together
    -k = a + b
    -  where a = 1
    -        b = 1
    -
    -l = a + b
    -  where
    -    a = 1
    -    b = 1
    +
    i x = let y = x+x+x+x+x+x in div y 5
    +
    +-- let and in are grouped together, an expression is split
    +j x = let y = x+x+x
    +              +x+x+x
    +      in div y 5
    +
    +-- the definitions of a and b are grouped together
    +k = a + b
    +  where a = 1
    +        b = 1
    +
    +l = a + b
    +  where
    +    a = 1
    +    b = 1

    These are not ok:

    -
    -- indentation not increased even though expression split on many lines
    -i x = let y = x+x+x+x+x+x
    -in div y 5
    -
    --- indentation not increased even though expression is split
    -j x = let y = x+x+x
    -      +x+x+x
    -      in div y 5
    -
    --- grouped things are not aligned
    -k = a + b
    -  where a = 1
    -      b = 1
    -
    --- grouped things are not aligned
    -l = a + b
    -  where
    -    a = 1
    -     b = 1
    -
    --- where is part of the equation, so indentation needs to increase
    -l = a + b
    -where
    -  a = 1
    -  b = 1
    +
    -- indentation not increased even though expression split on many lines
    +i x = let y = x+x+x+x+x+x
    +in div y 5
    +
    +-- indentation not increased even though expression is split
    +j x = let y = x+x+x
    +      +x+x+x
    +      in div y 5
    +
    +-- grouped things are not aligned
    +k = a + b
    +  where a = 1
    +      b = 1
    +
    +-- grouped things are not aligned
    +l = a + b
    +  where
    +    a = 1
    +     b = 1
    +
    +-- where is part of the equation, so indentation needs to increase
    +l = a + b
    +where
    +  a = 1
    +  b = 1

    If you make a mistake with the indentation, you’ll typically get a parse error like this:

    Indent.hs:2:1: error: parse error on input ‘where’

    The error includes the line number, so just go over that line again. If you can’t seem to get indentation to work, try putting everything on just one long line at first.

    @@ -1056,11 +1057,11 @@ Because `...` is used for delimiting strings.

    1.13 Working on the Exercises

    The course materials, including exercises, are available in a Git repository on GitHub at https://github.com/moocfi/haskell-mooc. If you’re not familiar with Git, see GitHub’s instructions on cloning a repository.

    Once you’ve cloned the haskell-mooc repository, go into the exercises directory. To download and build dependencies needed for running the exercise tests (such as the correct version of GHC and various libraries), run following command in your terminal:

    -
    $ stack build
    +
    $ stack build

    Do note that the dependencies are multiple gigabytes and it will take a while for the command to finish.

    There are primarily two types of files in the exercises directory: exercise sets named SetNX.hs and accompanying test program for the exercises named SetNXTest.hs. Both are Haskell source files, but only the exercise file should to be edited when solving the exercises. Instructions to all individual exercises are embedded in the exercise file as comments.

    Use the tests file to check your answers. For example when you have solved some of the exercises in Set1.hs, run the following command:

    -
    $ stack runhaskell Set1Test.hs
    +
    $ stack runhaskell Set1Test.hs

    The output of the tests looks something like this:

    ===== EXERCISE 1
     +++++ Pass
    @@ -1120,8 +1121,8 @@ Ok, two modules loaded.
     

    Once you’re done with an exercise set, you can turn it in on the Submit page on the course pages. After that you can see the results of your submission on the Results page and your total score on the My status page.

    Note! You may turn in an exercise set as many times as you want.

    Note! If you don’t want to use Stack or can’t get it working, you should also be able to run the tests with Cabal like this:

    -
    $ cabal v2-build
    -$ cabal v2-exec runhaskell Set1Test.hs
    +
    $ cabal v2-build
    +$ cabal v2-exec runhaskell Set1Test.hs

    1.13.1 Model Solutions

    Once you’ve successfully completed all the exercises in a set, you can view the model solutions on the My status page. It’s useful to glance at the model solutions, they might show you a technique you’ve missed!

    1.14 Exercises

    @@ -1139,66 +1140,66 @@ Ok, two modules loaded.

    Often you’ll find you need helper variables in recursion to keep track of things. You can get them by defining a helper function with more arguments. Analogy: arguments of the helper function are variables you update in your loop.

    Here’s an example of how you would convert a loop (in Java or Python) into a recursive helper function in Haskell.

    Java:

    -
    public String repeatString(int n, String str) {
    -    String result = "";
    -    while (n>0) {
    -        result = result+str;
    -        n = n-1;
    -    }
    -    return result;
    -}
    +
    public String repeatString(int n, String str) {
    +    String result = "";
    +    while (n>0) {
    +        result = result+str;
    +        n = n-1;
    +    }
    +    return result;
    +}

    Python:

    -
    def repeatString(n, str):
    -    result = ""
    -    while n>0:
    -        result = result+str
    -        n = n-1
    -    return result
    +
    def repeatString(n, str):
    +    result = ""
    +    while n>0:
    +        result = result+str
    +        n = n-1
    +    return result

    Haskell:

    -
    repeatString n str = repeatHelper n str ""
    -
    -repeatHelper n str result = if (n==0)
    -                            then result
    -                            else repeatHelper (n-1) str (result++str)
    -
    Prelude> repeatString 3 "ABC"
    -"ABCABCABC"
    +
    repeatString n str = repeatHelper n str ""
    +
    +repeatHelper n str result = if (n==0)
    +                            then result
    +                            else repeatHelper (n-1) str (result++str)
    +
    Prelude> repeatString 3 "ABC"
    +"ABCABCABC"

    You might have noticed that the Java and Python implementations look a bit weird since they use while loops instead of for loops. This is because this way the conversion to Haskell is more straightforward.

    This can be made a bit tidier by using pattern matching instead of an if:

    -
    repeatString n str = repeatHelper n str ""
    -
    -repeatHelper 0 _   result = result
    -repeatHelper n str result = repeatHelper (n-1) str (result++str)
    +
    repeatString n str = repeatHelper n str ""
    +
    +repeatHelper 0 _   result = result
    +repeatHelper n str result = repeatHelper (n-1) str (result++str)

    Here’s another example with more variables: computing fibonacci numbers efficiently.

    Java:

    -
    public int fibonacci(int n) {
    -    int a = 0;
    -    int b = 1;
    -    while (n>1) {
    -        int c = a+b;
    -        a=b;
    -        b=c;
    -        n--;
    -    }
    -    return b;
    -}
    +
    public int fibonacci(int n) {
    +    int a = 0;
    +    int b = 1;
    +    while (n>1) {
    +        int c = a+b;
    +        a=b;
    +        b=c;
    +        n--;
    +    }
    +    return b;
    +}

    Python:

    -
    def fibonacci(n):
    -    a = 0
    -    b = 1
    -    while n>1:
    -        c = a+b
    -        a = b
    -        b = c
    -        n = n-1
    -    return b
    +
    def fibonacci(n):
    +    a = 0
    +    b = 1
    +    while n>1:
    +        c = a+b
    +        a = b
    +        b = c
    +        n = n-1
    +    return b

    Haskell:

    -
    -- fibonacci numbers, fast version
    -fibonacci :: Integer -> Integer
    -fibonacci n = fibonacci' 0 1 n
    -
    -fibonacci' :: Integer -> Integer -> Integer -> Integer
    -fibonacci' a b 1 = b
    -fibonacci' a b n = fibonacci' b (a+b) (n-1)
    +
    -- fibonacci numbers, fast version
    +fibonacci :: Integer -> Integer
    +fibonacci n = fibonacci' 0 1 n
    +
    +fibonacci' :: Integer -> Integer -> Integer -> Integer
    +fibonacci' a b 1 = b
    +fibonacci' a b n = fibonacci' b (a+b) (n-1)

    Take a while to study these and note how the Haskell recursion has the same format as the loop.

    Sidenote: Haskell programs often use the apostrophe to name helper functions and alternative versions of functions. Thus the name fibonacci' for the helper function above. Names like foo' are usually read foo prime (like in mathematics).

    I said earlier that this version of fibonacci is more efficient. Can you see why? The answer is that there are less recursive calls. The expression fibonacci' _ _ n calls fibonacci' _ _ (n-1) once, and this means that we can compute fibonacci' _ _ n in n steps.

    @@ -1206,116 +1207,116 @@ Ok, two modules loaded.

    2.2 Guards

    Before we move on to new types, let’s go over one more piece of Haskell syntax.

    The if then else is often a bit cumbersome, especially when you have multiple cases. An easier alternative is Haskell’s conditional definition or guarded definition. This is a bit like pattern matching in that you have multiple equations, but you can have arbitrary code deciding which equation to use. Guarded definitions look like this:

    -
    f x y z
    -  | condition1 = something
    -  | condition2 = other
    -  | otherwise  = somethingother
    +
    f x y z
    +  | condition1 = something
    +  | condition2 = other
    +  | otherwise  = somethingother

    A condition can be any expression of type Bool. The first condition that evaluates to True is chosen. The word otherwise is just an alias for True. It is used to mark the default case.

    2.2.1 Examples

    Here are some examples of using guards. First off, we have a function that describes the given number. Note how it is important to have the "Two" case before the "Even" case.

    -
    describe :: Int -> String
    -describe n
    -  | n==2      = "Two"
    -  | even n    = "Even"
    -  | n==3      = "Three"
    -  | n>100     = "Big!!"
    -  | otherwise = "The number "++show n
    +
    describe :: Int -> String
    +describe n
    +  | n==2      = "Two"
    +  | even n    = "Even"
    +  | n==3      = "Three"
    +  | n>100     = "Big!!"
    +  | otherwise = "The number "++show n

    Here is factorial, implemented with guards instead of pattern matching. Unlike the pattern-matching version, this one doesn’t loop forever with negative inputs.

    -
    factorial n
    -  | n<0       = -1
    -  | n==0      = 1
    -  | otherwise = n * factorial (n-1)
    +
    factorial n
    +  | n<0       = -1
    +  | n==0      = 1
    +  | otherwise = n * factorial (n-1)

    You can even combine guards with pattern matching. Here’s the implementation of a simple age guessing game:

    -
    guessAge :: String -> Int -> String
    -guessAge "Griselda" age
    -    | age < 47 = "Too low!"
    -    | age > 47 = "Too high!"
    -    | otherwise = "Correct!"
    -guessAge "Hansel" age
    -    | age < 12 = "Too low!"
    -    | age > 12 = "Too high!"
    -    | otherwise = "Correct!"
    -guessAge name age = "Wrong name!"
    -
    Prelude> guessAge "Griselda" 30
    -"Too low!"
    -Prelude> guessAge "Griselda" 60
    -"Too high!"
    -Prelude> guessAge "Griselda" 47
    -"Correct!"
    -Prelude> guessAge "Bob" 30
    -"Wrong name!"
    -Prelude> guessAge "Hansel" 10
    -"Too low!"
    +
    guessAge :: String -> Int -> String
    +guessAge "Griselda" age
    +    | age < 47 = "Too low!"
    +    | age > 47 = "Too high!"
    +    | otherwise = "Correct!"
    +guessAge "Hansel" age
    +    | age < 12 = "Too low!"
    +    | age > 12 = "Too high!"
    +    | otherwise = "Correct!"
    +guessAge name age = "Wrong name!"
    +
    Prelude> guessAge "Griselda" 30
    +"Too low!"
    +Prelude> guessAge "Griselda" 60
    +"Too high!"
    +Prelude> guessAge "Griselda" 47
    +"Correct!"
    +Prelude> guessAge "Bob" 30
    +"Wrong name!"
    +Prelude> guessAge "Hansel" 10
    +"Too low!"

    2.3 Lists

    So far we’ve always worked with single values like number or booleans. Strings contain multiple characters, but still in some sense a string is just one piece of information. In order to be able to do actual programming, we need to handle variable numbers of items. For this we need data structures.

    The basic datastructure in Haskell is the list. Lists are used to store multiple values of the same type (in other words, Haskell lists are homogeneous). This is what a list literal looks like:

    -
    [0,3,4,1+1]
    +
    [0,3,4,1+1]

    A list type is written as [Element], where Element is the type of the lists elements. Here are some more list expressions and their types:

    -
    [True,True,False] :: [Bool]
    -["Moi","Hei"] :: [String]
    -[] :: [a]                   -- more about this later
    -[[1,2],[3,4]] :: [[Int]]    -- a list of lists
    -[1..7] :: [Int]             -- range syntax, value [1,2,3,4,5,6,7]
    +
    [True,True,False] :: [Bool]
    +["Moi","Hei"] :: [String]
    +[] :: [a]                   -- more about this later
    +[[1,2],[3,4]] :: [[Int]]    -- a list of lists
    +[1..7] :: [Int]             -- range syntax, value [1,2,3,4,5,6,7]

    Haskell lists are implemented as singly-linked lists. We’ll return to this later.

    2.3.1 List Operations

    The Haskell standard library comes with lots of functions that operate on lists. Here are some of the most important ones, together with their types. We’ll get back to what [a] actually means in a second, but for now you can imagine it means “any list”.

    -
    head :: [a] -> a            -- returns the first element
    -tail :: [a] -> [a]          -- returns everything except the first element
    -init :: [a] -> [a]          -- returns everything except the last element
    -take :: Int -> [a] -> [a]   -- returns the n first elements
    -drop :: Int -> [a] -> [a]   -- returns everything except the n first elements
    -(++) :: [a] -> [a] -> [a]   -- lists are catenated with the ++ operator
    -(!!) :: [a] -> Int -> a     -- lists are indexed with the !! operator
    -reverse :: [a] -> [a]       -- reverse a list
    -null :: [a] -> Bool         -- is this list empty?
    -length :: [a] -> Int        -- the length of a list
    +
    head :: [a] -> a            -- returns the first element
    +tail :: [a] -> [a]          -- returns everything except the first element
    +init :: [a] -> [a]          -- returns everything except the last element
    +take :: Int -> [a] -> [a]   -- returns the n first elements
    +drop :: Int -> [a] -> [a]   -- returns everything except the n first elements
    +(++) :: [a] -> [a] -> [a]   -- lists are catenated with the ++ operator
    +(!!) :: [a] -> Int -> a     -- lists are indexed with the !! operator
    +reverse :: [a] -> [a]       -- reverse a list
    +null :: [a] -> Bool         -- is this list empty?
    +length :: [a] -> Int        -- the length of a list

    Sidenote: the last two operations (null and length) actually have more generic types, but here I’m pretending that you can only use them on lists.

    Lists can be compared with the familiar == operator.

    Do you remember this from our first GHCi session?

    -
    Prelude> :t "asdf"
    -"asdf" :: [Char]
    +
    Prelude> :t "asdf"
    +"asdf" :: [Char]

    This means that String is just an alias for [Char], which means string is a list of characters. This means you can use all list operations on strings!

    Some list operations come from the module Data.List. You can import a module in code or in GHCi with the import Data.List syntax. One example is the sort function which sorts a list:

    -
    Prelude> import Data.List
    -Prelude Data.List> sort [1,0,5,3]
    -[0,1,3,5]
    +
    Prelude> import Data.List
    +Prelude Data.List> sort [1,0,5,3]
    +[0,1,3,5]

    Note how the set of imported modules shows up in the GHCi prompt.

    2.3.2 Examples

    Here are some examples of working with lists. In this case, instead of showing you output from GHCi, I merely use ==> to show what an expression evaluates to.

    Indexing a list:

    -
    [7,10,4,5] !! 2
    -  ==> 4
    +
    [7,10,4,5] !! 2
    +  ==> 4

    Defining a function that discards the 3rd and 4th elements of a list using take and drop:

    -
    f xs = take 2 xs ++ drop 4 xs
    -
    f [1,2,3,4,5,6]  ==>  [1,2,5,6]
    -f [1,2,3]        ==>  [1,2]
    +
    f xs = take 2 xs ++ drop 4 xs
    +
    f [1,2,3,4,5,6]  ==>  [1,2,5,6]
    +f [1,2,3]        ==>  [1,2]

    Rotating a list by taking the first element and moving it to the end:

    -
    g xs = tail xs ++ [head xs]
    -
    g [1,2,3]      ==>  [2,3,1]
    -g (g [1,2,3])  ==>  [3,1,2]
    +
    g xs = tail xs ++ [head xs]
    +
    g [1,2,3]      ==>  [2,3,1]
    +g (g [1,2,3])  ==>  [3,1,2]

    Here’s an example of the range syntax:

    -
    reverse [1..4] ==> [4,3,2,1]
    +
    reverse [1..4] ==> [4,3,2,1]

    2.4 A Word About Immutability

    Because Haskell is pure, it also means that functions can’t mutate (change) their inputs. Mutation is a side effect, and Haskell functions are only allowed output via their return value. This means that Haskell list functions always return a new list. In practice:

    -
    Prelude> let list = [1,2,3,4]
    -Prelude> reverse list
    -[4,3,2,1]
    -Prelude> list
    -[1,2,3,4]
    -Prelude> drop 2 list
    -[3,4]
    -Prelude> list
    -[1,2,3,4]
    +
    Prelude> let list = [1,2,3,4]
    +Prelude> reverse list
    +[4,3,2,1]
    +Prelude> list
    +[1,2,3,4]
    +Prelude> drop 2 list
    +[3,4]
    +Prelude> list
    +[1,2,3,4]

    This might seem very inefficient but it turns out it can be both performant and quite useful. We’ll get back to how Haskell datastructures work in a later lecture.

    2.5 A Word About Type Inference and Polymorphism

    So what does a type like head :: [a] -> a mean? It means given a list that contains elements of any type a, the return value will be of the same type a.

    In this type, a is a type variable. Type variables are types that start with a small letter, e.g. a, b, thisIsATypeVariable. A type variable means a type that is not yet known, or in other words a type that could be anything. Type variables can turn into concrete types (e.g. Bool) by the process of type inference (also called unification).

    Let’s have a look at some examples. If we apply head to a list of booleans, type inference will compare the type of head’s argument, [a], with the type of the actual argument, [Bool] and deduce that a must be Bool. This means that the return type of head will in this case also be Bool.

    -
    head :: [a] -> a
    -head [True,False] :: Bool
    +
    head :: [a] -> a
    +head [True,False] :: Bool

    The function tail takes a list, and returns a list of the same type. If we apply tail to a list of booleans, the return value will also be a list of booleans.

    -
    tail :: [a] -> [a]
    -tail [True,False] :: [Bool]
    +
    tail :: [a] -> [a]
    +tail [True,False] :: [Bool]

    If types don’t match, we get a type error. Consider the operator ++ which takes two lists of the same type, as we can see from its type [a] -> [a] -> [a]. If we try to apply ++ to a list of booleans and a list of characters we get an error. This is what happens in GHCi:

    Prelude> [True,False] ++ "Moi"
     
    @@ -1326,14 +1327,14 @@ Ok, two modules loaded.
         In the second argument of `(++)', namely `"Moi"'
         In the expression: [True, False] ++ "Moi"

    Type inference is really powerful. It uses the simple process of unification to get us types for practically any Haskell expression. Consider these two functions:

    -
    f xs ys = [head xs, head ys]
    -g zs = f "Moi" zs
    +
    f xs ys = [head xs, head ys]
    +g zs = f "Moi" zs

    We can ask GHCi for their types, and we will see that type inference has figured out that the two arguments to f must have the same type, since their heads get put into the same list.

    -
    Prelude> :t f
    -f :: [a] -> [a] -> [a]
    +
    Prelude> :t f
    +f :: [a] -> [a] -> [a]

    The function g, which fixed one of the arguments of f to a string (i.e. [Char]) gets a narrower type. Type inference has decided that the argument zs to g must also have type [Char], since otherwise the type of f would not match the call to f.

    -
    Prelude> :t g
    -g :: [Char] -> [Char]
    +
    Prelude> :t g
    +g :: [Char] -> [Char]

    2.5.1 Sidenote: Some Terminology

    In a type like [Char] we call Char a type parameter. A type like the list type that needs a type parameter is called a parameterized type.

    The fact that a function like head can be used with many different types of arguments is called polymorphism. The head function is said to be polymorphic. There are many forms of polymorphism, and this Haskell form that uses type variables is called parametric polymorphism.

    @@ -1373,41 +1374,41 @@ Ok, two modules loaded.

    You can think of Maybe a as being a bit like [a] except there can only be 0 or 1 elements, not more. Alternatively, you can think of Maybe a introducing a null value to the type a. If you’re familiar with Java, Maybe Integer is the Haskell equivalent of Java’s Optional<Integer>.

    You can create Maybe values by either specifying Nothing or Just someOtherValue:

    -
    Prelude> :t Nothing
    -Nothing :: Maybe a
    -Prelude> Just "a camel"
    -Just "a camel"
    -Prelude> :t Just "a camel"
    -Just "a camel" :: Maybe [Char]   -- the same as Maybe String
    -Prelude> Just True
    -Just True
    -Prelude> :t Just True
    -Just True :: Maybe Bool
    -
    -- given a password, return (Just username) if login succeeds, Nothing otherwise
    -login :: String -> Maybe String
    -login "f4bulous!" = Just "unicorn73"
    -login "swordfish" = Just "megahacker"
    -login _           = Nothing
    +
    Prelude> :t Nothing
    +Nothing :: Maybe a
    +Prelude> Just "a camel"
    +Just "a camel"
    +Prelude> :t Just "a camel"
    +Just "a camel" :: Maybe [Char]   -- the same as Maybe String
    +Prelude> Just True
    +Just True
    +Prelude> :t Just True
    +Just True :: Maybe Bool
    +
    -- given a password, return (Just username) if login succeeds, Nothing otherwise
    +login :: String -> Maybe String
    +login "f4bulous!" = Just "unicorn73"
    +login "swordfish" = Just "megahacker"
    +login _           = Nothing

    You use a Maybe value by pattern matching on it. Usually you define patterns for the Nothing and Just something cases. Some examples:

    -
    -- Multiply an Int with a Maybe Int. Nothing is treated as no multiplication at all.
    -perhapsMultiply :: Int -> Maybe Int -> Int
    -perhapsMultiply i Nothing = i
    -perhapsMultiply i (Just j) = i*j   -- Note how j denotes the value inside the Just
    -
    Prelude> perhapsMultiply 3 Nothing
    -3
    -Prelude> perhapsMultiply 3 (Just 2)
    -6
    -
    intOrZero :: Maybe Int -> Int
    -intOrZero Nothing = 0
    -intOrZero (Just i) = i
    -
    -safeHead :: [a] -> Maybe a
    -safeHead xs = if null xs then Nothing else Just (head xs)
    -
    -headOrZero :: [Int] -> Int
    -headOrZero xs = intOrZero (safeHead xs)
    -
    headOrZero []  ==> intOrZero (safeHead [])  ==> intOrZero Nothing  ==> 0
    -headOrZero [1] ==> intOrZero (safeHead [1]) ==> intOrZero (Just 1) ==> 1
    +
    -- Multiply an Int with a Maybe Int. Nothing is treated as no multiplication at all.
    +perhapsMultiply :: Int -> Maybe Int -> Int
    +perhapsMultiply i Nothing = i
    +perhapsMultiply i (Just j) = i*j   -- Note how j denotes the value inside the Just
    +
    Prelude> perhapsMultiply 3 Nothing
    +3
    +Prelude> perhapsMultiply 3 (Just 2)
    +6
    +
    intOrZero :: Maybe Int -> Int
    +intOrZero Nothing = 0
    +intOrZero (Just i) = i
    +
    +safeHead :: [a] -> Maybe a
    +safeHead xs = if null xs then Nothing else Just (head xs)
    +
    +headOrZero :: [Int] -> Int
    +headOrZero xs = intOrZero (safeHead xs)
    +
    headOrZero []  ==> intOrZero (safeHead [])  ==> intOrZero Nothing  ==> 0
    +headOrZero [1] ==> intOrZero (safeHead [1]) ==> intOrZero (Just 1) ==> 1

    2.7 Sidenote: Constructors

    As you can see above, we can pattern match on the constructors of Maybe: Just and Nothing. We’ll get back to what constructors mean later. For now it’s enough to note that constructors are special values that start with a capital letter that you can pattern match on.

    Other constructors that we’ve already seen include the constructors of Bool – True and False. We’ll introduce the constructors of the list type on the next lecture.

    @@ -1439,69 +1440,69 @@ Just :: a -> Maybe a

    Here’s a simple example: a readInt function that only knows a couple of numbers and returns a descriptive error for the rest. Note the Haskell convention of using Left for errors and Right for success.

    -
    readInt :: String -> Either String Int
    -readInt "0" = Right 0
    -readInt "1" = Right 1
    -readInt s = Left ("Unsupported string: " ++ s)
    +
    readInt :: String -> Either String Int
    +readInt "0" = Right 0
    +readInt "1" = Right 1
    +readInt s = Left ("Unsupported string: " ++ s)

    Sidenote: the constructors of Either are called Left and Right because they refer to the left and right type arguments of Either. Note how in Either a b, a is the left argument and b is the right argument. Thus Left contains a value of type a and likewise Right of type b. The convention of using Right for success is probably simply because right also means correct. No offense is intended to left-handed people.

    Here’s another example: pattern matching an Either. Just like with Maybe, there are two patterns for an Either, one for each constructor.

    -
    iWantAString :: Either Int String -> String
    -iWantAString (Right str)   = str
    -iWantAString (Left number) = show number
    +
    iWantAString :: Either Int String -> String
    +iWantAString (Right str)   = str
    +iWantAString (Left number) = show number

    As you recall, Haskell lists can only contain elements of the same type. You can’t have a value like [1,"foo",2]. However, you can use a type like Either to represent lists that can contain two different types of values. For example we could track the number of people on a lecture, with a possibility of adding an explanation if a value is missing:

    lectureParticipants :: [Either String Int]
     lectureParticipants = [Right 10, Right 13, Left "easter vacation", Right 17, Left "lecturer was sick", Right 3]

    2.9 The case-of Expression

    We’ve seen pattern matching in function arguments, but there’s also a way to pattern match in an expression. It looks like this:

    -
    case <value> of <pattern> -> <expression>
    -                <pattern> -> <expression>
    +
    case <value> of <pattern> -> <expression>
    +                <pattern> -> <expression>

    As an example let’s rewrite the describe example from the first lecture using case:

    -
    describe :: Integer -> String
    -describe 0 = "zero"
    -describe 1 = "one"
    -describe 2 = "an even prime"
    -describe n = "the number " ++ show n
    -
    describe :: Integer -> String
    -describe n = case n of 0 -> "zero"
    -                       1 -> "one"
    -                       2 -> "an even prime"
    -                       n -> "the number " ++ show n
    +
    describe :: Integer -> String
    +describe 0 = "zero"
    +describe 1 = "one"
    +describe 2 = "an even prime"
    +describe n = "the number " ++ show n
    +
    describe :: Integer -> String
    +describe n = case n of 0 -> "zero"
    +                       1 -> "one"
    +                       2 -> "an even prime"
    +                       n -> "the number " ++ show n

    A more interesting example is when the value we’re pattern matching on is not a function argument. For example:

    -
    -- parse country code into country name, returns Nothing if code not recognized
    -parseCountry :: String -> Maybe String
    -parseCountry "FI" = Just "Finland"
    -parseCountry "SE" = Just "Sweden"
    -parseCountry _ = Nothing
    -
    -flyTo :: String -> String
    -flyTo countryCode = case parseCountry countryCode of Just country -> "You're flying to " ++ country
    -                                                     Nothing -> "You're not flying anywhere"
    -
    Prelude> flyTo "FI"
    -"You're flying to Finland"
    -Prelude> flyTo "DE"
    -"You're not flying anywhere"
    +
    -- parse country code into country name, returns Nothing if code not recognized
    +parseCountry :: String -> Maybe String
    +parseCountry "FI" = Just "Finland"
    +parseCountry "SE" = Just "Sweden"
    +parseCountry _ = Nothing
    +
    +flyTo :: String -> String
    +flyTo countryCode = case parseCountry countryCode of Just country -> "You're flying to " ++ country
    +                                                     Nothing -> "You're not flying anywhere"
    +
    Prelude> flyTo "FI"
    +"You're flying to Finland"
    +Prelude> flyTo "DE"
    +"You're not flying anywhere"

    We could write the flyTo function using a helper function for pattern matching instead of using the case-of expression:

    -
    flyTo :: String -> String
    -flyTo countryCode = handleResult (parseCountry countryCode)
    -  where handleResult (Just country) = "You're flying to " ++ country
    -        handleResult Nothing        = "You're not flying anywhere"
    +
    flyTo :: String -> String
    +flyTo countryCode = handleResult (parseCountry countryCode)
    +  where handleResult (Just country) = "You're flying to " ++ country
    +        handleResult Nothing        = "You're not flying anywhere"

    In fact, a case-of expression can always be replaced with a helper function. Here’s one more example, written in both ways:

    -
    -- given a sentence, decide whether it is a statement, question or exclamation
    -sentenceType :: String -> String
    -sentenceType sentence = case last sentence of '.' -> "statement"
    -                                              '?' -> "question"
    -                                              '!' -> "exclamation"
    -                                              _   -> "not a sentence"
    -
    -- same function, helper function instead of case-of
    -sentenceType sentence = classify (last sentence)
    -  where classify '.' = "statement"
    -        classify '?' = "question"
    -        classify '!' = "exclamation"
    -        classify _   = "not a sentence"
    -
    Prelude> sentenceType "This is Haskell."
    -"statement"
    -Prelude> sentenceType "This is Haskell!"
    -"exclamation"
    +
    -- given a sentence, decide whether it is a statement, question or exclamation
    +sentenceType :: String -> String
    +sentenceType sentence = case last sentence of '.' -> "statement"
    +                                              '?' -> "question"
    +                                              '!' -> "exclamation"
    +                                              _   -> "not a sentence"
    +
    -- same function, helper function instead of case-of
    +sentenceType sentence = classify (last sentence)
    +  where classify '.' = "statement"
    +        classify '?' = "question"
    +        classify '!' = "exclamation"
    +        classify _   = "not a sentence"
    +
    Prelude> sentenceType "This is Haskell."
    +"statement"
    +Prelude> sentenceType "This is Haskell!"
    +"exclamation"

    2.10 Recap: Pattern Matching

    Things you can use as patterns:

      @@ -1519,33 +1520,33 @@ lectureParticipants = [Right 10, Right 13, Left "easter vacation", Rig
      • Defining a function with equations:
      -
      f :: Bool -> Maybe Int -> Int
      -f False Nothing  = 1
      -f False _        = 2
      -f True  (Just i) = i
      -f True  Nothing  = 0
      +
      f :: Bool -> Maybe Int -> Int
      +f False Nothing  = 1
      +f False _        = 2
      +f True  (Just i) = i
      +f True  Nothing  = 0
      • In the case of expression:
      -
      case number of 0 -> "zero"
      -               1 -> "one"
      -               _ -> "not zero or one"
      +
      case number of 0 -> "zero"
      +               1 -> "one"
      +               _ -> "not zero or one"

      The only thing you really need pattern matching for is getting the value inside a Just, Left or Right constructor. Here are two more examples of this:

      -
      -- getElement (Just i) gets the ith element (counting from zero) of a list, getElement Nothing gets the last element
      -getElement :: Maybe Int -> [a] -> a
      -getElement (Just i) xs = xs !! i
      -getElement Nothing xs = last xs
      -
      Prelude> getElement Nothing "hurray!"
      -'!'
      -Prelude> getElement (Just 3) [5,6,7,8,9]
      -8
      -
      direction :: Either Int Int -> String
      -direction (Left i) = "you should go left " ++ show i ++ " meters!"
      -direction (Right i) = "you should go right " ++ show i ++ " meters!"
      -
      Prelude> direction (Left 3)
      -"you should go left 3 meters!"
      -Prelude> direction (Right 5)
      -"you should go right 5 meters!"
      +
      -- getElement (Just i) gets the ith element (counting from zero) of a list, getElement Nothing gets the last element
      +getElement :: Maybe Int -> [a] -> a
      +getElement (Just i) xs = xs !! i
      +getElement Nothing xs = last xs
      +
      Prelude> getElement Nothing "hurray!"
      +'!'
      +Prelude> getElement (Just 3) [5,6,7,8,9]
      +8
      +
      direction :: Either Int Int -> String
      +direction (Left i) = "you should go left " ++ show i ++ " meters!"
      +direction (Right i) = "you should go right " ++ show i ++ " meters!"
      +
      Prelude> direction (Left 3)
      +"you should go left 3 meters!"
      +Prelude> direction (Right 5)
      +"you should go right 5 meters!"

      Other uses (that we’ve seen so far!) of pattern matching can also be accomplished with the == operator. However, things like x==Nothing won’t work in all cases. We’ll find out why when we talk about type classes in lecture 4.

      2.11 Quiz

      How many values does f x = [x,x] return?

      @@ -1624,104 +1625,104 @@ Because Nothing is a constructor

      3.1 Functional Programming, at Last

      Now with lists and polymorphism in our toolbox, we can finally start to look at functional programming.

      In Haskell a function is a value, just like a number or a list is. Functions can be passed as parameters to other functions. Here’s a toy example. The function applyTo1 takes a function of type Int->Int, applies it to the number 1, and returns the result.

      -
      applyTo1 :: (Int -> Int) -> Int
      -applyTo1 f = f 1
      +
      applyTo1 :: (Int -> Int) -> Int
      +applyTo1 f = f 1

      Let’s define a simple function of type Int->Int and see applyTo1 in action.

      -
      addThree :: Int -> Int
      -addThree x = x + 3
      -
      applyTo1 addThree
      -  ==> addThree 1
      -  ==> 1 + 3
      -  ==> 4
      +
      addThree :: Int -> Int
      +addThree x = x + 3
      +
      applyTo1 addThree
      +  ==> addThree 1
      +  ==> 1 + 3
      +  ==> 4

      Let’s go back to the type annotation for applyTo1.

      -
      applyTo1 :: (Int -> Int) -> Int
      +
      applyTo1 :: (Int -> Int) -> Int

      The parentheses are needed because the type Int -> Int -> Int would be the type of a function taking two Int arguments. More on this later.

      Let’s look at a slightly more interesting example. This time we’ll implement a polymorphic function doTwice. Note how we can use it with various types of values and functions.

      -
      doTwice :: (a -> a) -> a -> a
      -doTwice f x = f (f x)
      -
      doTwice addThree 1
      -  ==> addThree (addThree 1)
      -  ==> 7
      -doTwice tail "abcd"
      -  ==> tail (tail "abcd")
      -  ==> "cd"
      -
      makeCool :: String -> String
      -makeCool str = "WOW " ++ str ++ "!"
      -
      doTwice makeCool "Haskell"
      -  ==> "WOW WOW Haskell!!"
      +
      doTwice :: (a -> a) -> a -> a
      +doTwice f x = f (f x)
      +
      doTwice addThree 1
      +  ==> addThree (addThree 1)
      +  ==> 7
      +doTwice tail "abcd"
      +  ==> tail (tail "abcd")
      +  ==> "cd"
      +
      makeCool :: String -> String
      +makeCool str = "WOW " ++ str ++ "!"
      +
      doTwice makeCool "Haskell"
      +  ==> "WOW WOW Haskell!!"

      3.1.1 Functional Programming on Lists

      That was a bit boring. Luckily there are many useful list functions that take functions as arguments. By the way, functions that take functions as arguments (or return functions) are often called higher-order functions.

      The most famous of these list-processing higher-order functions is map. It gives you a new list by applying the given function to all elements of a list.

      -
      map :: (a -> b) -> [a] -> [b]
      -
      map addThree [1,2,3]
      -  ==> [4,5,6]
      +
      map :: (a -> b) -> [a] -> [b]
      +
      map addThree [1,2,3]
      +  ==> [4,5,6]

      The partner in crime for map is filter. Instead of transforming all elements of a list, filter drops some elements of a list and keeps others. In other words, filter selects the elements from a list that fulfill a condition.

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

      Here’s an example: selecting the positive elements from a list

      -
      positive :: Int -> Bool
      -positive x = x>0
      -
      filter positive [0,1,-1,3,-3]
      -  ==> [1,3]
      +
      positive :: Int -> Bool
      +positive x = x>0
      +
      filter positive [0,1,-1,3,-3]
      +  ==> [1,3]

      Note how both the type signatures of map and filter use polymorphism. They work on all kinds of lists. The type of map even uses two type parameters! Here are some examples of type inference using map and filter.

      -
      onlyPositive xs = filter positive xs
      -mapBooleans f = map f [False,True]
      -
      Prelude> :t onlyPositive
      -onlyPositive :: [Int] -> [Int]
      -Prelude> :t mapBooleans
      -mapBooleans :: (Bool -> b) -> [b]
      -Prelude> :t mapBooleans not
      -mapBooleans not :: [Bool]
      +
      onlyPositive xs = filter positive xs
      +mapBooleans f = map f [False,True]
      +
      Prelude> :t onlyPositive
      +onlyPositive :: [Int] -> [Int]
      +Prelude> :t mapBooleans
      +mapBooleans :: (Bool -> b) -> [b]
      +Prelude> :t mapBooleans not
      +mapBooleans not :: [Bool]

      One more thing: remember how constructors were just functions? That means you can pass them as arguments to other functions!

      -
      wrapJust xs = map Just xs
      -
      Prelude> :t wrapJust
      -wrapJust :: [a] -> [Maybe a]
      -Prelude> wrapJust [1,2,3]
      -[Just 1,Just 2,Just 3]
      +
      wrapJust xs = map Just xs
      +
      Prelude> :t wrapJust
      +wrapJust :: [a] -> [Maybe a]
      +Prelude> wrapJust [1,2,3]
      +[Just 1,Just 2,Just 3]

      3.1.2 Examples of Functional Programming on Lists

      How many “palindrome numbers” are between 1 and n?

      -
      -- a predicate that checks if a string is a palindrome
      -palindrome :: String -> Bool
      -palindrome str = str == reverse str
      -
      --- palindromes n takes all numbers from 1 to n, converts them to strings using show, and keeps only palindromes
      -palindromes :: Int -> [String]
      -palindromes n = filter palindrome (map show [1..n])
      -
      palindrome "1331" ==> True
      -palindromes 150 ==>
      -  ["1","2","3","4","5","6","7","8","9",
      -   "11","22","33","44","55","66","77","88","99",
      -   "101","111","121","131","141"]
      -length (palindromes 9999) ==> 198
      +
      -- a predicate that checks if a string is a palindrome
      +palindrome :: String -> Bool
      +palindrome str = str == reverse str
      +
      +-- palindromes n takes all numbers from 1 to n, converts them to strings using show, and keeps only palindromes
      +palindromes :: Int -> [String]
      +palindromes n = filter palindrome (map show [1..n])
      +
      palindrome "1331" ==> True
      +palindromes 150 ==>
      +  ["1","2","3","4","5","6","7","8","9",
      +   "11","22","33","44","55","66","77","88","99",
      +   "101","111","121","131","141"]
      +length (palindromes 9999) ==> 198

      How many words in a string start with “a”? This uses the function words from the module Data.List that splits a string into words.

      -
      countAWords :: String -> Int
      -countAWords string = length (filter startsWithA (words string))
      -  where startsWithA s = head s == 'a'
      -
      countAWords "does anyone want an apple?"
      -  ==> 3
      +
      countAWords :: String -> Int
      +countAWords string = length (filter startsWithA (words string))
      +  where startsWithA s = head s == 'a'
      +
      countAWords "does anyone want an apple?"
      +  ==> 3

      The function tails from Data.List returns the list of all suffixes (“tails”) of a list. We can use tails for many string processing tasks. Here’s how tails works:

      -
      tails "echo"
      -  ==> ["echo","cho","ho","o",""]
      +
      tails "echo"
      +  ==> ["echo","cho","ho","o",""]

      Here’s an example where we find what characters come after a given character in a string. First of all, we use tails, map and take to get all substrings of a certain length:

      -
      substringsOfLength :: Int -> String -> [String]
      -substringsOfLength n string = map shorten (tails string)
      -  where shorten s = take n s
      -
      substringsOfLength 3 "hello"
      -  ==> ["hel","ell","llo","lo","o",""]
      +
      substringsOfLength :: Int -> String -> [String]
      +substringsOfLength n string = map shorten (tails string)
      +  where shorten s = take n s
      +
      substringsOfLength 3 "hello"
      +  ==> ["hel","ell","llo","lo","o",""]

      There’s some shorter substrings left at the end (can you see why?), but they’re fine for our purposes right now. Now that we have substringsOfLength, we can implement the function whatFollows c k s that finds all the occurrences of the character c in the string s, and outputs the k letters that come after these occurrences.

      -
      whatFollows :: Char -> Int -> String -> [String]
      -whatFollows c k string = map tail (filter match (substringsOfLength (k+1) string))
      -  where match sub = take 1 sub == [c]
      -
      whatFollows 'a' 2 "abracadabra"
      -  ==> ["br","ca","da","br",""]
      +
      whatFollows :: Char -> Int -> String -> [String]
      +whatFollows c k string = map tail (filter match (substringsOfLength (k+1) string))
      +  where match sub = take 1 sub == [c]
      +
      whatFollows 'a' 2 "abracadabra"
      +  ==> ["br","ca","da","br",""]

      3.2 Partial Application

      When using higher-order functions you can find yourself defining lots of small helper functions, like addThree or shorten in the previous examples. This is a bit of a chore in the long run, but luckily Haskell’s functions behave a bit weirdly…

      Let’s start in GHCi:

      -
      Prelude> let add a b = a+b
      -Prelude> add 1 5
      -6
      -Prelude> let addThree = add 3
      -Prelude> addThree 2
      -5
      +
      Prelude> let add a b = a+b
      +Prelude> add 1 5
      +6
      +Prelude> let addThree = add 3
      +Prelude> addThree 2
      +5

      So, we’ve defined add, a function of two arguments, and only given it one argument. The result is not a type error but a new function. The new function just stores (or remembers) the given argument, waits for another argument, and then gives both to add.

      Prelude> map addThree [1,2,3]
       [4,5,6]
      @@ -1729,210 +1730,210 @@ Prelude> map (add 3) [1,2,3]
       [4,5,6]

      Here we can see that we don’t even need to give a name to the function returned by add 3. We can just use it anywhere where a function of one argument is expected.

      This is called partial application. All functions in Haskell behave like this. Let’s have a closer look. Here’s a function that takes many arguments.

      -
      between :: Integer -> Integer -> Integer -> Bool
      -between lo high x = x < high && x > lo
      -
      Prelude> between 3 7 5
      -True
      -Prelude> between 3 6 8
      -False
      +
      between :: Integer -> Integer -> Integer -> Bool
      +between lo high x = x < high && x > lo
      +
      Prelude> between 3 7 5
      +True
      +Prelude> between 3 6 8
      +False

      We can give between less arguments and get back new functions, just like we saw with add:

      -
      Prelude> (between 1 5) 2
      -True
      -Prelude> let f = between 1 5 in f 2
      -True
      -Prelude> map (between 1 3) [1,2,3]
      -[False,True,False]
      +
      Prelude> (between 1 5) 2
      +True
      +Prelude> let f = between 1 5 in f 2
      +True
      +Prelude> map (between 1 3) [1,2,3]
      +[False,True,False]

      Look at the types of partially applying between. They behave neatly, with arguments disappearing one by one from the type as values are added to the expression.

      -
      Prelude> :t between
      -between :: Integer -> Integer -> Integer -> Bool
      -Prelude> :t between 1
      -between 1 :: Integer -> Integer -> Bool
      -Prelude> :t between 1 2
      -between 1 2 :: Integer -> Bool
      -Prelude> :t between 1 2 3
      -between 1 2 3 :: Bool
      +
      Prelude> :t between
      +between :: Integer -> Integer -> Integer -> Bool
      +Prelude> :t between 1
      +between 1 :: Integer -> Integer -> Bool
      +Prelude> :t between 1 2
      +between 1 2 :: Integer -> Bool
      +Prelude> :t between 1 2 3
      +between 1 2 3 :: Bool

      Actually, when we write a type like Integer -> Integer -> Integer -> Bool, it means Integer -> (Integer -> (Integer -> Bool)). That is, a multi-argument function is just a function that returns a function. Similarly, an expression like between 1 2 3 is the same as ((between 1) 2) 3, so passing multiple arguments to a function happens via multiple single-argument calls. Representing multi-argument functions like this is called currying (after the logician Haskell Curry). Currying is what makes partial application possible.

      Here’s another example of using partial application with map:

      -
      map (drop 1) ["Hello","World!"]
      -  ==> ["ello","orld!"]
      +
      map (drop 1) ["Hello","World!"]
      +  ==> ["ello","orld!"]

      In addition to normal functions, partial application also works with operators. With operators you can choose whether you apply the left or the right argument. (Partially applied operators are also called sections or operator sections). Some examples:

      -
      Prelude> map (*2) [1,2,3]
      -[2,4,6]
      -Prelude> map (2*) [1,2,3]
      -[2,4,6]
      -Prelude> map (1/) [1,2,3,4,5]
      -[1.0,0.5,0.3333333333333333,0.25,0.2]
      +
      Prelude> map (*2) [1,2,3]
      +[2,4,6]
      +Prelude> map (2*) [1,2,3]
      +[2,4,6]
      +Prelude> map (1/) [1,2,3,4,5]
      +[1.0,0.5,0.3333333333333333,0.25,0.2]

      3.3 Prefix and Infix Notations

      Normal Haskell operators are applied with prefix notation, which is just a fancy way to say that the function name comes before the arguments. In contrast, operators are applied with infix notation – the name of the function comes between the arguments.

      An infix operator can be converted into a prefix function by adding parentheses around it. For instance,

      -
      (+) 1 2 ==> 1 + 2 ==> 3
      +
      (+) 1 2 ==> 1 + 2 ==> 3

      This is useful especially when an operator needs to be passed as an argument to another function.

      As an example, the function zipWith takes two lists, a binary function, and joins the lists using the function. We can use zipWith (+) to sum two lists, element-by-element:

      -
      Prelude> :t zipWith
      -zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
      -Prelude> zipWith (+) [0,2,5] [1,3,3]
      -[1,5,8]
      +
      Prelude> :t zipWith
      +zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
      +Prelude> zipWith (+) [0,2,5] [1,3,3]
      +[1,5,8]

      Without the ability to turn an operator into a function, we’d have to use a helper function – such as add above.

      Note that omitting the parentheses leads into a type error:

      -
      Prelude> zipWith + [0,2,5,3] [1,3,3]
      -
      -<interactive>:1:11: error:
      -    • Couldn't match expected type ‘[Integer]
      -                                    -> (a -> b -> c) -> [a] -> [b] -> [c]’
      -                  with actual type ‘[Integer]’
      -    • The function ‘[0, 2, 5, 3]’ is applied to one argument,
      -      but its type ‘[Integer]’ has none
      -      In the second argument of ‘(+)’, namely ‘[0, 2, 5, 3] [1, 3, 3]’
      -      In the expression: zipWith + [0, 2, 5, 3] [1, 3, 3]
      -    • Relevant bindings include
      -        it :: (a -> b -> c) -> [a] -> [b] -> [c]
      -          (bound at <interactive>:1:1)
      +
      Prelude> zipWith + [0,2,5,3] [1,3,3]
      +
      +<interactive>:1:11: error:
      +    • Couldn't match expected type ‘[Integer]
      +                                    -> (a -> b -> c) -> [a] -> [b] -> [c]’
      +                  with actual type ‘[Integer]’
      +    • The function ‘[0, 2, 5, 3]’ is applied to one argument,
      +      but its type ‘[Integer]’ has none
      +      In the second argument of ‘(+)’, namely ‘[0, 2, 5, 3] [1, 3, 3]’
      +      In the expression: zipWith + [0, 2, 5, 3] [1, 3, 3]
      +    • Relevant bindings include
      +        it :: (a -> b -> c) -> [a] -> [b] -> [c]
      +          (bound at <interactive>:1:1)

      The reason for this weird-looking error is that GHCi got confused and thought that we were somehow trying to add zipWith and [0,2,5,3] [1,3,3] together. Logically, it deduced that [0,2,5,3] must be a function since it’s being applied to [1,3,3] (remember that functions bind tighter than operators).

      Unfortunately, error messages can sometimes be obscure, since the compiler cannot always know the “real” cause of the error (which is in this case was omitting the parentheses). Weird error messages are frustrating, but only the programmer knows what was the original intent behind the code.

      Another nice feature of Haskell is the syntax for applying a binary function as if it was an infix operator, by surrounding it with backticks (`). For example:

      -
      6 `div` 2 ==> div 6 2 ==> 3
      -(+1) `map` [1,2,3] ==> map (+1) [1,2,3] ==> [2,3,4]
      +
      6 `div` 2 ==> div 6 2 ==> 3
      +(+1) `map` [1,2,3] ==> map (+1) [1,2,3] ==> [2,3,4]

      3.4 Lambdas

      The last spanner we need in our functional programming toolbox is λ (lambda). Lambda expressions are anonymous functions. Consider a situation where you need a function only once, for example in an expression like

      -
      let big x = x>7 in filter big [1,10,100]
      +
      let big x = x>7 in filter big [1,10,100]

      A lambda expression allows us to write this directly, without defining a name (big) for the helper function:

      -
      filter (\x -> x>7) [1,10,100]
      +
      filter (\x -> x>7) [1,10,100]

      Here are some more examples in GHCi:

      -
      Prelude> (\x -> x*x) 3
      -9
      -Prelude> (\x -> reverse x == x) "ABBA"
      -True
      -Prelude> filter (\x -> reverse x == x) ["ABBA","ACDC","otto","lothar","anna"]
      -["ABBA","otto","anna"]
      -Prelude> (\x y -> x^2+y^2) 2 3           -- multiple arguments
      -13
      +
      Prelude> (\x -> x*x) 3
      +9
      +Prelude> (\x -> reverse x == x) "ABBA"
      +True
      +Prelude> filter (\x -> reverse x == x) ["ABBA","ACDC","otto","lothar","anna"]
      +["ABBA","otto","anna"]
      +Prelude> (\x y -> x^2+y^2) 2 3           -- multiple arguments
      +13

      The Haskell syntax for lambdas is a bit surprising. The backslash character (\) stands for the greek letter lambda (λ). The Haskell expression \x -> x+1 is trying to mimic the mathematical notation λx. x+1. Other languages use syntax like x => x+1 (JavaScript) or lambda x: x+1 (Python).

      Note! You never need to use a lambda expression. You can always instead define the function normally using let or where.

      By the way, lambda expressions are quite powerful constructs which have a deep theory of their own, known as Lambda calculus. Some even consider purely functional programming languages such as Haskell to be typed extensions of Lambda calculus with extra syntax.

      3.5 Sidenote: The . and $ Operators

      The two most common operators in Haskell codebases are probably . and $. They are useful when writing code that uses higher-order functions. The first of these, the . operator, is the function composition operator. Here’s its type

      -
      (.) :: (b -> c) -> (a -> b) -> a -> c
      +
      (.) :: (b -> c) -> (a -> b) -> a -> c

      And here’s what it does

      (f.g) x ==> f (g x)

      You can use function composition to build functions out of other functions, without mentioning any arguments. For example:

      -
      double x = 2*x
      -quadruple = double . double  -- computes 2*(2*x) == 4*x
      -f = quadruple . (+1)         -- computes 4*(x+1)
      -g = (+1) . quadruple         -- computes 4*x+1
      -third = head . tail . tail   -- fetches the third element of a list
      +
      double x = 2*x
      +quadruple = double . double  -- computes 2*(2*x) == 4*x
      +f = quadruple . (+1)         -- computes 4*(x+1)
      +g = (+1) . quadruple         -- computes 4*x+1
      +third = head . tail . tail   -- fetches the third element of a list

      We can also reimplement doTwice using (.). Note how we can use doTwice both as applied only to a function, or as applied to a function and a value.

      -
      doTwice :: (a -> a) -> a -> a
      -doTwice f = f . f
      -
      let ttail = doTwice tail
      -in ttail [1,2,3,4]
      -  ==> [3,4]
      -
      -(doTwice tail) [1,2,3,4] ==> [3,4]
      -
      -doTwice tail [1,2,3,4] ==> [3,4]
      +
      doTwice :: (a -> a) -> a -> a
      +doTwice f = f . f
      +
      let ttail = doTwice tail
      +in ttail [1,2,3,4]
      +  ==> [3,4]
      +
      +(doTwice tail) [1,2,3,4] ==> [3,4]
      +
      +doTwice tail [1,2,3,4] ==> [3,4]

      Often function composition is not used when defining a new function, but instead to avoid defining a helper function. For instance, consider the difference between these two expressions:

      -
      let notEmpty x = not (null x)
      -in filter notEmpty [[1,2,3],[],[4]]
      -  ==> [[1,2,3],[4]]
      -
      filter (not . null) [[1,2,3],[],[4]]
      -  ==> [[1,2,3],[4]]
      +
      let notEmpty x = not (null x)
      +in filter notEmpty [[1,2,3],[],[4]]
      +  ==> [[1,2,3],[4]]
      +
      filter (not . null) [[1,2,3],[],[4]]
      +  ==> [[1,2,3],[4]]

      The other operator, $ is more subtle. Let’s look at its type.

      -
      ($) :: (a -> b) -> a -> b
      +
      ($) :: (a -> b) -> a -> b

      It takes a function of type a -> b and a value of type a, and returns a value of type b. In other words, it’s a function application operator. The expression f $ x is the same as f x. This seems pretty useless, but it means that the $ operator can be used to eliminate parentheses! These expressions are the same:

      -
      head (reverse "abcd")
      -head $ reverse "abcd"
      +
      head (reverse "abcd")
      +head $ reverse "abcd"

      This isn’t that impressive when it’s used to eliminate one pair of parentheses, but together . and $ can eliminate lots of them! For example, we can rewrite

      -
      reverse (map head (map reverse (["Haskell","pro"] ++ ["dodo","lyric"])))
      +
      reverse (map head (map reverse (["Haskell","pro"] ++ ["dodo","lyric"])))

      as

      -
      (reverse . map head . map reverse) (["Haskell","pro"] ++ ["dodo","lyric"])
      +
      (reverse . map head . map reverse) (["Haskell","pro"] ++ ["dodo","lyric"])

      and then

      -
      reverse . map head . map reverse $ ["Haskell","pro"] ++ ["dodo","lyric"]
      +
      reverse . map head . map reverse $ ["Haskell","pro"] ++ ["dodo","lyric"]

      Sometimes the operators . and $ are useful as functions in their own right. For example, a list of functions can be applied to an argument using map and a section of $:

      -
      map ($"string") [reverse, take 2, drop 2]
      -  ==> [reverse $ "string", take 2 $ "string", drop 2 $ "string"]
      -  ==> [reverse "string", take 2 "string", drop 2 "string"]
      -  ==> ["gnirts", "st", "ring"]
      +
      map ($"string") [reverse, take 2, drop 2]
      +  ==> [reverse $ "string", take 2 $ "string", drop 2 $ "string"]
      +  ==> [reverse "string", take 2 "string", drop 2 "string"]
      +  ==> ["gnirts", "st", "ring"]

      If this seems complicated, don’t worry. You don’t need to use . and $ in your own code until you’re comfortable with them. However, you’ll bump into . and $ when reading Haskell examples and code on the internet, so it’s good to know about them. This article might also help.

      3.6 Example: Rewriting whatFollows

      Now, let’s rewrite the whatFollows example from earlier using the tools we just saw. Here’s the original version:

      -
      substringsOfLength :: Int -> String -> [String]
      -substringsOfLength n string = map shorten (tails string)
      -  where shorten s = take n s
      -
      -whatFollows :: Char -> Int -> String -> [String]
      -whatFollows c k string = map tail (filter match (substringsOfLength (k+1) string))
      -  where match sub = take 1 sub == [c]
      +
      substringsOfLength :: Int -> String -> [String]
      +substringsOfLength n string = map shorten (tails string)
      +  where shorten s = take n s
      +
      +whatFollows :: Char -> Int -> String -> [String]
      +whatFollows c k string = map tail (filter match (substringsOfLength (k+1) string))
      +  where match sub = take 1 sub == [c]

      To get started, let’s get rid of the helper function substringsOfLength and move all the code to whatFollows:

      -
      whatFollows c k string = map tail (filter match (map shorten (tails string)))
      -  where shorten s = take (k+1) s
      -        match sub = take 1 sub == [c]
      +
      whatFollows c k string = map tail (filter match (map shorten (tails string)))
      +  where shorten s = take (k+1) s
      +        match sub = take 1 sub == [c]

      Now let’s use partial application instead of defining shorten:

      -
      whatFollows c k string = map tail (filter match (map (take (k+1)) (tails string)))
      -  where match sub = take 1 sub == [c]
      +
      whatFollows c k string = map tail (filter match (map (take (k+1)) (tails string)))
      +  where match sub = take 1 sub == [c]

      Let’s use . and $ to eliminate some of those parentheses:

      -
      whatFollows c k string = map tail . filter match . map (take (k+1)) $ tails string
      -  where match sub = take 1 sub == [c]
      +
      whatFollows c k string = map tail . filter match . map (take (k+1)) $ tails string
      +  where match sub = take 1 sub == [c]

      We can also replace match with a lambda:

      -
      whatFollows c k string = map tail . filter (\sub -> take 1 sub == [c]) . map (take (k+1)) $ tails string
      +
      whatFollows c k string = map tail . filter (\sub -> take 1 sub == [c]) . map (take (k+1)) $ tails string

      Finally, we don’t need to mention the string parameter at all, since we can just express whatFollows as a composition of map, filter, map and tails:

      -
      whatFollows c k = map tail . filter (\sub -> take 1 sub == [c]) . map (take (k+1)) . tails
      +
      whatFollows c k = map tail . filter (\sub -> take 1 sub == [c]) . map (take (k+1)) . tails

      We can even go a bit further by rewriting the lambda using an operator section

      -
          \sub -> take 1 sub == [c]
      -=== \sub -> (==[c]) (take 1 sub)
      -=== \sub -> (==[c]) ((take 1) sub)
      -=== \sub -> ((==[c]) . (take 1)) sub
      -=== ((==[c]) . (take 1))
      -=== ((==[c]) . take 1)
      +
          \sub -> take 1 sub == [c]
      +=== \sub -> (==[c]) (take 1 sub)
      +=== \sub -> (==[c]) ((take 1) sub)
      +=== \sub -> ((==[c]) . (take 1)) sub
      +=== ((==[c]) . (take 1))
      +=== ((==[c]) . take 1)

      Now what we have left is:

      -
      whatFollows c k = map tail . filter ((==[c]) . take 1) . map (take (k+1)) . tails
      +
      whatFollows c k = map tail . filter ((==[c]) . take 1) . map (take (k+1)) . tails

      This is a somewhat extreme version of the function, but when used in moderation the techniques shown here can make code easier to read.

      3.7 More Functional List Wrangling Examples

      Here are some more examples of functional programming with lists. Let’s start by introducing a couple of new list functions:

      -
      takeWhile :: (a -> Bool) -> [a] -> [a]   -- take elements from a list as long as they satisfy a predicate
      -dropWhile :: (a -> Bool) -> [a] -> [a]   -- drop elements from a list as long as they satisfy a predicate
      -
      takeWhile even [2,4,1,2,3]   ==> [2,4]
      -dropWhile even [2,4,1,2,3]   ==> [1,2,3]
      +
      takeWhile :: (a -> Bool) -> [a] -> [a]   -- take elements from a list as long as they satisfy a predicate
      +dropWhile :: (a -> Bool) -> [a] -> [a]   -- drop elements from a list as long as they satisfy a predicate
      +
      takeWhile even [2,4,1,2,3]   ==> [2,4]
      +dropWhile even [2,4,1,2,3]   ==> [1,2,3]

      There’s also the function elem, which can be used to check if a list contains an element:

      -
      elem 3 [1,2,3]   ==> True
      -elem 4 [1,2,3]   ==> False
      +
      elem 3 [1,2,3]   ==> True
      +elem 4 [1,2,3]   ==> False

      Using these, we can implement a function findSubstring that finds the earliest and longest substring in a string that consist only of the given characters.

      -
      findSubstring :: String -> String -> String
      -findSubstring chars = takeWhile (\x -> elem x chars)
      -                      . dropWhile (\x -> not $ elem x chars)
      -
      findSubstring "a" "bbaabaaaab"              ==> "aa"
      -findSubstring "abcd" "xxxyyyzabaaxxabcd"    ==> "abaa"
      +
      findSubstring :: String -> String -> String
      +findSubstring chars = takeWhile (\x -> elem x chars)
      +                      . dropWhile (\x -> not $ elem x chars)
      +
      findSubstring "a" "bbaabaaaab"              ==> "aa"
      +findSubstring "abcd" "xxxyyyzabaaxxabcd"    ==> "abaa"

      The function zipWith lets you combine two lists element-by-element:

      -
      zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
      -
      zipWith (++) ["John","Mary"] ["Smith","Cooper"]
      -  ==> ["JohnSmith","MaryCooper"]
      -zipWith take [4,3] ["Hello","Warden"]
      -  ==> ["Hell","War"]
      +
      zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
      +
      zipWith (++) ["John","Mary"] ["Smith","Cooper"]
      +  ==> ["JohnSmith","MaryCooper"]
      +zipWith take [4,3] ["Hello","Warden"]
      +  ==> ["Hell","War"]

      Sometimes with higher-order functions it’s useful to have a function that does nothing. The function id :: a -> a is the identity function and just returns its argument.

      -
      id 3 ==> 3
      -map id [1,2,3] ==> [1,2,3]
      +
      id 3 ==> 3
      +map id [1,2,3] ==> [1,2,3]

      This seems a bit useless, but you can use it for example with filter or dropWhile:

      -
      filter id [True,False,True,True]  ==>  [True,True,True]
      -dropWhile id [True,True,False,True,False]  ==>  [False,True,False]
      +
      filter id [True,False,True,True]  ==>  [True,True,True]
      +dropWhile id [True,True,False,True,False]  ==>  [False,True,False]

      Another very simple but sometimes crucial function is the constant function, const :: a -> b -> a. It always returns its first argument:

      -
      const 3 True ==> 3
      -const 3 0    ==> 3
      +
      const 3 True ==> 3
      +const 3 0    ==> 3

      When partially applied it can be used when you need a function that always returns the same value:

      -
      map (const 5) [1,2,3,4] ==> [5,5,5,5]
      -filter (const True) [1,2,3,4] ==> [1,2,3,4]
      +
      map (const 5) [1,2,3,4] ==> [5,5,5,5]
      +filter (const True) [1,2,3,4] ==> [1,2,3,4]

      3.8 Lists and Recursion

      Here’s a new operator, :

      -
      Prelude> 1:[]
      -[1]
      -Prelude> 1:[2,3]
      -[1,2,3]
      -Prelude> tail (1:[2,3])
      -[2,3]
      -Prelude> head (1:[2,3])
      -1
      -Prelude> :t (:)
      -(:) :: a -> [a] -> [a]
      +
      Prelude> 1:[]
      +[1]
      +Prelude> 1:[2,3]
      +[1,2,3]
      +Prelude> tail (1:[2,3])
      +[2,3]
      +Prelude> head (1:[2,3])
      +1
      +Prelude> :t (:)
      +(:) :: a -> [a] -> [a]

      The : operator builds a list out of a head and a tail. In other words, x : xs is the same as [x] ++ xs. Why do we need an operator for this?

      Actually, : is the constructor for lists: it returns a new linked list node. The other list constructor is [], the empty list. All lists are built using : and []. The familiar [x,y,z] syntax is actually just a nicer way to write x:y:z:[], or even more explicitly, x:(y:(z:[])). In fact (++) is defined in terms of : and recursion in the standard library.

      Here’s a picture of how [1,2,3] is structured in memory:

      @@ -1950,155 +1951,155 @@ Prelude> map (add 3) [1,2,3] -->

      3.8.1 Building a List

      Using : we can define recursive functions that build lists. For example here’s a function that builds lists like [3,2,1]:

      -
      descend 0 = []
      -descend n = n : descend (n-1)
      -
      descend 4 ==> [4,3,2,1]
      +
      descend 0 = []
      +descend n = n : descend (n-1)
      +
      descend 4 ==> [4,3,2,1]

      Here’s a function that builds a list by iterating a function n times:

      -
      iterate f 0 x = [x]
      -iterate f n x = x : iterate f (n-1) (f x)
      -
      iterate (*2) 4 3 ==> [3,6,12,24,48]
      -
      -let xs = "terve"
      -in iterate tail (length xs) xs
      -  ==> ["terve","erve","rve","ve","e",""]
      +
      iterate f 0 x = [x]
      +iterate f n x = x : iterate f (n-1) (f x)
      +
      iterate (*2) 4 3 ==> [3,6,12,24,48]
      +
      +let xs = "terve"
      +in iterate tail (length xs) xs
      +  ==> ["terve","erve","rve","ve","e",""]

      Here’s a more complicated example: splitting a string into pieces at a given character:

      -
      split :: Char -> String -> [String]
      -split c [] = []
      -split c xs = start : split c (drop 1 rest)
      -  where start = takeWhile (/=c) xs
      -        rest = dropWhile (/=c) xs
      -
      split 'x' "fooxxbarxquux"   ==>   ["foo","","bar","quu"]
      +
      split :: Char -> String -> [String]
      +split c [] = []
      +split c xs = start : split c (drop 1 rest)
      +  where start = takeWhile (/=c) xs
      +        rest = dropWhile (/=c) xs
      +
      split 'x' "fooxxbarxquux"   ==>   ["foo","","bar","quu"]

      3.8.2 Pattern Matching for Lists

      Last lecture, it was said that constructors are things that can be pattern matched on. Above, it was divulged that the constructors for the list type are : and []. We can put one and one together and guess that we can pattern match on : and []. This is true! Here’s how you can define your own versions of head and tail using pattern matching:

      -
      myhead :: [Int] -> Int
      -myhead [] = -1
      -myhead (first:rest) = first
      -
      -mytail :: [Int] -> [Int]
      -mytail [] = []
      -mytail (first:rest) = rest
      +
      myhead :: [Int] -> Int
      +myhead [] = -1
      +myhead (first:rest) = first
      +
      +mytail :: [Int] -> [Int]
      +mytail [] = []
      +mytail (first:rest) = rest

      You can nest patterns. That is, you can pattern match more than one element from the start of a list. In this example, we use the pattern (a:b:_) which is the same as (a:(b:_)):

      -
      sumFirstTwo :: [Integer] -> Integer
      --- this equation gets used for lists of length at least two
      -sumFirstTwo (a:b:_) = a+b
      --- this equation gets used for all other lists (i.e. lists of length 0 or 1)
      -sumFirstTwo _       = 0
      -
      sumFirstTwo [1]      ==> 0
      -sumFirstTwo [1,2]    ==> 3
      -sumFirstTwo [1,2,4]  ==> 3
      +
      sumFirstTwo :: [Integer] -> Integer
      +-- this equation gets used for lists of length at least two
      +sumFirstTwo (a:b:_) = a+b
      +-- this equation gets used for all other lists (i.e. lists of length 0 or 1)
      +sumFirstTwo _       = 0
      +
      sumFirstTwo [1]      ==> 0
      +sumFirstTwo [1,2]    ==> 3
      +sumFirstTwo [1,2,4]  ==> 3

      Here’s an example that uses many different list patterns:

      -
      describeList :: [Int] -> String
      -describeList []         = "an empty list"
      -describeList (x:[])     = "a list with one element"
      -describeList (x:y:[])   = "a list with two elements"
      -describeList (x:y:z:xs) = "a list with at least three elements"
      -
      describeList [1,3]        ==> "a list with two elements"
      -describeList [1,2,3,4,5]  ==> "a list with at least three elements"
      +
      describeList :: [Int] -> String
      +describeList []         = "an empty list"
      +describeList (x:[])     = "a list with one element"
      +describeList (x:y:[])   = "a list with two elements"
      +describeList (x:y:z:xs) = "a list with at least three elements"
      +
      describeList [1,3]        ==> "a list with two elements"
      +describeList [1,2,3,4,5]  ==> "a list with at least three elements"

      List patterns that end with :[] can be typed out as list literals. That is, just like [1,2,3] is the same value as 1:2:3:[], the pattern [x,y] is the same as the pattern x:y:[]. Let’s rewrite that previous example.

      -
      describeList :: [Int] -> String
      -describeList []         = "an empty list"
      -describeList [x]        = "a list with exactly one element"
      -describeList [x,y]      = "a list with exactly two elements"
      -describeList (x:y:z:xs) = "a list with at least three elements"
      +
      describeList :: [Int] -> String
      +describeList []         = "an empty list"
      +describeList [x]        = "a list with exactly one element"
      +describeList [x,y]      = "a list with exactly two elements"
      +describeList (x:y:z:xs) = "a list with at least three elements"

      Another way we can nest patterns is pattern matching on the head while pattern matching on a list. For example this function checks if a list starts with 0:

      -
      startsWithZero :: [Integer] -> Bool
      -startsWithZero (0:xs) = True
      -startsWithZero (x:xs) = False
      -startsWithZero []     = False
      +
      startsWithZero :: [Integer] -> Bool
      +startsWithZero (0:xs) = True
      +startsWithZero (x:xs) = False
      +startsWithZero []     = False

      3.8.3 Consuming a List

      Using pattern matching and recursion, we can recursively process a whole list. Here’s how you sum all the numbers in a list:

      -
      sumNumbers :: [Int] -> Int
      -sumNumbers [] = 0
      -sumNumbers (x:xs) = x + sumNumbers xs
      +
      sumNumbers :: [Int] -> Int
      +sumNumbers [] = 0
      +sumNumbers (x:xs) = x + sumNumbers xs

      Here’s how you compute the largest number in a list, this time using a helper function.

      -
      myMaximum :: [Int] -> Int
      -myMaximum [] = 0       -- actually this should be some sort of error...
      -myMaximum (x:xs) = go x xs
      -  where go biggest [] = biggest
      -        go biggest (x:xs) = go (max biggest x) xs
      +
      myMaximum :: [Int] -> Int
      +myMaximum [] = 0       -- actually this should be some sort of error...
      +myMaximum (x:xs) = go x xs
      +  where go biggest [] = biggest
      +        go biggest (x:xs) = go (max biggest x) xs

      Note!, “go” is just a cute name for the helper function here. It’s not special syntax.

      It’s often convenient to use nested patterns while consuming a list. Here’s an example that counts how many Nothing values occur in a list of Maybes:

      -
      countNothings :: [Maybe a] -> Int
      -countNothings [] = 0
      -countNothings (Nothing : xs) = 1 + countNothings xs
      -countNothings (Just _  : xs) = countNothings xs
      -
      countNothings [Nothing,Just 1,Nothing]  ==>  2
      +
      countNothings :: [Maybe a] -> Int
      +countNothings [] = 0
      +countNothings (Nothing : xs) = 1 + countNothings xs
      +countNothings (Just _  : xs) = countNothings xs
      +
      countNothings [Nothing,Just 1,Nothing]  ==>  2

      3.8.4 Building and Consuming a List

      Now that we can build and consume lists, let’s do both of them at the same time. This function doubles all elements in a list.

      -
      doubleList :: [Int] -> [Int]
      -doubleList [] = []
      -doubleList (x:xs) = 2*x : doubleList xs
      +
      doubleList :: [Int] -> [Int]
      +doubleList [] = []
      +doubleList (x:xs) = 2*x : doubleList xs

      It evaluates like this:

      -
      doubleList [1,2,3]
      -=== doubleList (1:(2:(3:[])))
      -==> 2*1 : doubleList (2:(3:[]))
      -==> 2*1 : (2*2 : doubleList (3:[]))
      -==> 2*1 : (2*2 : (2*3 : doubleList []))
      -==> 2*1 : (2*2 : (2*3 : []))
      -=== [2*1, 2*2, 2*3]
      -==> [2,4,6]
      +
      doubleList [1,2,3]
      +=== doubleList (1:(2:(3:[])))
      +==> 2*1 : doubleList (2:(3:[]))
      +==> 2*1 : (2*2 : doubleList (3:[]))
      +==> 2*1 : (2*2 : (2*3 : doubleList []))
      +==> 2*1 : (2*2 : (2*3 : []))
      +=== [2*1, 2*2, 2*3]
      +==> [2,4,6]

      Once you know pattern matching for lists, it’s straightforward to define map and filter. Actually, let’s just look at the GHC standard library implementations. Here’s map:

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

      and here’s filter:

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

      (Note! Naming the argument _pred is a way to tell the reader of the code that this argument is unused. It could have been just _ as well.)

      3.8.5 Tail Recursion and Lists

      When a recursive function evaluates to a new call to that same function with different arguments, it is called tail-recursive. (The recursive call is said to be in tail position.) This is the type of recursion that corresponds to an imperative loop. We’ve already seen many examples of tail-recursive functions, but we haven’t really contrasted the two ways for writing the same function. This is sumNumbers from earlier in this lecture:

      -
      sumNumbers :: [Int] -> Int
      -sumNumbers [] = 0
      -sumNumbers (x:xs) = x + sumNumbers xs
      +
      sumNumbers :: [Int] -> Int
      +sumNumbers [] = 0
      +sumNumbers (x:xs) = x + sumNumbers xs

      In the second equation the function + is at the top level, i.e. in tail position. The recursive call to sumNumbers is an argument of +. This is sumNumbers written using a tail recursive helper function:

      -
      sumNumbers :: [Int] -> Int
      -sumNumbers xs = go 0 xs
      -  where go sum [] = sum
      -        go sum (x:xs) = go (sum+x) xs
      +
      sumNumbers :: [Int] -> Int
      +sumNumbers xs = go 0 xs
      +  where go sum [] = sum
      +        go sum (x:xs) = go (sum+x) xs

      Note the second equation of go: it has the recursive call to go at the top level, i.e. in tail position. The + is now in an argument to go.

      For a function like sumNumbers that produces a single value (a number), it doesn’t really matter which form of recursion you choose. The non-tail-recursive function is easier to read, while the tail-recursive one can be easier to come up with. You can try writing a function both ways. The tail-recursive form might be more efficient, but that depends on many details. We’ll talk more about Haskell performance in part 2 of this course.

      However, when you’re returning a list there is a big difference between these two forms. Consider the function doubleList from earlier. Here it is again, implemented first directly, and then via a tail-recursive helper function.

      -
      doubleList :: [Int] -> [Int]
      -doubleList [] = []
      -doubleList (x:xs) = 2*x : doubleList xs
      -
      doubleList :: [Int] -> [Int]
      -doubleList xs = go [] xs
      -    where go result [] = result
      -          go result (x:xs) = go (result++[2*x]) xs
      +
      doubleList :: [Int] -> [Int]
      +doubleList [] = []
      +doubleList (x:xs) = 2*x : doubleList xs
      +
      doubleList :: [Int] -> [Int]
      +doubleList xs = go [] xs
      +    where go result [] = result
      +          go result (x:xs) = go (result++[2*x]) xs

      Here the direct version is much more efficient. The (:) operator works in constant time, whereas the (++) operator needs to walk the whole list, needing linear time. Thus the direct version uses linear time (O(n)) with respect to the length of the list, while the tail-recursive version is quadratic (O(n²))!

      One might be tempted to fix this by using (:) in the tail-recursive version, but then the list would get generated in the reverse order. This could be fixed with an application of reverse, but that would make the resulting function quite complicated.

      There is another reason to prefer the direct version: laziness. We’ll get back to laziness in part 2 of the course, but for now it’s enough for you to know that the direct way of generating a list is simpler, more efficient and more idiomatic. You should try to practice it in the exercises. Check out the standard library implementations of map and filter above, even they produce the list directly without tail recursion!

      3.9 Something Fun: List Comprehensions

      Haskell has list comprehensions, a nice syntax for defining lists that combines the power of map and filter. You might be familiar with Python’s list comprehensions already. Haskell’s work pretty much the same way, but their syntax is a bit different.

      Mapping:

      -
      [2*i | i<-[1,2,3]]
      -  ==> [2,4,6]
      +
      [2*i | i<-[1,2,3]]
      +  ==> [2,4,6]

      Filtering:

      -
      [i | i <- [1..7], even i]
      -  ==> [2,4,6]
      +
      [i | i <- [1..7], even i]
      +  ==> [2,4,6]

      In general, these two forms are equivalent:

      -
      [f x | x <- lis, p x]
      -map f (filter p lis)
      +
      [f x | x <- lis, p x]
      +map f (filter p lis)

      List comprehensions can do even more. You can iterate over multiple lists:

      -
      [ first ++ " " ++ last | first <- ["John", "Mary"], last <- ["Smith","Cooper"] ]
      -  ==> ["John Smith","John Cooper","Mary Smith","Mary Cooper"]
      +
      [ first ++ " " ++ last | first <- ["John", "Mary"], last <- ["Smith","Cooper"] ]
      +  ==> ["John Smith","John Cooper","Mary Smith","Mary Cooper"]

      You can make local defitions:

      -
      [ reversed | word <- ["this","is","a","string"], let reversed = reverse word ]
      -  ==> ["siht","si","a","gnirts"]
      +
      [ reversed | word <- ["this","is","a","string"], let reversed = reverse word ]
      +  ==> ["siht","si","a","gnirts"]

      You can even do pattern matching in list comprehensions!

      -
      firstLetters string = [ char | (char:_) <- words string ]
      -
      firstLetters "Hello World!"
      -  ==> "HW"
      +
      firstLetters string = [ char | (char:_) <- words string ]
      +
      firstLetters "Hello World!"
      +  ==> "HW"

      3.10 Something Fun: Custom Operators

      In Haskell an operator is anything built from the characters !#$%&*+./<=>?@\^|-~. Operators can be defined just like functions (note the slightly different type annotation):

      -
      (<+>) :: [Int] -> [Int] -> [Int]
      -xs <+> ys = zipWith (+) xs ys
      -
      (+++) :: String -> String -> String
      -a +++ b = a ++ " " ++ b
      +
      (<+>) :: [Int] -> [Int] -> [Int]
      +xs <+> ys = zipWith (+) xs ys
      +
      (+++) :: String -> String -> String
      +a +++ b = a ++ " " ++ b

      3.11 Quiz

      What’s the type of this function? both p q x = p x && q x

        @@ -2249,7 +2250,7 @@ unspecified
      1. Set3b: list recursion exercises

    3.12.1 Common Errors

    -
    No instance for (Eq a) arising from a use of ‘==’
    +
    No instance for (Eq a) arising from a use of ‘==’

    You’ve probably tried to use x==Nothing to check if a value is Nothing. Use pattern matching instead. The reason for this error is that values of type Maybe a can’t be compared because Haskell doesn’t know how to compare values of the polymorphic type a. You’ll find more about this in the next lecture. Use pattern matching instead of == for now.

    4 Lecture 4: Real Classy

      @@ -2282,53 +2283,53 @@ unspecified

      To get values out of tuples, you can use the functions fst and snd:

      -
      fst :: (a, b) -> a
      -snd :: (a, b) -> b
      +
      fst :: (a, b) -> a
      +snd :: (a, b) -> b

      You can also pattern match on tuples. This is often the most convenient way, and also works for tuples of larger sizes. The fst and snd functions work only on pairs.

      Tuples are very useful in combination with lists. Here are some examples using the zip, unzip and partition functions from the Data.List module.

      -
      zip :: [a] -> [b] -> [(a, b)]    -- two lists to list of pairs
      -unzip :: [(a, b)] -> ([a], [b])  -- list of pairs to pair of lists
      -partition :: (a -> Bool) -> [a] -> ([a], [a])    -- elements that satisfy and don't satisfy a predicate
      +
      zip :: [a] -> [b] -> [(a, b)]    -- two lists to list of pairs
      +unzip :: [(a, b)] -> ([a], [b])  -- list of pairs to pair of lists
      +partition :: (a -> Bool) -> [a] -> ([a], [a])    -- elements that satisfy and don't satisfy a predicate
      -
      zip [1,2,3] [True,False,True]
      -  ==> [(1,True),(2,False),(3,True)]
      -unzip [("Fred",1), ("Jack",10), ("Helen",13)]
      -  ==> (["Fred","Jack","Helen"],[1,10,13])
      -partition (>0) [-1,1,-4,3,2,0]
      -  ==> ([1,3,2],[-1,-4,0])
      +
      zip [1,2,3] [True,False,True]
      +  ==> [(1,True),(2,False),(3,True)]
      +unzip [("Fred",1), ("Jack",10), ("Helen",13)]
      +  ==> (["Fred","Jack","Helen"],[1,10,13])
      +partition (>0) [-1,1,-4,3,2,0]
      +  ==> ([1,3,2],[-1,-4,0])

      Here’s an example of pattern matching on tuples:

      -
      swap :: (a,b) -> (b,a)
      -swap (x,y) = (y,x)
      +
      swap :: (a,b) -> (b,a)
      +swap (x,y) = (y,x)

      Here’s an example of pattern matching on tuples and lists at the same time:

      -
      -- sum all numbers that are paired with True
      -sumIf :: [(Bool,Int)] -> Int
      -sumIf [] = 0
      -sumIf ((True,x):xs) = x + sumIf xs
      -sumIf ((False,_):xs) = sumIf xs
      -
      sumIf [(True,1),(False,10),(True,100)]
      -  ==> 101
      +
      -- sum all numbers that are paired with True
      +sumIf :: [(Bool,Int)] -> Int
      +sumIf [] = 0
      +sumIf ((True,x):xs) = x + sumIf xs
      +sumIf ((False,_):xs) = sumIf xs
      +
      sumIf [(True,1),(False,10),(True,100)]
      +  ==> 101

      4.2 Interlude: Folding

      Consider the functions sumNumbers :: [Int] -> Int, myMaximum :: [Int] -> Int, and countNothings :: [Maybe a] -> Int again.

      -
      sumNumbers :: [Int] -> Int
      -sumNumbers [] = 0
      -sumNumbers (x:xs) = x + sumNumbers xs
      -
      -myMaximum :: [Int] -> Int
      -myMaximum [] = 0
      -myMaximum (x:xs) = go x xs
      -  where go biggest [] = biggest
      -        go biggest (x:xs) = go (max biggest x) xs
      -
      -countNothings :: [Maybe a] -> Int
      -countNothings [] = 0
      -countNothings (Nothing : xs) = 1 + countNothings xs
      -countNothings (Just _  : xs) = countNothings xs
      +
      sumNumbers :: [Int] -> Int
      +sumNumbers [] = 0
      +sumNumbers (x:xs) = x + sumNumbers xs
      +
      +myMaximum :: [Int] -> Int
      +myMaximum [] = 0
      +myMaximum (x:xs) = go x xs
      +  where go biggest [] = biggest
      +        go biggest (x:xs) = go (max biggest x) xs
      +
      +countNothings :: [Maybe a] -> Int
      +countNothings [] = 0
      +countNothings (Nothing : xs) = 1 + countNothings xs
      +countNothings (Just _  : xs) = countNothings xs

      They have one common characteristic. They take a list and produce a value that depends on the values of the elements in the given list. They “crunch” or fold a list of many values into a single value.

      Prelude has a function called foldr, which performs a right associative fold over a Foldable data type. We’ll learn more about Foldable soon. At this point, it suffices to think of lists, so we define

      -
      foldr :: (a -> b -> b) -> b -> [a] -> b
      -foldr f y []     = y
      -foldr f y (x:xs) = f x (foldr f y xs)
      +
      foldr :: (a -> b -> b) -> b -> [a] -> b
      +foldr f y []     = y
      +foldr f y (x:xs) = f x (foldr f y xs)

      What this definition says, is that for an empty list [] :: [a], foldr returns the default value y :: b. For any other list x : xs, foldr applies f to x and the result of foldr f y xs (i.e. folding over the rest of the list). It’s a simple definition by recursion.

      In other words, foldr calls its argument function f repeatedly with two arguments.

        @@ -2349,11 +2350,11 @@ unspecified ``` -->

        The expression foldr (+) 0 [1,2,3] evaluates as follows:

        -
        foldr (+) 0 [1,2,3] ==> foldr (+) 0 (1:2:3:[])
        -                    ==> 1 + (foldr (+) 0 (2:3:[]))
        -                    ==> 1 + (2 + (foldr (+) 0 (3:[])))
        -                    ==> 1 + (2 + (3 + (foldr (+) 0 [])))
        -                    ==> 1 + (2 + (3 + 0))
        +
        foldr (+) 0 [1,2,3] ==> foldr (+) 0 (1:2:3:[])
        +                    ==> 1 + (foldr (+) 0 (2:3:[]))
        +                    ==> 1 + (2 + (foldr (+) 0 (3:[])))
        +                    ==> 1 + (2 + (3 + (foldr (+) 0 [])))
        +                    ==> 1 + (2 + (3 + 0))

        The result can be thought of as a tree:

        One way to think about foldr f y xs is that it replaces the (:) operation with f and [] with y. In this case, f was (+) and y was 0. If you write out how sumNumbers [1,2,3] behaves, you’ll notice that it performs the same computation as foldr (+) 0 [1,2,3] does! More generally:

        -
        sumNumbers xs == foldr (+) 0 xs
        +
        sumNumbers xs == foldr (+) 0 xs

        Those more experienced with math may notice that we can prove this claim by induction: Firstly, sumNumbers [] ==> 0 and foldr (+) 0 [] ==> 0, so in the base case sumNumbers [] == foldr (+) 0 []. Next, we may assume as our induction hypothesis that sumNumbers xs == foldr (+) 0 xs for any list xs. Then, for the list x:xs, we have sumNumbers (x:xs) ==> x + sumNumbers xs. Hence, foldr (+) 0 (x:xs) ==> x + foldr (+) 0 xs ==> x + sumNumbers xs by induction hypothesis. Therefore, by induction, the equation holds.

        You don’t need to read, write, or understand induction proofs in this course, but perhaps it is reassuring to know that properties and equalities of functions in Haskell can be (in principle) analysed mathematically, because Haskell is such a nice language. (Equalities and properties can be analysed in any programming language, but for Haskell, this analysis is especially convenient because Haskell is pure.)

        Another folding example is the map function:

        -
        map g xs = foldr helper [] xs
        -  where helper y ys = g y : ys
        +
        map g xs = foldr helper [] xs
        +  where helper y ys = g y : ys

        To see why this works, consider what foldr helper [] [x1,x2,..,xn] does:

        @@ -2412,27 +2413,27 @@ unspecified

        The resulting list, [ g x1, g x2, g x3, ..., g xn ], is then exactly what we would have gotten with map g xs. (This could have been also proved by induction as we did for sumNumbers.) The lesson to take away is that folding is a particular, yet quite general, way to apply some transformation recursively into some structure (e.g. a list).

        4.3 Type Classes

        How can Haskell’s + work on both Ints and Doubles? Why can I compare all sorts of things with ==? We’ve briefly mentioned constrained types earlier. Let’s see what they really mean. Let’s look at the types of == and +.

        -
        (==) :: (Eq a) => a -> a -> Bool
        +
        (==) :: (Eq a) => a -> a -> Bool

        The type (Eq a) => a -> a -> Bool means: for all types a that belong to the class Eq, this is a function of type a -> a -> Bool. That is, if the type a is a member of the class Eq, you can give two values of type a to == and get a Bool result.

        -
        (+) :: (Num a) => a -> a -> a
        +
        (+) :: (Num a) => a -> a -> a

        Similarly, the type (Num a) => a -> a -> a means: for all types a that belong to the class Num, this is a function of type a -> a -> a. That is, you can give two values of the same type a to + and get out a third value of type a, as long as a is a member of Num.

        Num and Eq are type classes. A type class is a way to group together types that support similar operations.

        Note! A type class is a collection of types. It doesn’t have much to do with the classes of object oriented programming! In some situations, type classes can act like interfaces in object oriented programming. Unfortunately the functions in a type class are often called methods, adding to the confusion.

        PS. remember how using type variables for polymorphism was called parametric polymorphism? The fancy word for what type classes achieve is ad-hoc polymorphism. The difference is that with parametric polymorphism the function (e.g. head) has the same implementation for all types, whereas with ad-hoc polymorphisms there are multiple implementations (consider == on numbers and strings).

        4.4 Type Constraints

        When you’re working with a concrete type (not a type variable), you can just use type class functions (in this case, (==)):

        -
        f :: (Int -> Int) -> Int -> Bool
        -f g x = x == g x
        +
        f :: (Int -> Int) -> Int -> Bool
        +f g x = x == g x

        Of course, if the type in question isn’t a member of the right class, you get an error. For example:

        -
        addTrue :: Bool -> Bool
        -addTrue b = b + True
        +
        addTrue :: Bool -> Bool
        +addTrue b = b + True
        error:
             • No instance for (Num Bool) arising from a use of ‘+’
             • In the expression: b + True
               In an equation for ‘addTrue’: addTrue b = b + True

        However in a polymorphic function, you need to add type constraints. This doesn’t work:

        -
        f :: (a -> a) -> a -> Bool
        -f g x = x == g x
        +
        f :: (a -> a) -> a -> Bool
        +f g x = x == g x

        Luckily the error is nice:

        error:
             • No instance for (Eq a) arising from a use of ‘==’
        @@ -2443,230 +2444,230 @@ unspecified
             • In the expression: x == g x
               In an equation for ‘f’: f g x = x == g x

        To signal that f only works on types that are members of the Eq class, we add a type constraint (Eq a) => to the type annotation.

        -
        f :: (Eq a) => (a -> a) -> a -> Bool
        -f g x = x == g x
        +
        f :: (Eq a) => (a -> a) -> a -> Bool
        +f g x = x == g x

        If you don’t have a type annotation, type inference can provide the constraints!

        -
        Prelude> let f g x = x == g x
        -Prelude> :type f
        -f :: (Eq a) => (a -> a) -> a -> Bool
        +
        Prelude> let f g x = x == g x
        +Prelude> :type f
        +f :: (Eq a) => (a -> a) -> a -> Bool

        You can also have multiple constraints:

        -
        bothPairsEqual :: (Eq a, Eq b) => a -> a -> b -> b -> Bool
        -bothPairsEqual left1 left2 right1 right2 = left1 == left2 && right1 == right2
        +
        bothPairsEqual :: (Eq a, Eq b) => a -> a -> b -> b -> Bool
        +bothPairsEqual left1 left2 right1 right2 = left1 == left2 && right1 == right2

        4.5 Standard Type Classes

        Here are some standard Haskell type classes you should know about.

        4.5.1 Eq

        We already saw the Eq class for equality comparisons. Here are the basic operations of the Eq class and some examples of their use. As you can see pretty much all the types we’ve seen so far, except for functions, are members of Eq.

        -
        (==) :: Eq a => a -> a -> Bool
        -(/=) :: Eq a => a -> a -> Bool
        -
        Prelude> 1 == 2
        -False
        -Prelude> 1 /= 2
        -True
        -Prelude> "Foo" == "Bar"
        -False
        -Prelude> [[1,2],[3,4]] == [[1,2],[3,4]]
        -True
        -Prelude> (\x -> x+1) == (\x -> x+2)
        -
        -<interactive>:5:1: error:
        -    • No instance for (Eq (Integer -> Integer))
        -        arising from a use of ‘==’
        -        (maybe you haven't applied a function to enough arguments?)
        -    • In the expression: (\ x -> x + 1) == (\ x -> x + 2)
        -      In an equation for ‘it’: it = (\ x -> x + 1) == (\ x -> x + 2)
        +
        (==) :: Eq a => a -> a -> Bool
        +(/=) :: Eq a => a -> a -> Bool
        +
        Prelude> 1 == 2
        +False
        +Prelude> 1 /= 2
        +True
        +Prelude> "Foo" == "Bar"
        +False
        +Prelude> [[1,2],[3,4]] == [[1,2],[3,4]]
        +True
        +Prelude> (\x -> x+1) == (\x -> x+2)
        +
        +<interactive>:5:1: error:
        +    • No instance for (Eq (Integer -> Integer))
        +        arising from a use of ‘==’
        +        (maybe you haven't applied a function to enough arguments?)
        +    • In the expression: (\ x -> x + 1) == (\ x -> x + 2)
        +      In an equation for ‘it’: it = (\ x -> x + 1) == (\ x -> x + 2)

        There are some other useful functions that use the Eq class, like nub from the module Data.List.

        -
        Prelude> import Data.List
        -Prelude Data.List> :t nub
        -nub :: Eq a => [a] -> [a]
        -Prelude Data.List> nub [3,5,3,1,1]      -- eliminates duplicates
        -[3,5,1]
        +
        Prelude> import Data.List
        +Prelude Data.List> :t nub
        +nub :: Eq a => [a] -> [a]
        +Prelude Data.List> nub [3,5,3,1,1]      -- eliminates duplicates
        +[3,5,1]

        4.5.2 Ord

        The Ord class is for ordering (less than, greater than). Again, here are the basic operations and some examples of their use. Note the new Ordering type. It has values LT for “less than”, EQ for “equal” and GT for “greater than”.

        -
        compare :: Ord a => a -> a -> Ordering
        -(<) :: Ord a => a -> a -> Bool
        -(>) :: Ord a => a -> a -> Bool
        -(>=) :: Ord a => a -> a -> Bool
        -(<=) :: Ord a => a -> a -> Bool
        -max :: Ord a => a -> a -> a
        -min :: Ord a => a -> a -> a
        -
        Prelude> compare 1 1                -- 1 is EQual to 1
        -EQ
        -Prelude> compare 1 3                -- 1 is Less Than 3
        -LT
        -Prelude> compare 1 0                -- 1 is Greater Than 0
        -GT
        -Prelude> min 5 3
        -3
        -Prelude> max 5 3
        -5
        -Prelude> "aardvark" < "banana"      -- strings are compared alphabetically
        -True
        -Prelude> [1,2,3] > [2,5]            -- lists are compared like strings
        -False
        -Prelude> [1,2,3] > [1,1]
        -True
        +
        compare :: Ord a => a -> a -> Ordering
        +(<) :: Ord a => a -> a -> Bool
        +(>) :: Ord a => a -> a -> Bool
        +(>=) :: Ord a => a -> a -> Bool
        +(<=) :: Ord a => a -> a -> Bool
        +max :: Ord a => a -> a -> a
        +min :: Ord a => a -> a -> a
        +
        Prelude> compare 1 1                -- 1 is EQual to 1
        +EQ
        +Prelude> compare 1 3                -- 1 is Less Than 3
        +LT
        +Prelude> compare 1 0                -- 1 is Greater Than 0
        +GT
        +Prelude> min 5 3
        +3
        +Prelude> max 5 3
        +5
        +Prelude> "aardvark" < "banana"      -- strings are compared alphabetically
        +True
        +Prelude> [1,2,3] > [2,5]            -- lists are compared like strings
        +False
        +Prelude> [1,2,3] > [1,1]
        +True

        When we can compare values, we can also sort lists of them. The function sort from Data.List works on all types that belong to the Ord class.

        -
        Prelude> import Data.List
        -Prelude Data.List> :t sort
        -sort :: Ord a => [a] -> [a]
        -Prelude Data.List> sort [6,1,4,8,2]
        -[1,2,4,6,8]
        -Prelude Data.List> sort "black sphinx of quartz, judge my vow!"     -- remember, strings are lists!
        -"      !,aabcdefghijklmnoopqrstuuvwxyz"
        +
        Prelude> import Data.List
        +Prelude Data.List> :t sort
        +sort :: Ord a => [a] -> [a]
        +Prelude Data.List> sort [6,1,4,8,2]
        +[1,2,4,6,8]
        +Prelude Data.List> sort "black sphinx of quartz, judge my vow!"     -- remember, strings are lists!
        +"      !,aabcdefghijklmnoopqrstuuvwxyz"

        As a last example, let’s sort a list of lists according to length. We’ll need two helper functions:

        -
        -- from the module Data.Ord
        --- compares two values "through" the function f
        -comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
        -comparing f x y = compare (f x) (f y)
        -
        --- from the module Data.List
        --- sorts a list using the given comparison function
        -sortBy :: (a -> a -> Ordering) -> [a] -> [a]
        +
        -- from the module Data.Ord
        +-- compares two values "through" the function f
        +comparing :: (Ord a) => (b -> a) -> b -> b -> Ordering
        +comparing f x y = compare (f x) (f y)
        +
        +-- from the module Data.List
        +-- sorts a list using the given comparison function
        +sortBy :: (a -> a -> Ordering) -> [a] -> [a]

        Now the implementation of sortByLength is straightforward:

        -
        -- sorts lists by their length
        -sortByLength :: [[a]] -> [[a]]
        -sortByLength = sortBy (comparing length)
        -
        sortByLength [[1,2,3],[4,5],[4,5,6,7]]   ==>  [[4,5],[1,2,3],[4,5,6,7]]
        +
        -- sorts lists by their length
        +sortByLength :: [[a]] -> [[a]]
        +sortByLength = sortBy (comparing length)
        +
        sortByLength [[1,2,3],[4,5],[4,5,6,7]]   ==>  [[4,5],[1,2,3],[4,5,6,7]]

        4.5.3 Num, Integral, Fractional, Floating

        The Num class contains integer arithmetic:

        -
        (+) :: Num a => a -> a -> a
        -(-) :: Num a => a -> a -> a
        -(*) :: Num a => a -> a -> a
        -negate :: Num a => a -> a    -- 0-x
        -abs :: Num a => a -> a       -- absolute value
        -signum :: Num a => a -> a    -- -1 for negative values, 0 for 0, +1 for positive values
        -fromInteger :: Num a => Integer -> a
        +
        (+) :: Num a => a -> a -> a
        +(-) :: Num a => a -> a -> a
        +(*) :: Num a => a -> a -> a
        +negate :: Num a => a -> a    -- 0-x
        +abs :: Num a => a -> a       -- absolute value
        +signum :: Num a => a -> a    -- -1 for negative values, 0 for 0, +1 for positive values
        +fromInteger :: Num a => Integer -> a

        Num also shows up in the types of integer literals:

        -
        Prelude> :t 12
        -12 :: Num p => p
        +
        Prelude> :t 12
        +12 :: Num p => p

        This means that a literal like 12 can be interpreted as a member of any type implementing Num. When GHC reads a number literal like, 12 it produces code that corresponds to fromIntegral 12.

        -
        Prelude> 1 :: Int
        -1
        -Prelude> 1 :: Double
        -1.0
        -Prelude> fromIntegral 1 :: Double
        -1.0
        +
        Prelude> 1 :: Int
        +1
        +Prelude> 1 :: Double
        +1.0
        +Prelude> fromIntegral 1 :: Double
        +1.0

        Integral is the class of types that represent whole numbers, like Int and Integer. The most interesting functions are div and mod for integer division and remainder. All types that belong to Integral also belong to Num.

        -
        div :: Integral a => a -> a -> a
        -mod :: Integral a => a -> a -> a
        +
        div :: Integral a => a -> a -> a
        +mod :: Integral a => a -> a -> a

        Fractional is the class for types that have division. All types that belong to Fractional also belong to Num.

        -
        (/) :: Fractional a => a -> a -> a
        +
        (/) :: Fractional a => a -> a -> a

        Floating contains some additional operations that only make sense for floating point numbers. All types that belong to Floating also belong to Fractional (and to Num).

        -
        sqrt :: Floating a => a -> a
        -sin :: Floating a => a -> a
        +
        sqrt :: Floating a => a -> a
        +sin :: Floating a => a -> a

        4.5.4 Read and Show

        The Show and Read classes are for the functions show and read, that convert values to and from Strings.

        -
        show :: Show a => a -> String
        -read :: Read a => String -> a
        -
        Prelude> show 3
        -"3"
        -Prelude> read "3" :: Int
        -3
        -Prelude> read "3" :: Double
        -3.0
        +
        show :: Show a => a -> String
        +read :: Read a => String -> a
        +
        Prelude> show 3
        +"3"
        +Prelude> read "3" :: Int
        +3
        +Prelude> read "3" :: Double
        +3.0

        As you can see above, you often need to use a type annotation with read so that the compiler can choose the right implementation.

        4.5.5 Sidenote: Foldable

        One more thing! You might remember that it was mentioned earlier that the type of length isn’t [a] -> Int but something more general. Let’s have a look:

        -
        Prelude> :t length
        -length :: Foldable t => t a -> Int
        +
        Prelude> :t length
        +length :: Foldable t => t a -> Int

        This type looks a bit different than the ones we’ve seen before. The type variable t has an argument a. We’ll look at type classes like this in more detail in part 2, but here’s a crash course.

        What Foldable represents is types that you can fold over. The true type of foldr is:

        -
        foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
        +
        foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b

        We’ve succesfully used the fact that lists are Foldable since we’ve managed to use length and foldr on lists. However, Maybe is also Foldable! The Foldable instance for Maybe just pretends that values of Maybe a are like lists of length 0 or 1:

        -
        foldr (+) 1 Nothing   ==> 1
        -foldr (+) 1 (Just 3)  ==> 4
        -length Nothing        ==> 0
        -length (Just 'a')     ==> 1
        +
        foldr (+) 1 Nothing   ==> 1
        +foldr (+) 1 (Just 3)  ==> 4
        +length Nothing        ==> 0
        +length (Just 'a')     ==> 1

        We’ll meet some more foldable types next.

        4.6 More Data Structures

        Now that we are familiar with the standard type classes, we can look at one of their applications: the Map and Array data structures.

        4.6.1 Data.Map

        The Data.Map module defines the Map type. Maps are search trees for key-value pairs. One way to look at this is that a value of type Map k v is roughly the same as a value of type [(k,v)], a list of pairs. However, the operations on a map are more efficient than operations on a list.

        Since Data.Map contains some function with the same names as Prelude functions, the namespace needs to be imported qualified:

        -
        import qualified Data.Map as Map
        +
        import qualified Data.Map as Map

        Now we can refer to the map type as Map.Map, and to various map functions like Map.insert. Here are the most important functions for maps:

        -
        -- Create a Map from a list of key-value pairs
        -Map.fromList :: Ord k => [(k, a)] -> Map.Map k a
        -
        --- Insert a value into a map. Overrides any previous value with the same key.
        --- Returns a new map. Does not mutate the given map.
        -Map.insert :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
        -
        --- Get a value from a map using a key. Returns Nothing if the key was not present in the map.
        -Map.lookup :: Ord k => k -> Map.Map k a -> Maybe a
        -
        --- An empty map
        -Map.empty :: Map.Map k a
        +
        -- Create a Map from a list of key-value pairs
        +Map.fromList :: Ord k => [(k, a)] -> Map.Map k a
        +
        +-- Insert a value into a map. Overrides any previous value with the same key.
        +-- Returns a new map. Does not mutate the given map.
        +Map.insert :: Ord k => k -> a -> Map.Map k a -> Map.Map k a
        +
        +-- Get a value from a map using a key. Returns Nothing if the key was not present in the map.
        +Map.lookup :: Ord k => k -> Map.Map k a -> Maybe a
        +
        +-- An empty map
        +Map.empty :: Map.Map k a

        The Ord constraint for the key type of the map is needed because maps are implemented as ordered binary search trees.

        Note that like all Haskell values, maps are immutable meaning you can’t change a map once you define it. However, map operations like insert produce a new map. To perform multiple map operations you need to reuse the return value. Here’s a GHCi session operating on a map.

        -
        Prelude> import qualified Data.Map as Map
        -Prelude Map> let values = Map.fromList [("z",3),("w",4)]
        -Prelude Map> Map.lookup "z" values
        -Just 3
        -Prelude Map> Map.lookup "banana" values
        -Nothing
        -Prelude Map> Map.insert "x" 7 values
        -fromList [("w",4),("x",7),("z",3)]
        -Prelude Map> values                                       -- note immutability!
        -fromList [("w",4),("z",3)]
        -Prelude Map> Map.insert "x" 1 (Map.insert "y" 2 values)   -- two insertions
        -fromList [("w",4),("x",1),("y",2),("z",3)]
        -Prelude Map>
        +
        Prelude> import qualified Data.Map as Map
        +Prelude Map> let values = Map.fromList [("z",3),("w",4)]
        +Prelude Map> Map.lookup "z" values
        +Just 3
        +Prelude Map> Map.lookup "banana" values
        +Nothing
        +Prelude Map> Map.insert "x" 7 values
        +fromList [("w",4),("x",7),("z",3)]
        +Prelude Map> values                                       -- note immutability!
        +fromList [("w",4),("z",3)]
        +Prelude Map> Map.insert "x" 1 (Map.insert "y" 2 values)   -- two insertions
        +fromList [("w",4),("x",1),("y",2),("z",3)]
        +Prelude Map>

        Here’s an example of representing a bank as a Map String Int (map from account name to account balance), and withdrawing some money from an account:

        -
        withdraw :: String -> Int -> Map.Map String Int -> Map.Map String Int
        -withdraw account amount bank =
        -  case Map.lookup account bank of
        -    Nothing  -> bank                                   -- account not found, no change
        -    Just sum -> Map.insert account (sum-amount) bank   -- set new balance
        +
        withdraw :: String -> Int -> Map.Map String Int -> Map.Map String Int
        +withdraw account amount bank =
        +  case Map.lookup account bank of
        +    Nothing  -> bank                                   -- account not found, no change
        +    Just sum -> Map.insert account (sum-amount) bank   -- set new balance

        Here’s how you might use the withdraw function in GHCi. Note how the maps get printed as fromList invocations. Also note how calling withdraw ... bank returns a new bank and doesn’t change the existing bank.

        -
        GHCi> let bank = Map.fromList [("Bob",100),("Mike",50)]
        -GHCi> withdraw "Bob" 80 bank
        -fromList [("Bob",20),("Mike",50)]
        -GHCi> bank                         -- note immutability
        -fromList [("Bob",100),("Mike",50)]
        -GHCi> withdraw "Bozo" 1000 bank
        -fromList [("Bob",100),("Mike",50)]
        +
        GHCi> let bank = Map.fromList [("Bob",100),("Mike",50)]
        +GHCi> withdraw "Bob" 80 bank
        +fromList [("Bob",20),("Mike",50)]
        +GHCi> bank                         -- note immutability
        +fromList [("Bob",100),("Mike",50)]
        +GHCi> withdraw "Bozo" 1000 bank
        +fromList [("Bob",100),("Mike",50)]

        Data.Map defines all sorts of useful higher-order functions for updating maps. We can rewrite the withdraw function using Data.Map.adjust:

        -
        withdraw :: String -> Int -> Map.Map String Int -> Map.Map String Int
        -withdraw account amount bank = Map.adjust (\x -> x-amount) account bank
        +
        withdraw :: String -> Int -> Map.Map String Int -> Map.Map String Int
        +withdraw account amount bank = Map.adjust (\x -> x-amount) account bank

        Note! There are separate Data.Map.Strict and Data.Map.Lazy implementations. When you import Data.Map you get Data.Map.Lazy. You can find the documentation for all the Data.Map functions in the docs for Data.Map.Lazy. We won’t go into their differences here, but mostly you should use Data.Map.Strict in real code.

        4.6.2 Data.Array

        Another type that works kind of like a list but is more efficient for some operations is the array. Arrays are familiar from many other programming languages, but Haskell arrays are a bit different.

        Unlike the Data.Map module, the Data.Array can just be imported normally:

        -
        import Data.Array
        +
        import Data.Array

        Now we can look at the type of the array function that constructs an array.

        -
        array :: Ix i => (i, i) -> [(i, e)] -> Array i e
        +
        array :: Ix i => (i, i) -> [(i, e)] -> Array i e

        There are a couple of things to notice here. First of all, the Array type is parameterized by two types: the index type and the element type. Most other programming languages only parameterize arrays with the element type, but the index type is always int. In Haskell, we can have, for example, an Array Char Int: an array indexed by characters, or Array Bool String, an array indexed by booleans, or even Array (Int,Int) Int, a two-dimensional array of ints.

        Not all types can be index types. Only types that are similar to integers are suitable. That is the reason for the Ix i class constraint. The Ix class collects all the types that can be used as array indexes.

        Secondly, the array function takes an extra (i,i) parameter. These are the minimun and maximum indexes of the array. Unlike some other languages, where arrays always start at index 0 or 1, in Haskell you can define an array that starts from 7 and goes to 11. So here’s that array:

        -
        myArray :: Array Int String
        -myArray = array (7,11) [(7,"seven"), (8,"eight"), (9,"nine"), (10,"ten"), (11,"ELEVEN")]
        +
        myArray :: Array Int String
        +myArray = array (7,11) [(7,"seven"), (8,"eight"), (9,"nine"), (10,"ten"), (11,"ELEVEN")]

        Listing all the indices and elements in order can be a bit cumbersome, so there’s also the listArray constructor that just takes a list of elements in order:

        -
        listArray :: Ix i => (i, i) -> [e] -> Array i e
        -
        myArray :: Array Int String
        -myArray = listArray (7,11) ["seven", "eight", "nine", "ten", "ELEVEN"]
        +
        listArray :: Ix i => (i, i) -> [e] -> Array i e
        +
        myArray :: Array Int String
        +myArray = listArray (7,11) ["seven", "eight", "nine", "ten", "ELEVEN"]

        Arrays are used with two new operators:

        -
        -- Array lookup
        -(!) :: Ix i => Array i e -> i -> e
        --- Array update
        -(//) :: Ix i => Array i e -> [(i, e)] -> Array i e
        +
        -- Array lookup
        +(!) :: Ix i => Array i e -> i -> e
        +-- Array update
        +(//) :: Ix i => Array i e -> [(i, e)] -> Array i e

        Here’s an example GHCi session:

        -
        Prelude> import Data.Array
        -Prelude Data.Array> let myArray = listArray (7,11) ["seven", "eight", "nine", "ten", "ELEVEN"]
        -Prelude Data.Array> myArray
        -array (7,11) [(7,"seven"),(8,"eight"),(9,"nine"),(10,"ten"),(11,"ELEVEN")]
        -Prelude Data.Array> myArray ! 8
        -"eight"
        -Prelude Data.Array> myArray // [(8,"ocho"),(9,"nueve")]
        -array (7,11) [(7,"seven"),(8,"ocho"),(9,"nueve"),(10,"ten"),(11,"ELEVEN")]
        +
        Prelude> import Data.Array
        +Prelude Data.Array> let myArray = listArray (7,11) ["seven", "eight", "nine", "ten", "ELEVEN"]
        +Prelude Data.Array> myArray
        +array (7,11) [(7,"seven"),(8,"eight"),(9,"nine"),(10,"ten"),(11,"ELEVEN")]
        +Prelude Data.Array> myArray ! 8
        +"eight"
        +Prelude Data.Array> myArray // [(8,"ocho"),(9,"nueve")]
        +array (7,11) [(7,"seven"),(8,"ocho"),(9,"nueve"),(10,"ten"),(11,"ELEVEN")]

        You might be wondering why the (//) operator does multiple updates at once. The reason is the main weakness of Haskell arrays: immutability. Since arrays can’t be changed in place, (//) must copy the whole array. This is why in Haskell it’s often preferable to use lists or maps to store data that needs to be updated. However, arrays may still be useful when constructed once and then used for a large number of lookups. We’ll get back to how Haskell data structures work in the next lecture.

        Note! In this course we’ll use only Array, a simple array type that’s specified in the Haskell standard. There are many other array types like the mutable IOArray and the somewhat obscure DiffArray. There are also type classes for arrays like IArray and MArray. In addition to arrays there is a wide family of Vector types that can be more practical than Array for real programs.

        4.6.3 Sidenote: Folding over Maps & Arrays

        The Map and Array type are instances of Foldable just like lists are! This means you can use functions like length and foldr on them:

        -
        length (array (7,11) [(7,"seven"),(8,"eight"),(9,"nine"),(10,"ten"),(11,"ELEVEN")])
        -  ==> 5
        -foldr (+) 0 (Map.fromList [("banana",3),("egg",7)])
        -  ==> 10
        +
        length (array (7,11) [(7,"seven"),(8,"eight"),(9,"nine"),(10,"ten"),(11,"ELEVEN")])
        +  ==> 5
        +foldr (+) 0 (Map.fromList [("banana",3),("egg",7)])
        +  ==> 10

        4.7 Reading Docs

        Haskell libraries tend to have pretty good docs. We’ve linked to docs via Hackage (https://hackage.haskell.org) previously, but it’s important to know how to find the docs by your self too. The tool for generating Haskell documentation is called Haddock so sometimes Haskell docs are referred to as haddocks.

        Hackage is the Haskell package repository (just like PyPI for Python, Maven Central for Java or NPM for Javascript). In addition to the actual packages, it hosts documentation for them. Most of the modules that we use on this course are in the package called base. You can browse the docs for the base package at https://hackage.haskell.org/package/base-4.14.1.0/.

        @@ -2779,190 +2780,190 @@ It turns a list of pairs into a list of elements

      5.1 Algebraic Datatypes

      Haskell has a system called algebraic datatypes for defining new types. This sounds fancy, but is rather simple. Let’s dive in by looking at the standard library definitions of some familiar types:

      -
      data Bool = True | False
      -data Ordering = LT | EQ | GT
      +
      data Bool = True | False
      +data Ordering = LT | EQ | GT

      With this syntax you too can define types:

      -
      -- definition of a type with three values
      -data Color = Red | Green | Blue
      -
      --- a function that uses pattern matching on our new type
      -rgb :: Color -> [Double]
      -rgb Red = [1,0,0]
      -rgb Green = [0,1,0]
      -rgb Blue = [0,0,1]
      -
      Prelude> :t Red
      -Red :: Color
      -Prelude> :t [Red,Blue,Green]
      -[Red,Blue,Green] :: [Color]
      -Prelude> rgb Red
      -[1.0,0.0,0.0]
      +
      -- definition of a type with three values
      +data Color = Red | Green | Blue
      +
      +-- a function that uses pattern matching on our new type
      +rgb :: Color -> [Double]
      +rgb Red = [1,0,0]
      +rgb Green = [0,1,0]
      +rgb Blue = [0,0,1]
      +
      Prelude> :t Red
      +Red :: Color
      +Prelude> :t [Red,Blue,Green]
      +[Red,Blue,Green] :: [Color]
      +Prelude> rgb Red
      +[1.0,0.0,0.0]

      5.1.1 Fields

      Types like Bool, Ordering and Color that just list a bunch of constants are called enumerations or enums in Haskell and other languages. Enums are useful, but you need other types as well. Here we define a type for reports containing an id number, a title, and a body:

      -
      data Report = ConstructReport Int String String
      +
      data Report = ConstructReport Int String String

      This is how you create a report:

      -
      Prelude> :t ConstructReport 1 "Title" "This is the body."
      -ConstructReport 1 "Title" "This is the body." :: Report
      +
      Prelude> :t ConstructReport 1 "Title" "This is the body."
      +ConstructReport 1 "Title" "This is the body." :: Report

      You can access the fields with pattern matching:

      -
      reportContents :: Report -> String
      -reportContents (ConstructReport id title contents) = contents
      -setReportContents :: String -> Report -> Report
      -setReportContents contents (ConstructReport id title _contents) = ConstructReport id title contents
      +
      reportContents :: Report -> String
      +reportContents (ConstructReport id title contents) = contents
      +setReportContents :: String -> Report -> Report
      +setReportContents contents (ConstructReport id title _contents) = ConstructReport id title contents

      5.1.2 Constructors

      The things on the right hand side of a data declaration are called constructors. True, False, Red and ConstructReport are all examples of constructors. A type can have multiple constructors, and a constructor can have zero or more fields.

      Here is a datatype for a standard playing card. It has five constructors, of which Joker has zero fields and the others have one field.

      -
      data Card = Joker | Heart Int | Club Int | Spade Int | Diamond Int
      +
      data Card = Joker | Heart Int | Club Int | Spade Int | Diamond Int

      Constructors with fields have function type and can be used wherever functions can:

      -
      Prelude> :t Heart
      -Heart :: Int -> Card
      -Prelude> :t Club
      -Club :: Int -> Card
      -Prelude> map Heart [1,2,3]
      -[Heart 1,Heart 2,Heart 3]
      -Prelude> (Heart . (\x -> x+1)) 3
      -Heart 4
      +
      Prelude> :t Heart
      +Heart :: Int -> Card
      +Prelude> :t Club
      +Club :: Int -> Card
      +Prelude> map Heart [1,2,3]
      +[Heart 1,Heart 2,Heart 3]
      +Prelude> (Heart . (\x -> x+1)) 3
      +Heart 4

      5.1.3 Sidenote: Deriving

      By the way, there’s something missing from our Card type. Look at how it behaves compared to Ordering and Bool:

      -
      Prelude> EQ
      -EQ
      -Prelude> True
      -True
      -Prelude> Joker
      -<interactive>:1:0:
      -    No instance for (Show Card)
      -      arising from a use of `print' at <interactive>:1:0-4
      -    Possible fix: add an instance declaration for (Show Card)
      -    In a stmt of a 'do' expression: print it
      +
      Prelude> EQ
      +EQ
      +Prelude> True
      +True
      +Prelude> Joker
      +<interactive>:1:0:
      +    No instance for (Show Card)
      +      arising from a use of `print' at <interactive>:1:0-4
      +    Possible fix: add an instance declaration for (Show Card)
      +    In a stmt of a 'do' expression: print it

      The problem is that Haskell does not know how to print the types we defined. As the error says, they are not part of the Show class. The easy solution is to just add a deriving Show after the type definition:

      -
      data Card = Joker | Heart Int | Club Int | Spade Int | Diamond Int
      -  deriving Show
      -
      Prelude> Joker
      -Joker
      +
      data Card = Joker | Heart Int | Club Int | Spade Int | Diamond Int
      +  deriving Show
      +
      Prelude> Joker
      +Joker

      The deriving syntax is a way to automatically make your class a member of certain basic type classes, most notably Read, Show and Eq. We’ll talk more about what this means later.

      5.1.4 Algebraic?

      So why are these datatypes called algebraic? This is because, theoretically speaking, each datatype can be a sum of constructors, and each constructor is a product of fields. It makes sense to think of these as sums and products for many reasons, one being that we can count the possible values of each type this way:

      -
      data Bool = True | False            -- corresponds to 1+1. Has 2 possible values.
      -data TwoBools = TwoBools Bool Bool  -- corresponds to Bool*Bool, i.e. 2*2. Has 4 possible values.
      -data Complex = Two Bool Bool | One Bool | None
      -                                    -- corresponds to Bool*Bool+Bool+1 = 2*2+2+1 = 7. Has 7 possible values.
      +
      data Bool = True | False            -- corresponds to 1+1. Has 2 possible values.
      +data TwoBools = TwoBools Bool Bool  -- corresponds to Bool*Bool, i.e. 2*2. Has 4 possible values.
      +data Complex = Two Bool Bool | One Bool | None
      +                                    -- corresponds to Bool*Bool+Bool+1 = 2*2+2+1 = 7. Has 7 possible values.

      There is a rich theory of algebraic datatypes. If you’re interested, you might find more info here or here.

      5.2 Type Parameters

      We introduced type parameters and parametric polymorphism when introducing lists in Lecture 2. Since then, we’ve seen other parameterized types like Maybe and Either. Now we’ll learn how we can define our own parameterized types.

      5.2.1 Defining Parameterized Types

      The definition for Maybe is:

      -
      data Maybe a = Nothing | Just a
      +
      data Maybe a = Nothing | Just a

      What’s a? We define a parameterized type by mentioning a type variable (a in this case) on the left side of the = sign. We can then use the same type variable in fields for our constructors. This is analogous to polymorphic functions. Instead of defining separate functions

      -
      headInt :: [Int] -> Int
      -headBool :: [Bool] -> Bool
      +
      headInt :: [Int] -> Int
      +headBool :: [Bool] -> Bool

      and so on, we define one function head :: [a] -> a that works for all types a. Similarly, instead of defining multiple types

      -
      data MaybeInt = NothingInt | JustInt Int
      -data MaybeBool = NothingBool | JustBool Bool
      +
      data MaybeInt = NothingInt | JustInt Int
      +data MaybeBool = NothingBool | JustBool Bool

      we define one type Maybe a that works for all types a.

      Here’s our first own parameterized type Described. The values of type Described a contain a value of type a and a String description.

      -
      data Described a = Describe a String
      -
      -getValue :: Described a -> a
      -getValue (Describe x _) = x
      -
      -getDescription :: Described a -> String
      -getDescription (Describe _ desc) = desc
      -
      Prelude> :t Describe
      -Describe :: a -> String -> Described a
      -Prelude> :t Describe True "This is true"
      -Describe True "This is true" :: Described Bool
      -Prelude> getValue (Describe 3 "a number")
      -3
      -Prelude> getDescription (Describe 3 "a number")
      -"a number"
      +
      data Described a = Describe a String
      +
      +getValue :: Described a -> a
      +getValue (Describe x _) = x
      +
      +getDescription :: Described a -> String
      +getDescription (Describe _ desc) = desc
      +
      Prelude> :t Describe
      +Describe :: a -> String -> Described a
      +Prelude> :t Describe True "This is true"
      +Describe True "This is true" :: Described Bool
      +Prelude> getValue (Describe 3 "a number")
      +3
      +Prelude> getDescription (Describe 3 "a number")
      +"a number"

      5.2.2 Syntactic Note

      In the above definitions, we’ve used a as a type variable. However any word that starts with a lower case letter is fine. We could have defined Maybe like this:

      -
      data Maybe theType = Nothing | Just theType
      +
      data Maybe theType = Nothing | Just theType

      The rules for Haskell identifiers are:

      • Type variables and names for functions and values start lower case (e.g. a, map, xs)
      • Type names and constructor names start with upper case (e.g. Maybe, Just, Card, Heart)

      Note that a type and its constructor can have the same name. This is very common in Haskell code for types that only have one constructor. In this material we try to avoid it to avoid confusion. Here are some examples:

      -
      data Pair a = Pair a a
      -data Report = Report Int String String
      -
      Prelude> :t Pair
      -Pair :: a -> a -> Pair a
      +
      data Pair a = Pair a a
      +data Report = Report Int String String
      +
      Prelude> :t Pair
      +Pair :: a -> a -> Pair a

      Beware of mixing up types and constructors. Luckily types and constructors can never occur in the same context, so you get a nice error:

      -
      Prelude> Maybe                              -- trying to use a type name as a value
      -<interactive>:1:1: error:
      -    • Data constructor not in scope: Maybe
      -
      -Prelude> undefined :: Nothing               -- trying to use a constructor as a type
      -<interactive>:2:14: error:
      -    Not in scope: type constructor or class ‘Nothing’
      +
      Prelude> Maybe                              -- trying to use a type name as a value
      +<interactive>:1:1: error:
      +    • Data constructor not in scope: Maybe
      +
      +Prelude> undefined :: Nothing               -- trying to use a constructor as a type
      +<interactive>:2:14: error:
      +    Not in scope: type constructor or class ‘Nothing’

      5.2.3 Sidenote: Multiple Type Parameters

      Types can have multiple type parameters. The syntax is similar to defining functions with many arguments. Here’s the definition of the standard Either type:

      -
      data Either a b = Left a | Right b
      +
      data Either a b = Left a | Right b

      5.3 Recursive Types

      So far, all of the types we’ve defined have been of constant size. We can represent one report or one colour, but how could we represent a collection of things? We could use lists of course, but could we define a list type ourselves?

      Just like Haskell functions, Haskell data types can be recursive. This is no weirder than having an object in Java or Python that refers to another object of the same class. This is how you define a list of integers:

      -
      data IntList = Empty | Node Int IntList
      -  deriving Show
      -
      -ihead :: IntList -> Int
      -ihead (Node i _) = i
      -
      -itail :: IntList -> IntList
      -itail (Node _ t) = t
      -
      -ilength :: IntList -> Int
      -ilength Empty = 0
      -ilength (Node _ t) = 1 + ilength t
      +
      data IntList = Empty | Node Int IntList
      +  deriving Show
      +
      +ihead :: IntList -> Int
      +ihead (Node i _) = i
      +
      +itail :: IntList -> IntList
      +itail (Node _ t) = t
      +
      +ilength :: IntList -> Int
      +ilength Empty = 0
      +ilength (Node _ t) = 1 + ilength t

      We can use the functions defined above to work with lists of integers:

      -
      Prelude> ihead (Node 3 (Node 5 (Node 4 Empty)))
      -3
      -Prelude> itail (Node 3 (Node 5 (Node 4 Empty)))
      -Node 5 (Node 4 Empty)
      -Prelude> ilength (Node 3 (Node 5 (Node 4 Empty)))
      -3
      +
      Prelude> ihead (Node 3 (Node 5 (Node 4 Empty)))
      +3
      +Prelude> itail (Node 3 (Node 5 (Node 4 Empty)))
      +Node 5 (Node 4 Empty)
      +Prelude> ilength (Node 3 (Node 5 (Node 4 Empty)))
      +3

      Note that we can’t put values other than Ints inside our IntList:

      -
      Prelude> Node False Empty
      -
      -<interactive>:3:6: error:
      -    • Couldn't match expected type ‘Int’ with actual type ‘Bool’
      -    • In the first argument of ‘Node’, namely ‘False’
      -      In the expression: Node False Empty
      -      In an equation for ‘it’: it = Node False Empty
      +
      Prelude> Node False Empty
      +
      +<interactive>:3:6: error:
      +    • Couldn't match expected type ‘Int’ with actual type ‘Bool’
      +    • In the first argument of ‘Node’, namely ‘False’
      +      In the expression: Node False Empty
      +      In an equation for ‘it’: it = Node False Empty

      To be able to put any type of element in our list, let’s do the same thing with a type parameter. This is the same as the built in type [a], but with slightly clunkier syntax:

      -
      data List a = Empty | Node a (List a)
      -  deriving Show
      +
      data List a = Empty | Node a (List a)
      +  deriving Show

      Note how we need to pass the the type parameter a onwards in the recursion. We need to write Node a (List a) instead of Node a List. The Node constructor has two arguments. The first has type a, and the second has type List a. Here are the reimplementations of some standard list functions for our List type:

      -
      lhead :: List a -> a
      -lhead (Node h _) = h
      -
      -ltail :: List a -> List a
      -ltail (Node _ t) = t
      -
      -lnull :: List a -> Bool
      -lnull Empty = True
      -lnull _     = False
      -
      -llength :: List a -> Int
      -llength Empty = 0
      -llength (Node _ t) = 1 + llength t
      -
      Prelude> lhead (Node True Empty)
      -True
      -Prelude> ltail (Node True (Node False Empty))
      -Node False Empty
      -Prelude> lnull Empty
      -True
      +
      lhead :: List a -> a
      +lhead (Node h _) = h
      +
      +ltail :: List a -> List a
      +ltail (Node _ t) = t
      +
      +lnull :: List a -> Bool
      +lnull Empty = True
      +lnull _     = False
      +
      +llength :: List a -> Int
      +llength Empty = 0
      +llength (Node _ t) = 1 + llength t
      +
      Prelude> lhead (Node True Empty)
      +True
      +Prelude> ltail (Node True (Node False Empty))
      +Node False Empty
      +Prelude> lnull Empty
      +True

      Note that just like with normal Haskell lists, we can’t have elements of different types in the same list:

      -
      Prelude> Node True (Node "foo" Empty)
      -
      -<interactive>:5:12: error:
      -    • Couldn't match type ‘[Char]’ with ‘Bool’
      -      Expected type: List Bool
      -        Actual type: List [Char]
      -    • In the second argument of ‘Node’, namely ‘(Node "foo" Empty)’
      -      In the expression: Node True (Node "foo" Empty)
      -      In an equation for ‘it’: it = Node True (Node "foo" Empty)
      +
      Prelude> Node True (Node "foo" Empty)
      +
      +<interactive>:5:12: error:
      +    • Couldn't match type ‘[Char]’ with ‘Bool’
      +      Expected type: List Bool
      +        Actual type: List [Char]
      +    • In the second argument of ‘Node’, namely ‘(Node "foo" Empty)’
      +      In the expression: Node True (Node "foo" Empty)
      +      In an equation for ‘it’: it = Node True (Node "foo" Empty)

      5.3.1 Example: Growing a Tree

      Just like a list, we can also represent a binary tree:

      -
      data Tree a = Node a (Tree a) (Tree a) | Empty
      +
      data Tree a = Node a (Tree a) (Tree a) | Empty

      Our tree contains nodes, which contain a value of type a and two child trees, and empty trees.

      In case you’re not familiar with binary trees, they’re a data structure that’s often used as the basis for other data structures (Data.Map is based on trees!). Binary trees are often drawn as (upside-down) pictures, like this:

      @@ -2976,122 +2977,122 @@ It turns a list of pairs into a list of elements ``` -->

      The highest node in the tree is called the root (0 in this case), and the nodes with no children are called leaves (2, 3 and 4 in this case). We can define this tree using our Tree type like this:

      -
      example :: Tree Int
      -example = (Node 0 (Node 1 (Node 2 Empty Empty)
      -                          (Node 3 Empty Empty))
      -                  (Node 4 Empty Empty))
      +
      example :: Tree Int
      +example = (Node 0 (Node 1 (Node 2 Empty Empty)
      +                          (Node 3 Empty Empty))
      +                  (Node 4 Empty Empty))

      The height of a binary tree is length of the longest path from the root to a leaf. In Haskell terms, it’s how many nested levels of Node constructors you need to build the tree. The height of our example tree is 3. Here’s a function that computes the height of a tree:

      -
      treeHeight :: Tree a -> Int
      -treeHeight Empty = 0
      -treeHeight (Node _ l r) = 1 + max (treeHeight l) (treeHeight r)
      -
      treeHeight Empty ==> 0
      -treeHeight (Node 2 Empty Empty)
      -  ==> 1 + max (treeHeight Empty) (treeHeight Empty)
      -  ==> 1 + max 0 0
      -  ==> 1
      -treeHeight (Node 1 Empty (Node 2 Empty Empty))
      -  ==> 1 + max (treeHeight Empty) (treeHeight (Node 2 Empty Empty))
      -  ==> 1 + max 0 1
      -  ==> 2
      -treeHeight (Node 0 (Node 1 Empty (Node 2 Empty Empty)) Empty)
      -  ==> 1 + max (treeHeight (Node 1 Empty (Node 2 Empty Empty))) (treeHeight Empty)
      -  ==> 1 + max 2 0
      -  ==> 3
      +
      treeHeight :: Tree a -> Int
      +treeHeight Empty = 0
      +treeHeight (Node _ l r) = 1 + max (treeHeight l) (treeHeight r)
      +
      treeHeight Empty ==> 0
      +treeHeight (Node 2 Empty Empty)
      +  ==> 1 + max (treeHeight Empty) (treeHeight Empty)
      +  ==> 1 + max 0 0
      +  ==> 1
      +treeHeight (Node 1 Empty (Node 2 Empty Empty))
      +  ==> 1 + max (treeHeight Empty) (treeHeight (Node 2 Empty Empty))
      +  ==> 1 + max 0 1
      +  ==> 2
      +treeHeight (Node 0 (Node 1 Empty (Node 2 Empty Empty)) Empty)
      +  ==> 1 + max (treeHeight (Node 1 Empty (Node 2 Empty Empty))) (treeHeight Empty)
      +  ==> 1 + max 2 0
      +  ==> 3

      In case you’re familiar with binary search trees, here are the definitions of the lookup and insert opertions for a binary search tree. If you don’t know what I’m talking about, you don’t need to understand this.

      -
      lookup :: Int -> Tree Int -> Bool
      -lookup x Empty = False
      -lookup x (Node y l r)
      -  | x < y = lookup x l
      -  | x > y = lookup x r
      -  | otherwise = True
      -
      -insert :: Int -> Tree Int -> Tree Int
      -insert x Empty = Node x Empty Empty
      -insert x (Node y l r)
      -  | x < y = Node y (insert x l) r
      -  | x > y = Node y l (insert x r)
      -  | otherwise = Node y l r
      +
      lookup :: Int -> Tree Int -> Bool
      +lookup x Empty = False
      +lookup x (Node y l r)
      +  | x < y = lookup x l
      +  | x > y = lookup x r
      +  | otherwise = True
      +
      +insert :: Int -> Tree Int -> Tree Int
      +insert x Empty = Node x Empty Empty
      +insert x (Node y l r)
      +  | x < y = Node y (insert x l) r
      +  | x > y = Node y l (insert x r)
      +  | otherwise = Node y l r

      5.4 Record Syntax

      If some fields need to be accessed often, it can be convenient to have helper functions for reading those fields. For instance, the type Person might have multiple fields:

      -
      data Person = MkPerson String Int String String String deriving Show
      +
      data Person = MkPerson String Int String String String deriving Show

      A list of persons might look like the following:

      -
      people :: [Person]
      -people = [ MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer"
      -         , MkPerson "Maija Meikäläinen" 35 "Rovaniemi" "Finland" "Engineer"
      -         , MkPerson "Mauno Mutikainen" 27 "Turku" "Finland" "Mathematician"
      -         ]
      +
      people :: [Person]
      +people = [ MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer"
      +         , MkPerson "Maija Meikäläinen" 35 "Rovaniemi" "Finland" "Engineer"
      +         , MkPerson "Mauno Mutikainen" 27 "Turku" "Finland" "Mathematician"
      +         ]

      Suppose that we need to find all engineers from Finland:

      -
      query :: [Person] -> [Person]
      -query [] = []
      -query (MkPerson name age town state profession):xs
      -  | state == "Finland" && profession == "Engineer" =
      -      (MkPerson name age town state profession) : query xs
      -  | otherwise = query xs
      +
      query :: [Person] -> [Person]
      +query [] = []
      +query (MkPerson name age town state profession):xs
      +  | state == "Finland" && profession == "Engineer" =
      +      (MkPerson name age town state profession) : query xs
      +  | otherwise = query xs

      Thus,

      -
      query people ==> [MkPerson "Maija Meikäläinen" 35 "Rovaniemi" "Finland" "Engineer"]
      +
      query people ==> [MkPerson "Maija Meikäläinen" 35 "Rovaniemi" "Finland" "Engineer"]

      Note that the types of the fields give little information on what is the intended content in those fields. We need to remember in all places in the code that town goes before state and not vice versa.

      Haskell has a feature called record syntax that is helpful in these kinds of cases. The datatype Person can be defined as a record:

      -
      data Person = MkPerson { name :: String, age :: Int, town :: String, state :: String, profession :: String}
      -  deriving Show
      +
      data Person = MkPerson { name :: String, age :: Int, town :: String, state :: String, profession :: String}
      +  deriving Show

      We can still define values of Person normally, but the Show instance prints the field names for us:

      -
      Prelude> MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer"
      -MkPerson {name = "Jane Doe", age = 21, town = "Houston", state = "Texas", profession = "Engineer"}
      +
      Prelude> MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer"
      +MkPerson {name = "Jane Doe", age = 21, town = "Houston", state = "Texas", profession = "Engineer"}

      However, we can also define values using record syntax. Note how the fields don’t need to be in any specific order now that they have names.

      -
      Prelude> MkPerson {name = "Jane Doe", town = "Houston", profession = "Engineer", state = "Texas", age = 21}
      -MkPerson {name = "Jane Doe", age = 21, town = "Houston", state = "Texas", profession = "Engineer"}
      +
      Prelude> MkPerson {name = "Jane Doe", town = "Houston", profession = "Engineer", state = "Texas", age = 21}
      +MkPerson {name = "Jane Doe", age = 21, town = "Houston", state = "Texas", profession = "Engineer"}

      Most importantly, We get accessor functions for the fields for free:

      -
      Prelude> :t profession
      -profession :: Person -> String
      -Prelude> profession (MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer")
      -"Engineer"
      +
      Prelude> :t profession
      +profession :: Person -> String
      +Prelude> profession (MkPerson "Jane Doe" 21 "Houston" "Texas" "Engineer")
      +"Engineer"

      We can now rewrite the query function using these accessor functions:

      -
      query :: [Person] -> [Person]
      -query []     = []
      -query (x:xs)
      -  | state x == "Finland" && profession x == "Engineer" =
      -      x : query xs
      -  | otherwise = query xs
      +
      query :: [Person] -> [Person]
      +query []     = []
      +query (x:xs)
      +  | state x == "Finland" && profession x == "Engineer" =
      +      x : query xs
      +  | otherwise = query xs

      You’ll probably agree that the code looks more pleasant now.

      5.5 Algebraic Datatypes: Summary

      • Types are defined like this
      -
      data TypeName = ConstructorName FieldType FieldType2 | AnotherConstructor FieldType3 | OneMoreCons
      +
      data TypeName = ConstructorName FieldType FieldType2 | AnotherConstructor FieldType3 | OneMoreCons
      • … or like this if we’re using type variables
      -
      data TypeName variable = Cons1 variable Type1 | Cons2 Type2 variable
      +
      data TypeName variable = Cons1 variable Type1 | Cons2 Type2 variable
      • You can have one or more constructors
      • Each constructor can have zero or more fields
      • Constructors start with upper case, type variables with lower case
      • Values are handled with pattern matching:
      -
      foo (ConstructorName a b) = a+b
      -foo (AnotherConstructor _) = 0
      -foo OneMoreCons = 7
      +
      foo (ConstructorName a b) = a+b
      +foo (AnotherConstructor _) = 0
      +foo OneMoreCons = 7
      • Constructors are just functions:
      -
      ConstructorName :: FieldType -> FieldType2 -> TypeName
      -Cons1 :: a -> Type1 -> TypeName a
      +
      ConstructorName :: FieldType -> FieldType2 -> TypeName
      +Cons1 :: a -> Type1 -> TypeName a
      • You can also define datatypes using record syntax:
      -
      data TypeName = Constructor { field1 :: Field1Type, field2 :: Field2Type }
      +
      data TypeName = Constructor { field1 :: Field1Type, field2 :: Field2Type }

      This gives you accessor functions like field1 :: TypeName -> Field1Type for free.

      5.6 Sidenote: Other Ways of Defining Types

      In addition to the data keyword, there are two additional ways of defining types in Haskell.

      The newtype keyword works like data, but you can only have a single constructor with a single field. It’s sometimes wise to use newtype for performance resons, but we’ll get back to those in part 2.

      The type keyword introduces a type alias. Type aliases don’t affect type checking, they just offer a shorthand for writing types. For example the familiar String type is an alias for [Char]:

      -
      type String = [Char]
      +
      type String = [Char]

      This means that whenever the compiler reads String, it just immediately replaces it with [Char]. Type aliases seem useful, but they can easily make reading type errors harder.

      5.7 How Do Algebraic Datatypes Work?

      Remember how lists were represented in memory as linked lists? Let’s look in more detail at what algebraic datatypes look like in memory.

      Haskell data forms directed graphs in memory. Every constructor is a node, every field is an edge. Names (of variables) are pointers into this graph. Different names can share parts of the structure. Here’s an example with lists. Note how the last two elements of x are shared with y and z.

      -
      let x = [1,2,3,4]
      -    y = drop 2 x
      -    z = 5:y
      +
      let x = [1,2,3,4]
      +    y = drop 2 x
      +    z = 5:y

      What happens when you make a new version of a datastructure is called path copying. Since Haskell data is immutable, the changed parts of the datastructure get copied, while the unchanged parts can be shared between the old and new versions.

      Consider the definition of ++:

      -
      []     ++ ys = ys
      -(x:xs) ++ ys = x:(xs ++ ys)
      +
      []     ++ ys = ys
      +(x:xs) ++ ys = x:(xs ++ ys)

      We are making a copy of the first argument while we walk it. For every : constructor in the first input list, we are creating a new : constructor in the output list. The second argument can be shared. It is not used at all in the recursion. Visually:

      One more way to think about it is this: we want to change the tail pointer of the list element (3:). That means we need to make a new (3:). However the (2:) points to the (3:) so we need a new copy of the (2:) as well. Likewise for (1:).

      The graphs that we get when working with lists are fairly simple. As a more involved example, here is what happens in memory when we run the binary tree insertion example from earlier in this lecture.

      -
      insert :: Int -> Tree Int -> Tree Int
      -insert x Empty = Node x Empty Empty
      -insert x (Node y l r)
      -  | x < y = Node y (insert x l) r
      -  | x > y = Node y l (insert x r)
      -  | otherwise = Node y l r
      +
      insert :: Int -> Tree Int -> Tree Int
      +insert x Empty = Node x Empty Empty
      +insert x (Node y l r)
      +  | x < y = Node y (insert x l) r
      +  | x > y = Node y l (insert x r)
      +  | otherwise = Node y l r

      @@ -3846,103 +3847,103 @@ If I define an instance for Num, I also get an instance for F

      8.1 A Taste of IO

      This course has been centered around pure functional programming. We’ve done lots of arithmetic, reversed lists, worked with binary trees, but so far we haven’t been able to affect the world outside our GHCi.

      Things like reading input, writing to a file, or talking over the network are side effects. Side effects can’t be represented with pure functional code. A function like

      -
      readInputFromTheUser :: String -> String
      +
      readInputFromTheUser :: String -> String

      can’t be pure, because if it were, readInputFromUser "What is your name?" would always have to return the same result. However, representing side-effects and impurity in a pure language is possible. There are many ways of doing it, and the Haskell way is to use Monads.

      Monads are reputedly difficult to understand. That is probably because they are so abstract. I think it’s best to focus on practical and concrete cases first. Here’s a taste of the IO Monad, which you can use for all sorts of side effects in Haskell.

      Let’s start!

      -
      Prelude> :t getLine
      -getLine :: IO String
      -Prelude> line <- getLine
      +
      Prelude> :t getLine
      +getLine :: IO String
      +Prelude> line <- getLine
      another line
      -
      Prelude> :t line
      -line :: String
      -Prelude> line
      -"another line"
      -Prelude> reverse line
      -"enil rehtona"
      +
      Prelude> :t line
      +line :: String
      +Prelude> line
      +"another line"
      +Prelude> reverse line
      +"enil rehtona"

      What we’ve seen here is the IO action getLine. It has type IO String. This means that GHCi can execute the action to produce a value of type String. When we enter line <- getLine into GHCi, we mean:

      Execute the IO action getLine, and give the result the name line.

      After we’ve received line, it’s a pure String value and we can work with it normally.

      Some IO actions take parameters. For example putStrLn :: String -> IO () takes a String and returns an IO action that prints that string. The () type is a special type that only has one value, (). In this case IO () means that this IO always produces the same empty value (). You can run IO actions by just

      -
      Prelude> :t putStrLn
      -putStrLn :: String -> IO ()
      -Prelude> :t putStrLn "hello"
      -putStrLn "hello" :: IO ()
      -Prelude> val <- putStrLn "hello"
      -hello
      -Prelude> val
      -()
      +
      Prelude> :t putStrLn
      +putStrLn :: String -> IO ()
      +Prelude> :t putStrLn "hello"
      +putStrLn "hello" :: IO ()
      +Prelude> val <- putStrLn "hello"
      +hello
      +Prelude> val
      +()

      If you don’t need the return value of an IO action, you can run it in GHCi without the <-:

      -
      Prelude> putStrLn "hello"
      -hello
      +
      Prelude> putStrLn "hello"
      +hello

      You can build your own IO actions by combining other actions with do-notation. A do block lists IO actions that are executed in order.

      -
      printTwoThings :: IO ()
      -printTwoThings = do
      -  putStrLn "Hello!"
      -  putStrLn "How are you?"
      -
      -greet :: IO ()
      -greet = do
      -  putStrLn "What's your name?"
      -  name <- getLine
      -  putStrLn ("Hello, " ++ name)
      -
      Prelude> printTwoThings
      -Hello!
      -How are you?
      -Prelude> greet
      -What's your name?
      -Seraphim
      -Hello, Seraphim
      +
      printTwoThings :: IO ()
      +printTwoThings = do
      +  putStrLn "Hello!"
      +  putStrLn "How are you?"
      +
      +greet :: IO ()
      +greet = do
      +  putStrLn "What's your name?"
      +  name <- getLine
      +  putStrLn ("Hello, " ++ name)
      +
      Prelude> printTwoThings
      +Hello!
      +How are you?
      +Prelude> greet
      +What's your name?
      +Seraphim
      +Hello, Seraphim

      8.1.1 What About Purity?

      It feels as if we can just do side effects where ever we want with these IO actions. However, it’s important to remember the distinction between defining an IO action and executing it.

      Let’s try to print while mapping over a list

      -
      printAndIncrement :: Int -> Int
      -printAndIncrement x = x+1
      -  where action = putStrLn "got a number!"
      -
      Prelude> map printAndIncrement [1,2,3]
      -[2,3,4]
      +
      printAndIncrement :: Int -> Int
      +printAndIncrement x = x+1
      +  where action = putStrLn "got a number!"
      +
      Prelude> map printAndIncrement [1,2,3]
      +[2,3,4]

      This didn’t print anything, because even though we defined our action, it wasn’t given to GHCi for execution. Because printAndIncrement returns an Int, it can’t return an action. Ok, let’s try another approach:

      -
      Prelude> length (map putStrLn ["string1","string2"])
      -2
      +
      Prelude> length (map putStrLn ["string1","string2"])
      +2

      That didn’t print anything either! Let’s see why:

      -
      Prelude> :t map putStrLn ["string1","string2"]
      -map putStrLn ["string1","string2"] :: [IO ()]
      -Prelude> :t length (map putStrLn ["string1","string2"])
      -length (map putStrLn ["string1","string2"]) :: Int
      +
      Prelude> :t map putStrLn ["string1","string2"]
      +map putStrLn ["string1","string2"] :: [IO ()]
      +Prelude> :t length (map putStrLn ["string1","string2"])
      +length (map putStrLn ["string1","string2"]) :: Int

      We generated a list of IO actions and computed the value of the list. Defining IO actions is pure, it’s running them that causes side effects. Since the type of our expression was Int, no IO actions could land in GHCi and be executed.

      If we instead return an IO action, it does get run:

      -
      Prelude> :t head (map putStrLn ["string1","string2"])
      -head (map putStrLn ["string1","string2"]) :: IO ()
      -Prelude> head (map putStrLn ["string1","string2"])
      -string1
      +
      Prelude> :t head (map putStrLn ["string1","string2"])
      +head (map putStrLn ["string1","string2"]) :: IO ()
      +Prelude> head (map putStrLn ["string1","string2"])
      +string1

      Here too, the code that produces the action putStrLn "string1" is pure, it’s only after the IO action is executed by GHCi that we see the printed string. And as you can see, the other IO action, putStrLn "string2", never got run.

      If this feels complicated, don’t worry. We’ll get back to this on part 2 of the course.

      8.1.2 What About Haskell Programs?

      We know that GHCi can run IO actions. What about actual Haskell programs? The way Haskell programs work is that the IO action called main gets executed when the program is run. Recall our example program from Lecture 1.

      -
      module Gold where
      -
      --- The golden ratio
      -phi :: Double
      -phi = (sqrt 5 + 1) / 2
      -
      -polynomial :: Double -> Double
      -polynomial x = x^2 - x - 1
      -
      -f x = polynomial (polynomial x)
      -
      -main = do
      -  print (polynomial phi)
      -  print (f phi)
      +
      module Gold where
      +
      +-- The golden ratio
      +phi :: Double
      +phi = (sqrt 5 + 1) / 2
      +
      +polynomial :: Double -> Double
      +polynomial x = x^2 - x - 1
      +
      +f x = polynomial (polynomial x)
      +
      +main = do
      +  print (polynomial phi)
      +  print (f phi)

      Here we see some pure code and a main IO action that prints two things (print is just putStrLn combined with show).

      We can place this code in a file called Gold.hs, compile it into an executable, and run it:

      -
      $ ghc -main-is Gold Gold.hs
      -[1 of 1] Compiling Gold             ( Gold.hs, Gold.o )
      -Linking Gold ...
      -$ ./Gold
      -0.0
      --1.0
      +
      $ ghc -main-is Gold Gold.hs
      +[1 of 1] Compiling Gold             ( Gold.hs, Gold.o )
      +Linking Gold ...
      +$ ./Gold
      +0.0
      +-1.0

      8.2 Summary

      So far, we’ve learned about Haskell’s syntax and types, quite a bit of functional programming and about some language features like type classes.

      We’ve also seen some type-oriented programming, and even gotten a taste of I/O in Haskell.

      diff --git a/part2.html b/part2.html index 004e8af..c9267be 100644 --- a/part2.html +++ b/part2.html @@ -15,6 +15,7 @@ pre > code.sourceCode { white-space: pre; position: relative; } pre > code.sourceCode > span { display: inline-block; line-height: 1.25; } pre > code.sourceCode > span:empty { height: 1.2em; } +.sourceCode { overflow: visible; } code.sourceCode > span { color: inherit; text-decoration: inherit; } div.sourceCode { margin: 1em 0; } pre.sourceCode { margin: 0; } @@ -290,26 +291,26 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni

      There’s one more powerful mechanism for creating more types: Algebraic datatypes (ADTs). Some examples include:

      -
      -- Enumeration types
      -data Bool = True | False
      -data Color = Red | Green | Blue
      -
      --- Record types that contain fields
      -data Vector2d = MakeVector Double Double
      -data Person = Person Int String
      -
      --- Parameterized types. Note the type parameter `a`
      -data PairOf a = TwoValues a a
      -
      --- Recursive types
      -data IntList = Empty | Node Int IntList
      -
      --- Complex types which combine many of these features
      -data Maybe a = Nothing | Just a
      -data Either a b = Left a | Right b
      -data List a = Nil | Cons a (List a)             -- This is equivalent to the built-in [a] type
      -data Tree a = Leaf a | Node a (Tree a) (Tree a)
      -data MultiTree a = MultiTree a [MultiTree a]     -- Note the list
      +
      -- Enumeration types
      +data Bool = True | False
      +data Color = Red | Green | Blue
      +
      +-- Record types that contain fields
      +data Vector2d = MakeVector Double Double
      +data Person = Person Int String
      +
      +-- Parameterized types. Note the type parameter `a`
      +data PairOf a = TwoValues a a
      +
      +-- Recursive types
      +data IntList = Empty | Node Int IntList
      +
      +-- Complex types which combine many of these features
      +data Maybe a = Nothing | Just a
      +data Either a b = Left a | Right b
      +data List a = Nil | Cons a (List a)             -- This is equivalent to the built-in [a] type
      +data Tree a = Leaf a | Node a (Tree a) (Tree a)
      +data MultiTree a = MultiTree a [MultiTree a]     -- Note the list

      Values of these types include:

      @@ -371,9 +372,9 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni

      You can combine parameterized types in complex ways, for example with something like Either [String->String] (Maybe String, Int).

      The names of concrete types start with capital letters. Lowercase letters are used for type variables which indicate parametric polymorphism: functions and values that can have multiple types. Here are some examples of polymorphic function types:

      -
      [a] -> [a]    -- function from list of any type, to list of the same type
      -[a] -> a      -- function from list of any type, to the element type
      -(a,b) -> [a]  -- function from tuple to list
      +
      [a] -> [a]    -- function from list of any type, to list of the same type
      +[a] -> a      -- function from list of any type, to the element type
      +(a,b) -> [a]  -- function from tuple to list

      9.1.1 More About Lists

      List literals can be written in using the familiar [x,y,z] syntax. However, that notation is just a shorthand as lists are actually built up of the list constructors [] and (:). These constructors are also used when pattern matching lists. Here are some examples of lists:

      @@ -476,168 +477,168 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni

      9.2 Functions

      The basic form of function definition is:

      -
      functionName :: argumentType -> returnType
      -functionName argument = returnValue
      +
      functionName :: argumentType -> returnType
      +functionName argument = returnValue

      For example:

      -
      repeatString :: String -> String
      -repeatString s = s ++ s
      +
      repeatString :: String -> String
      +repeatString s = s ++ s

      Functions taking multiple arguments are defined in a similar manner. Note how the type of a multi-argument function looks like.

      -
      surroundString :: String -> String -> String
      -surroundString around s = around ++ s ++ around
      +
      surroundString :: String -> String -> String
      +surroundString around s = around ++ s ++ around

      Functions can be polymorphic, can take multiple arguments, and can even take functions as arguments. Here are more examples:

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

      More sophisticated functions can be defined using pattern matching. We can pattern match on the constructors of algebraic datatypes like Maybe, and also lists with the constructors [] and (:).

      -
      swap :: (a,b) -> (b,a)
      -swap (x,y) = (y,x)
      -
      -maybe :: b -> (a -> b) -> Maybe a -> b
      -maybe def _ Nothing  = def
      -maybe _   f (Just x) = Just (f x)
      -
      -safeHead :: [a] -> Maybe a
      -safeHead []    = Nothing
      -safeHead (x:_) = Just x
      +
      swap :: (a,b) -> (b,a)
      +swap (x,y) = (y,x)
      +
      +maybe :: b -> (a -> b) -> Maybe a -> b
      +maybe def _ Nothing  = def
      +maybe _   f (Just x) = Just (f x)
      +
      +safeHead :: [a] -> Maybe a
      +safeHead []    = Nothing
      +safeHead (x:_) = Just x

      Yet more sophistication can be achieved with guards. Guards let you define a function case-by-case based on tests of type Bool. Guards are useful in situations where pattern matching can’t be used. Of course, guards can also be combined with pattern matching:

      -
      myAbs :: Int -> Int
      -myAbs x
      -  | x < 0     = -x
      -  | otherwise = x
      -
      -safeDiv :: Double -> Double -> Maybe Double
      -safeDiv x y
      -  | y == 0    = Nothing
      -  | otherwise = Just (x / y)
      -
      -buy :: String -> Double -> String
      -buy "Banana" money
      -  | money < 3.2   = "You don't have enough money for a banana"
      -  | otherwise     = "You bought a banana"
      -buy product  _    = "No such product: " ++ product
      +
      myAbs :: Int -> Int
      +myAbs x
      +  | x < 0     = -x
      +  | otherwise = x
      +
      +safeDiv :: Double -> Double -> Maybe Double
      +safeDiv x y
      +  | y == 0    = Nothing
      +  | otherwise = Just (x / y)
      +
      +buy :: String -> Double -> String
      +buy "Banana" money
      +  | money < 3.2   = "You don't have enough money for a banana"
      +  | otherwise     = "You bought a banana"
      +buy product  _    = "No such product: " ++ product

      Case expressions let us pattern match inside functions. They are useful in situations where the result of one function depends on the result of another and we want to match the pattern on the output of the other function:

      -
      divDefault :: Double -> Double -> Double -> Double
      -divDefault x y def = case safeDiv x y of
      -  Nothing -> def
      -  Just w  -> w
      +
      divDefault :: Double -> Double -> Double -> Double
      +divDefault x y def = case safeDiv x y of
      +  Nothing -> def
      +  Just w  -> w

      Let-expressions enable local definitions. Where-clauses work similarly to lets. For example:

      -
      circleArea :: Double -> Double
      -circleArea r = let pi = 3.1415926
      -                   square x = x * x
      -               in pi * square r
      -
      -circleArea' :: Double -> Double
      -circleArea' r = pi * square r
      -    where pi = 3.1415926
      -          square x = x * x
      +
      circleArea :: Double -> Double
      +circleArea r = let pi = 3.1415926
      +                   square x = x * x
      +               in pi * square r
      +
      +circleArea' :: Double -> Double
      +circleArea' r = pi * square r
      +    where pi = 3.1415926
      +          square x = x * x

      Lambda expressions are another occasionally useful syntax for defining functions. Lambda expressions represent anonymous (unnamed) functions. They can be used for defining local functions that are typically used only once.

      -
      incrementAll :: [Int] -> [Int]
      -incrementAll xs = map (\x -> x + 1) xs
      +
      incrementAll :: [Int] -> [Int]
      +incrementAll xs = map (\x -> x + 1) xs

      Note that f x = y is the same thing as f = \x -> y.

      Finally, binary operators have sections. Sections are partially applied operators. The section of an operator is obtained by writing the operator and one of its arguments in parentheses. For example, (*2) multiplies its argument by 2 from the right, e.g. (*2) 5 ==> 5 * 2. A fractional number (e.g. a Double) can be inverted with the section (1/), e.g. (1/) 2 ==> 0.5.

      -
      incrementAll' :: [Int] -> [Int]
      -incrementAll' xs = map (+1) xs
      +
      incrementAll' :: [Int] -> [Int]
      +incrementAll' xs = map (+1) xs

      9.3 Functional Programming

      Haskell is a functional programming language, which means that functions can be passed in as arguments and returned from functions. As a programming paradigm, functional programming aims to build programs by combining simple functions together to form larger and larger ones.

      The most often presented example of functional programming is functional list manipulation using higher-order functions (functions that take functions as arguments) like map and filter. Here’s one example from part 1:

      -
      -- a predicate that checks if a string is a palindrome
      -palindrome :: String -> Bool
      -palindrome str = str == reverse str
      -
      --- palindromes n takes all numbers from 1 to n, converts them to
      --- strings using show, and keeps only palindromes
      -palindromes :: Int -> [String]
      -palindromes n = filter palindrome (map show [1..n])
      -
      palindromes 150
      -  ==> ["1","2","3","4","5","6","7","8","9",
      -       "11","22","33","44","55","66","77","88","99",
      -       "101","111","121","131","141"]
      +
      -- a predicate that checks if a string is a palindrome
      +palindrome :: String -> Bool
      +palindrome str = str == reverse str
      +
      +-- palindromes n takes all numbers from 1 to n, converts them to
      +-- strings using show, and keeps only palindromes
      +palindromes :: Int -> [String]
      +palindromes n = filter palindrome (map show [1..n])
      +
      palindromes 150
      +  ==> ["1","2","3","4","5","6","7","8","9",
      +       "11","22","33","44","55","66","77","88","99",
      +       "101","111","121","131","141"]

      We also encountered other functional programming patterns in part 1, like partial application:

      -
      map (take 3) [[1,2,3,4,5],[6,7,8,9,0]]
      -  ==> [[1,2,3],[6,7,8]]
      +
      map (take 3) [[1,2,3,4,5],[6,7,8,9,0]]
      +  ==> [[1,2,3],[6,7,8]]

      Also, function composition:

      -
      (map reverse . filter (/="Smith")) ["Jones","Smith","White"]
      -  ==> ["senoJ","etihW"]
      -map (negate . sum) [[1,2,3],[5]]
      -  ==> [-6,-5]
      +
      (map reverse . filter (/="Smith")) ["Jones","Smith","White"]
      +  ==> ["senoJ","etihW"]
      +map (negate . sum) [[1,2,3],[5]]
      +  ==> [-6,-5]

      And finally, folds:

      -
      foldr (*) 1 [2,3,4]  ==> 24
      -foldr max 0 [1,3,7]  ==> 7
      -foldr (++) "" ["abc","de","f"] ==> "abcdef"
      +
      foldr (*) 1 [2,3,4]  ==> 24
      +foldr max 0 [1,3,7]  ==> 7
      +foldr (++) "" ["abc","de","f"] ==> "abcdef"

      9.4 Recursion

      To implement a function that uses repetition in Haskell, you need recursion. Haskell has no loops like other programming languages. Here are some simple recursive functions in Haskell:

      -
      repeatString :: String -> Int -> String
      -repeatString 0 s = ""
      -repeatString n s = s ++ repeatString (n-1) s
      -
      -times :: Int -> Int -> Int
      -times 0 n = 0
      -times 1 n = n
      -times m n = n + times (m - 1) n
      -
      -safeLast :: [a] -> Maybe a
      -safeLast []     = Nothing
      -safeLast [x]    = Just x
      -safeLast (x:xs) = safeLast xs
      +
      repeatString :: String -> Int -> String
      +repeatString 0 s = ""
      +repeatString n s = s ++ repeatString (n-1) s
      +
      +times :: Int -> Int -> Int
      +times 0 n = 0
      +times 1 n = n
      +times m n = n + times (m - 1) n
      +
      +safeLast :: [a] -> Maybe a
      +safeLast []     = Nothing
      +safeLast [x]    = Just x
      +safeLast (x:xs) = safeLast xs

      To consume or produce a list you often need recursion. Here are the implementations of map and filter as examples of recursive list processing:

      -
      map :: (a -> b) -> [a] -> [b]
      -map _ []     = []
      -map f (x:xs) = f x : map f xs
      -
      -filter :: (a -> Bool) -> [a] -> [a]
      -filter _    []     = []
      -filter pred (x:xs)
      -  | pred x         = x : filter pred xs
      -  | otherwise      = filter pred xs
      +
      map :: (a -> b) -> [a] -> [b]
      +map _ []     = []
      +map f (x:xs) = f x : map f xs
      +
      +filter :: (a -> Bool) -> [a] -> [a]
      +filter _    []     = []
      +filter pred (x:xs)
      +  | pred x         = x : filter pred xs
      +  | otherwise      = filter pred xs

      Sometimes, a recursive helper function is needed in case you need to keep track of multiple pieces of data.

      -
      sumNumbers :: [Int] -> Int
      -sumNumbers xs = go 0 xs
      -  where go sum [] = sum
      -        go sum (x:xs) = go (sum+x) xs
      +
      sumNumbers :: [Int] -> Int
      +sumNumbers xs = go 0 xs
      +  where go sum [] = sum
      +        go sum (x:xs) = go (sum+x) xs

      Here’s a final example utilizing guards, pattern matching, a helper function and recursion:

      -
      -- split a string into pieces at the given character
      -mySplit :: Char -> String -> [String]
      -mySplit c xs = helper [] xs
      -  where helper piece [] = [piece]
      -        helper piece (y:ys)
      -          | c == y    = piece : helper [] ys
      -          | otherwise = helper (piece++[y]) ys
      -
      mySplit '-' "a-bcd-ef"  ==>  ["a","bcd","ef"]
      +
      -- split a string into pieces at the given character
      +mySplit :: Char -> String -> [String]
      +mySplit c xs = helper [] xs
      +  where helper piece [] = [piece]
      +        helper piece (y:ys)
      +          | c == y    = piece : helper [] ys
      +          | otherwise = helper (piece++[y]) ys
      +
      mySplit '-' "a-bcd-ef"  ==>  ["a","bcd","ef"]

      9.5 Type Classes

      The following functions are parametrically polymorphic:

      -
      id :: a -> a
      -id x = x
      -
      -head :: [a] -> a
      -head (x:_) = x
      -
      -fst :: (a,b) -> a
      -fst (x,y) = x
      +
      id :: a -> a
      +id x = x
      +
      +head :: [a] -> a
      +head (x:_) = x
      +
      +fst :: (a,b) -> a
      +fst (x,y) = x

      Parametrically polymorphic functions always work the same way, no matter what types we’re working with. This means that we can’t define a special implementation of id just for the Int type, or fst for the type (Bool, String).

      By contrast, ad hoc polymorphism allows different types to have different implementations of the same function. Ad hoc polymorphism in Haskell can be achieved by defining a type class and then declaring instances of that type class for various types. Ad hoc polymorphism is a handy way for expressing a common set of operations, even when the implementation of the operations depends on the type they’re acting on.

      Functions that use ad hoc polymorphism have a class constraint in their types. Here are some examples:

      -
      negate :: Num a => a -> a
      -(==) :: Eq a => a -> a -> Bool
      -sort :: Ord a => [a] -> [a]
      +
      negate :: Num a => a -> a
      +(==) :: Eq a => a -> a -> Bool
      +sort :: Ord a => [a] -> [a]

      A type like Num a => a -> a means: for any type X that’s a member of the Num class, this function has type X -> X. In other words, we can invoke negate on any number type, but not on other types:

      -
      Prelude> negate 1
      --1
      -Prelude> negate 1.0
      --1.0
      -Prelude> negate True
      -<interactive>:3:1: error:
      -    • No instance for (Num Bool) arising from a use of ‘negate’
      +
      Prelude> negate 1
      +-1
      +Prelude> negate 1.0
      +-1.0
      +Prelude> negate True
      +<interactive>:3:1: error:
      +    • No instance for (Num Bool) arising from a use of ‘negate’

      Here’s a summary of some useful type classes from the standard library.

      • Comparison @@ -658,62 +659,62 @@ code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warni

    Sometimes, multiple class constraints are needed. For instance here:

    -
    sumTwoSmallest :: (Num a, Ord a) => [a] -> a
    -sumTwoSmallest xs = let (a:b:_) = sort xs
    -                    in a+b
    +
    sumTwoSmallest :: (Num a, Ord a) => [a] -> a
    +sumTwoSmallest xs = let (a:b:_) = sort xs
    +                    in a+b

    Now that we’ve seen some classes and types, let’s look at the syntax of declaring classes and instances. Here are two class definitions:

    -
    class Sized a where
    -  empty :: a        -- a thing with size 0
    -  size :: a -> Int
    -
    -class Eq a where
    -  (==) :: a -> a -> Bool
    +
    class Sized a where
    +  empty :: a        -- a thing with size 0
    +  size :: a -> Int
    +
    +class Eq a where
    +  (==) :: a -> a -> Bool

    Consider the following data structures:

    -
    data Numbers = None | One Int | Two Int Int
    -data IntList = Nil | ListNode Int IntList
    -data Tree a = Leaf | Node a (Tree a) (Tree a)
    +
    data Numbers = None | One Int | Two Int Int
    +data IntList = Nil | ListNode Int IntList
    +data Tree a = Leaf | Node a (Tree a) (Tree a)

    All of these have sizes that we can count, but we need to perform the operation differently:

    -
    instance Sized Numbers where
    -  empty = None
    -  size None      = 0
    -  size (One _)   = 1
    -  size (Two _ _) = 2
    -
    -instance Sized IntList where
    -  empty = Nil
    -  size Nil               = 0
    -  size (ListNode _ list) = 1 + size list
    -
    -instance Sized (Tree a) where
    -  empty = Leaf
    -  size Leaf = 0
    -  size (Node _ left right) = 1 + size left + size right
    +
    instance Sized Numbers where
    +  empty = None
    +  size None      = 0
    +  size (One _)   = 1
    +  size (Two _ _) = 2
    +
    +instance Sized IntList where
    +  empty = Nil
    +  size Nil               = 0
    +  size (ListNode _ list) = 1 + size list
    +
    +instance Sized (Tree a) where
    +  empty = Leaf
    +  size Leaf = 0
    +  size (Node _ left right) = 1 + size left + size right

    We can also easily declare Eq instances for Numbers and IntList:

    -
    instance Eq Numbers where
    -  None      == None       = True
    -  (One x)   == (One y)    = x==y
    -  (Two x y) == (Two z w)  = x==z && y==w
    -  _         == _          = False         -- to handle cases like None == One 1
    -
    -instance Eq IntList where
    -  Nil             == Nil               = True
    -  (ListNode x xs) == (ListNode y ys)   = x == y && xs == ys
    -  _               == _                 = False
    +
    instance Eq Numbers where
    +  None      == None       = True
    +  (One x)   == (One y)    = x==y
    +  (Two x y) == (Two z w)  = x==z && y==w
    +  _         == _          = False         -- to handle cases like None == One 1
    +
    +instance Eq IntList where
    +  Nil             == Nil               = True
    +  (ListNode x xs) == (ListNode y ys)   = x == y && xs == ys
    +  _               == _                 = False

    However, since the Tree datatype is parameterized over the element type a, we need an Eq a instance in order to have an Eq (Tree a) instance. This is achieved by adding a class constraint to the instance declaration. This is called an instance hierarchy.

    -
    instance Eq a => Eq (Tree a) where
    -  Leaf         == Leaf              = True
    -  (Node x l r) == (Node x' l' r')   = x == x' && l == l' && r == r'
    -  _            == _                 = False
    +
    instance Eq a => Eq (Tree a) where
    +  Leaf         == Leaf              = True
    +  (Node x l r) == (Node x' l' r')   = x == x' && l == l' && r == r'
    +  _            == _                 = False

    9.5.1 Deriving

    Some standard type classes, most notably Show, Read, Eq and Ord can be derived, that is, you can ask the compiler to generate automatic instances for you. For example we could have derived all of these classes for our earlier Numbers example.

    -
    data Numbers = None | One Int | Two Int Int
    -  deriving (Show, Read, Eq, Ord)
    -
    None == One 1       ==> False
    -Two 1 2 == Two 1 2  ==> True
    -None < Two 1 2      ==> True
    -Two 1 3 < Two 1 2   ==> False
    -show (Two 1 3)      ==> "Two 1 3"
    +
    data Numbers = None | One Int | Two Int Int
    +  deriving (Show, Read, Eq, Ord)
    +
    None == One 1       ==> False
    +Two 1 2 == Two 1 2  ==> True
    +None < Two 1 2      ==> True
    +Two 1 3 < Two 1 2   ==> False
    +show (Two 1 3)      ==> "Two 1 3"
    @@ -755,7 +756,7 @@ It is a type error.

    Which of these is a value of the following type?

    -
    data T = X Int | Y String String | Z T
    +
    data T = X Int | Y String String | Z T
    1. X "foo" @@ -771,8 +772,8 @@ It is a type error.

    What is the type of this function?

    -
    f (_:Just x:_) = x
    -f _            = False
    +
    f (_:Just x:_) = x
    +f _            = False
    1. Maybe a -> a @@ -788,7 +789,7 @@ It is a type error.

    What is the type of this function?

    -
    f x y = x-y == 0
    +
    f x y = x-y == 0
    1. (Num a, Eq a) => a -> a -> Bool @@ -852,10 +853,10 @@ It is a type error.

      Haskell is a pure functional language. This means that the value f x y is always the same for given x and y. In other words, the values of x and y uniquely determine the value of f x y. This property is also called referential transparency.

      Purity also means that there are no side effects: you can’t have the evaluation of f x y read a line from the user - the line would be different on different invocations of f and would affect the return value, breaking referential transparency! Obviously you need side effects to actually get something done. We’ll get back to how Haskell handles side effects later.

      Haskell is a lazy language. This means that a value is not evaluated if it is not needed. An example illustrates this best. Consider these two functions:

      -
      f x = f x   -- infinite recursion
      -g x y = x
      +
      f x = f x   -- infinite recursion
      +g x y = x

      Evaluating f 1 does not halt due to the infinite recursion. However, this works:

      -
      g 2 (f 1)  ==>  2
      +
      g 2 (f 1)  ==>  2

      Laziness is not a problem because Haskell is pure. Only the result of the function matters, not the side effects. So if the result of a function is not used, we can simply not evaluate it without changing the meaning (semantics) of a program. Well okay, sometimes we get a terminating program instead of one that goes on for ever, but adding laziness never makes a functioning Haskell program break.

      If you’re interested in the theory behind this, check out the Church-Rosser theorem or the Haskell Wiki article Lazy vs. non-strict.

    @@ -870,7 +871,7 @@ int funny(int x) {

    The expression c++ increments the value of c and returns the old value of c. The next time it is evaluated, the value of c has increased by one. This means that depending on the current value of c, funny(0) might return 0, 1, 2, or any other integer value. (It might even return negative values if c overflows!)

    In some situations this kind of behaviour with side-effects may be useful, but there are also times when it is more important to be able to reason about the code easily. The advantage of pure functions is that they can be analyzed using basic mathematical techniques. Sometimes applying math to our functions can even reveal simplifications or optimisations we otherwise wouldn’t have thought about.

    Consider the following expression:

    -
    map (+1) . reverse . map (-1)
    +
    map (+1) . reverse . map (-1)

    This expression can be simplified to just reverse. We begin by establishing some helpful facts (or lemmas). First, suppose that we know

    1. map id === id
    2. @@ -882,48 +883,48 @@ int funny(int x) {
    3. (+1) . (-1) === id

    We can prove fact 4 by reasoning about how (+1) . (-1) behaves for an arbitrary input x:

    -
    ((+1) . (-1)) x === ((+1) ((-1) x))
    -                === ((+1) (x - 1))
    -                === (x - 1) + 1
    -                === x
    -                === id x
    +
    ((+1) . (-1)) x === ((+1) ((-1) x))
    +                === ((+1) (x - 1))
    +                === (x - 1) + 1
    +                === x
    +                === id x

    Because we didn’t assume anything about x, we may conclude that the above chain of equations holds for every x. Thus,

    -
    (+1) . (-1) === id
    +
    (+1) . (-1) === id

    For those who are familiar with the technique of proof by induction, it is a fun exercise to prove the first three facts also. This course doesn’t discuss induction proofs, though, so don’t sweat if you don’t know induction.

    Now, from facts 1-4 it follows that

    -
        map (+1) . reverse . map (-1)
    -=== map (+1) . (reverse . map (-1))    -- By associativity of (.)
    -=== map (+1) . (map (-1) . reverse)    -- By fact 3
    -=== (map (+1) . map (-1)) . reverse    -- By associativity of (.)
    -=== map ((+1) . (-1)) . reverse        -- By fact 2
    -=== map id . reverse                   -- By fact 4
    -=== id . reverse                       -- By fact 1
    -=== reverse                            -- By the definition of id
    +
        map (+1) . reverse . map (-1)
    +=== map (+1) . (reverse . map (-1))    -- By associativity of (.)
    +=== map (+1) . (map (-1) . reverse)    -- By fact 3
    +=== (map (+1) . map (-1)) . reverse    -- By associativity of (.)
    +=== map ((+1) . (-1)) . reverse        -- By fact 2
    +=== map id . reverse                   -- By fact 4
    +=== id . reverse                       -- By fact 1
    +=== reverse                            -- By the definition of id

    This course won’t go into details about proving things about programs, but it’s good to know that pure functional programming is very compatible with analysis like this.

    10.3 Infinite Lists

    The benefits of laziness are best demonstrated with some examples involving infinite lists. Let’s start with repeat 1, which generates an infinite list of 1s. If we try to tell GHCi to print the value repeat 1, it will just keep printing 1s for ever until we interrupt it using Control-C:

    -
    Prelude> repeat 1
    -[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    -^C
    +
    Prelude> repeat 1
    +[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    +^C

    However, due to laziness, we can work with infinite lists and write computations that end. We just need to use a finite number of elements from the infinite list. Here are some examples:

    -
    Prelude> take 10 $ repeat 1
    -[1,1,1,1,1,1,1,1,1,1]
    -Prelude> take 20 $ repeat 1
    -[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
    -Prelude> repeat 1 !! 13337
    -1
    +
    Prelude> take 10 $ repeat 1
    +[1,1,1,1,1,1,1,1,1,1]
    +Prelude> take 20 $ repeat 1
    +[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
    +Prelude> repeat 1 !! 13337
    +1

    An infinite list that just repeats one element can sometimes be necessary, but it’s kind of pointless. Let’s look at some more useful infinite lists next. You can use the [n..] syntax to generate an infinite list of numbers, starting from n:

    -
    Prelude> take 20 [0..]
    -[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
    -Prelude> take 10 . map (2^) $ [0..]
    -[1,2,4,8,16,32,64,128,256,512]
    +
    Prelude> take 20 [0..]
    +[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
    +Prelude> take 10 . map (2^) $ [0..]
    +[1,2,4,8,16,32,64,128,256,512]

    The function cycle repeats elements from the given list over and over again. It can be useful when dealing with rotations or cycles.

    -
    Prelude> take 21 $ cycle "asdf"
    -"asdfasdfasdfasdfasdfa"
    -Prelude> take 4 . map (take 4) . tails $ cycle "asdf"
    -["asdf","sdfa","dfas","fasd"]
    +
    Prelude> take 21 $ cycle "asdf"
    +"asdfasdfasdfasdfasdfa"
    +Prelude> take 4 . map (take 4) . tails $ cycle "asdf"
    +["asdf","sdfa","dfas","fasd"]

    10.3.1 Example: Transaction Numbers

    As a more concrete example of how cycle is useful, let’s look at computing the check digit of Finnish bank transfer transaction numbers (viitenumero). A transaction number consists of any number of digits, followed by a single check digit. The check digit is checked by multiplying the digits (from right to left) with the numbers 7, 3, 1, 7, 3, 1 and so on, and summing the results. If the result of the sum plus the check digit is divisible by 10, the number is valid.

    @@ -934,34 +935,34 @@ multipliers: 3 7 1 3 7 3+ 7+ 6+ 3+14 = 33 check digit is 7, 33+7=40 is divisible by 10, valid

    Here’s the Haskell code for a transaction number checker. Note how we use use the infinite list cycle [7,3,1] for the multipliers.

    -
    viitenumeroCheck :: [Int] -> Bool
    -viitenumeroCheck allDigits = mod (checksum+checkDigit) 10 == 0
    -  where (checkDigit:digits) = reverse allDigits
    -        multipliers = cycle [7,3,1]
    -        checksum = sum $ zipWith (*) multipliers digits
    -
    viitenumeroCheck [1,1,6,1,2,7]  ==> True
    -viitenumeroCheck [1,1,6,1,2,8]  ==> False
    +
    viitenumeroCheck :: [Int] -> Bool
    +viitenumeroCheck allDigits = mod (checksum+checkDigit) 10 == 0
    +  where (checkDigit:digits) = reverse allDigits
    +        multipliers = cycle [7,3,1]
    +        checksum = sum $ zipWith (*) multipliers digits
    +
    viitenumeroCheck [1,1,6,1,2,7]  ==> True
    +viitenumeroCheck [1,1,6,1,2,8]  ==> False

    10.3.2 Example: Finding a Power

    Finally, here’s how you would find the first power of 3 that’s larger than 100.

    -
    Prelude> head . filter (>100) $ map (3^) [0..]
    -243
    +
    Prelude> head . filter (>100) $ map (3^) [0..]
    +243

    Let’s go through how this works step by step. Note how map and filter are processing the list lazily, one element at a time, as needed. This is similar to how generators or iterators work in languages like Python or Java.

    -
        head (filter (>100) (map (3^) [0..]))
    -==> head (filter (>100) (map (3^) (0:[1..])))   -- evaluate first element of the lazy list
    -==> head (filter (>100) (1 : map (3^) [1..]))   -- map processes the element
    -==> head (filter (>100) (map (3^) [1..]))       -- filter drops the element
    -==> head (filter (>100) (map (3^) (1:[2..])))   -- evaluate second element of the lazy list
    -==> head (filter (>100) (3 : map (3^) [2..]))   -- map processes the element
    -==> head (filter (>100) (map (3^) [2..]))       -- filter drops the element
    --- let's take bigger steps now
    -==> head (filter (>100) (9 : map (3^) [3..]))   -- map processes, filter will drop
    -==> head (filter (>100) (27 : map (3^) [4..]))  -- map processes, filter will drop
    -==> head (filter (>100) (81 : map (3^) [5..]))  -- map processes, filter will drop
    -==> head (filter (>100) (243 : map (3^) [6..])) -- map processes
    -==> head (243 : filter (>100) (map (3^) [6..])) -- filter lets the value through
    -==> 243                                         -- head returns the result
    +
        head (filter (>100) (map (3^) [0..]))
    +==> head (filter (>100) (map (3^) (0:[1..])))   -- evaluate first element of the lazy list
    +==> head (filter (>100) (1 : map (3^) [1..]))   -- map processes the element
    +==> head (filter (>100) (map (3^) [1..]))       -- filter drops the element
    +==> head (filter (>100) (map (3^) (1:[2..])))   -- evaluate second element of the lazy list
    +==> head (filter (>100) (3 : map (3^) [2..]))   -- map processes the element
    +==> head (filter (>100) (map (3^) [2..]))       -- filter drops the element
    +-- let's take bigger steps now
    +==> head (filter (>100) (9 : map (3^) [3..]))   -- map processes, filter will drop
    +==> head (filter (>100) (27 : map (3^) [4..]))  -- map processes, filter will drop
    +==> head (filter (>100) (81 : map (3^) [5..]))  -- map processes, filter will drop
    +==> head (filter (>100) (243 : map (3^) [6..])) -- map processes
    +==> head (243 : filter (>100) (map (3^) [6..])) -- filter lets the value through
    +==> 243                                         -- head returns the result
    @@ -969,68 +970,68 @@ check digit is 7, 33+7=40 is divisible by 10, valid

    Laziness will probably feel a bit magical to you right now. You might wonder how it can be implemented. Haskell evaluation is remarkably simple, it’s just different than what you might be used to. Let’s dig in.

    In most other programming languages (like Java, C or Python), evaluation proceeds inside-out. Arguments to functions are evaluated before the function.

    Haskell evaluation proceeds outside-in instead of inside-out. The definition of the outermost function in an expression is applied without evaluating any arguments. Here’s a concrete example with toy functions f and g:

    -
    g :: Int -> Int -> Int
    -g x y = y+1
    -f :: Int -> Int -> Int -> Int
    -f a b c = g (a*1000) c
    +
    g :: Int -> Int -> Int
    +g x y = y+1
    +f :: Int -> Int -> Int -> Int
    +f a b c = g (a*1000) c

    Inside-out (normal) evaluation:

    -
    f 1 (1234*1234) 2
    -  -- evaluate arguments to f
    -  ==> f 1 1522756 2
    -  -- evaluate f
    -  ==> g (1*1000) 2
    -  -- evaluate arguments to g
    -  ==> g 1000 2
    -  -- evaluate g
    -  ==> 2+1
    -  ==> 3
    +
    f 1 (1234*1234) 2
    +  -- evaluate arguments to f
    +  ==> f 1 1522756 2
    +  -- evaluate f
    +  ==> g (1*1000) 2
    +  -- evaluate arguments to g
    +  ==> g 1000 2
    +  -- evaluate g
    +  ==> 2+1
    +  ==> 3

    Haskell outside-in evaluation:

    -
    f 1 (1234*1234) 2
    -  -- evaluate f without evaluating arguments
    -  ==> g (1*1000) 2
    -  -- evaluate g without evaluating arguments
    -  ==> 2+1
    -  ==> 3
    +
    f 1 (1234*1234) 2
    +  -- evaluate f without evaluating arguments
    +  ==> g (1*1000) 2
    +  -- evaluate g without evaluating arguments
    +  ==> 2+1
    +  ==> 3

    Note how the unused calculations 1234*1234 and 1*1000 didn’t get evaluated. This is why laziness is often helpful.

    10.4.1 Pattern Matching Drives Evaluation

    Let’s look at a more involved example, with pattern matching and more complex data (lists). Pattern matching drives Haskell evaluation in a very concrete way, as we’ll see. Here are some functions that we’ll use. They’re familiar from the Prelude, but I’ll give them simple definitions.

    -
    not True = False
    -not False = True
    -map f [] = []
    -map f (x:xs) = f x : map f xs
    -length [] = 0
    -length (x:xs) = 1+length xs
    +
    not True = False
    +not False = True
    +map f [] = []
    +map f (x:xs) = f x : map f xs
    +length [] = 0
    +length (x:xs) = 1+length xs

    Here’s the inside-out evaluation of an expression:

    -
    length (map not (True:False:[]))
    -  ==> length (not True : not False : [])  -- evaluate call to map
    -  ==> length (False:True:[])              -- evaluate calls to not
    -  ==> 2
    +
    length (map not (True:False:[]))
    +  ==> length (not True : not False : [])  -- evaluate call to map
    +  ==> length (False:True:[])              -- evaluate calls to not
    +  ==> 2

    Here’s how the evaluation proceeds in Haskell. Note how it’s not strictly outside-in, since we sometimes need to evaluate inside arguments to be able to know which pattern is matched.

    -
    length (map not (True:False:[]))
    -  -- We can't evaluate length since we don't know which equation of length applies,
    -  -- so we look at length's argument. We can apply the second equation of map, so we do.
    -  ==> length (not True : map not (False:[]))
    -  -- Now the argument of length has a (:) we can pattern match on, so we apply the
    -  -- second equation of length
    -  ==> 1 + length (map not (False:[]))
    -  -- The outermost function is now +, but it can't do anything unless both arguments
    -  -- are numbers. So we need to evaluate length. In order to pick an equation, we need
    -  -- to evaluate the argument of length again. We apply the second equation of map
    -  ==> 1 + length (not False : map not ([]))
    -  -- Now we can apply the second equation of length again.
    -  ==> 1 + (1 + length (map not []))
    -  -- The outermost + needs a number to be evaluated. The second + also needs a number.
    -  -- We need to evaluate length again, which means we need to pick an equation for length,
    -  -- which means we need to evaluate its argument. This time it is the first equation for
    -  -- map that applies.
    -  ==> 1 + (1 + length [])
    -  -- Now we can apply the first equation for length
    -  ==> 1 + (1 + 0)
    -  -- The outermost + still can't be evaluated, but the inner one can
    -  ==> 1 + 1
    -  -- Finally we evaluate the outer +
    -  ==> 2
    +
    length (map not (True:False:[]))
    +  -- We can't evaluate length since we don't know which equation of length applies,
    +  -- so we look at length's argument. We can apply the second equation of map, so we do.
    +  ==> length (not True : map not (False:[]))
    +  -- Now the argument of length has a (:) we can pattern match on, so we apply the
    +  -- second equation of length
    +  ==> 1 + length (map not (False:[]))
    +  -- The outermost function is now +, but it can't do anything unless both arguments
    +  -- are numbers. So we need to evaluate length. In order to pick an equation, we need
    +  -- to evaluate the argument of length again. We apply the second equation of map
    +  ==> 1 + length (not False : map not ([]))
    +  -- Now we can apply the second equation of length again.
    +  ==> 1 + (1 + length (map not []))
    +  -- The outermost + needs a number to be evaluated. The second + also needs a number.
    +  -- We need to evaluate length again, which means we need to pick an equation for length,
    +  -- which means we need to evaluate its argument. This time it is the first equation for
    +  -- map that applies.
    +  ==> 1 + (1 + length [])
    +  -- Now we can apply the first equation for length
    +  ==> 1 + (1 + 0)
    +  -- The outermost + still can't be evaluated, but the inner one can
    +  ==> 1 + 1
    +  -- Finally we evaluate the outer +
    +  ==> 2

    Note that we didn’t need to evaluate any of the not applications.

    Let’s introduce some terminology. We say that pattern matching forces evaluation. When Haskell evaluates something, it evaluates it to something called weak head normal form (WHNF). WHNF basically means a value that can be pattern matched on. An expression is in WHNF if it can’t be evaluated on its top level. This means it either:

    @@ -1197,276 +1198,276 @@ square ==> * ==> * ==> 16

    10.5 Working with Infinite Lists

    Functions that work with lists often have the best performance when they’re written in such a way that they utilize laziness. One way to try to accomplish this is to write list-handling functions that work well with infinite lists.

    To write a function that transforms an infinite list, you need to write a function that only looks at a limited prefix of the input list, then outputs a (:) constructor, and then recurses. Here’s a first example.

    -
    everySecond :: [a] -> [a]
    -everySecond [] = []
    -everySecond (x:y:xs) = x : everySecond xs
    -
    take 10 (everySecond [0..])  ==>  [0,2,4,6,8,10,12,14,16,18]
    +
    everySecond :: [a] -> [a]
    +everySecond [] = []
    +everySecond (x:y:xs) = x : everySecond xs
    +
    take 10 (everySecond [0..])  ==>  [0,2,4,6,8,10,12,14,16,18]

    A good heuristic for writing functions that work well with infinite lists is: can the head of the result be evaluated cheaply? Here are two examples of functions that don’t work with infinite inputs. In the case of mapTailRecursive, the problem is that it needs to process the whole input before being in WHNF. In the case of myDrop, the problem is that it uses the function length, doesn’t work for infinite lists since it tries to iterate until the end of the list.

    -
    map :: (a -> b) -> [a] -> [b]
    -map _ []     = []
    -map f (x:xs) = f x : map f xs
    -
    -mapTailRecursive :: (a -> b) -> [a] -> [b]
    -mapTailRecursive f xs = go xs []
    -    where go (x:xs) res = go xs (res++[f x])
    -          go []     res = res
    -
    head (map inc [0..]) ==> head (inc 0 : map inc [1..]) ==> inc 0 ==> 1
    -head (mapTailRecursive inc [0..])
    -  ==> head (go [0..] [])
    -  ==> head (go [1..] ([]++[inc 0]))
    -  ==> head (go [2..] ([]++[inc 0]++[inc 1]))
    -  ==> head (go [3..] ([]++[inc 0]++[inc 1]++[inc 2]))
    -  --  never terminates
    -
    drop :: Int -> [a] -> [a]
    -drop 0 xs = xs
    -drop _ [] = []
    -drop n (x:xs) = drop (n-1) xs
    -
    -myDrop :: Int -> [a] -> [a]
    -myDrop 0 xs = xs
    -myDrop n xs = if n > length xs then [] else myDrop (n-1) (tail xs)
    -
    head (drop 2 [0..]) ==> head (drop 1 [1..]) ==> head (drop 0 [2..]) ==> head [2..] ==> 2
    -head (myDrop 2 [0..])
    -  ==> head (if n > length [0..] then [] else myDrop (n-1) (tail [0..]))
    -  ==> head (if n > 1+length [1..] then [] else myDrop (n-1) (tail [0..]))
    -  ==> head (if n > 1+1+length [2..] then [] else myDrop (n-1) (tail [0..]))
    -  ==> head (if n > 1+1+1+length [3..] then [] else myDrop (n-1) (tail [0..]))
    -  --  never terminates
    +
    map :: (a -> b) -> [a] -> [b]
    +map _ []     = []
    +map f (x:xs) = f x : map f xs
    +
    +mapTailRecursive :: (a -> b) -> [a] -> [b]
    +mapTailRecursive f xs = go xs []
    +    where go (x:xs) res = go xs (res++[f x])
    +          go []     res = res
    +
    head (map inc [0..]) ==> head (inc 0 : map inc [1..]) ==> inc 0 ==> 1
    +head (mapTailRecursive inc [0..])
    +  ==> head (go [0..] [])
    +  ==> head (go [1..] ([]++[inc 0]))
    +  ==> head (go [2..] ([]++[inc 0]++[inc 1]))
    +  ==> head (go [3..] ([]++[inc 0]++[inc 1]++[inc 2]))
    +  --  never terminates
    +
    drop :: Int -> [a] -> [a]
    +drop 0 xs = xs
    +drop _ [] = []
    +drop n (x:xs) = drop (n-1) xs
    +
    +myDrop :: Int -> [a] -> [a]
    +myDrop 0 xs = xs
    +myDrop n xs = if n > length xs then [] else myDrop (n-1) (tail xs)
    +
    head (drop 2 [0..]) ==> head (drop 1 [1..]) ==> head (drop 0 [2..]) ==> head [2..] ==> 2
    +head (myDrop 2 [0..])
    +  ==> head (if n > length [0..] then [] else myDrop (n-1) (tail [0..]))
    +  ==> head (if n > 1+length [1..] then [] else myDrop (n-1) (tail [0..]))
    +  ==> head (if n > 1+1+length [2..] then [] else myDrop (n-1) (tail [0..]))
    +  ==> head (if n > 1+1+1+length [3..] then [] else myDrop (n-1) (tail [0..]))
    +  --  never terminates

    Pretty much all the list functions in the standard library are written in this form, for example:

    -
    head (takeWhile (>=0) [0..]) ==> 0
    -head (concat (repeat [1,2,3])) ==> 1
    -head (zip [0..] [2..]) ==> (0,2)
    -head (filter even [3..]) ==> 4
    +
    head (takeWhile (>=0) [0..]) ==> 0
    +head (concat (repeat [1,2,3])) ==> 1
    +head (zip [0..] [2..]) ==> (0,2)
    +head (filter even [3..]) ==> 4

    10.6 Interlude: Adding Strictness

    Remember foldr from part 1? Let’s have a look at its cousin foldl. Here’s the definition of foldl for lists (it’s actually part of the Foldable typeclass and so works for various other types too). While foldr processes a list right-to-left, foldl processes a list left-to-right. To be a bit more exact, foldr associates to the right while foldl associates to the left. Note the difference in the next example:

    -
    foldr (+) 0 [1,2,3]  ==>  1+(2+(3+0))
    -foldl (+) 0 [1,2,3]  ==>  ((0+1)+2)+3
    +
    foldr (+) 0 [1,2,3]  ==>  1+(2+(3+0))
    +foldl (+) 0 [1,2,3]  ==>  ((0+1)+2)+3

    Here are the definitions of foldl and foldr:

    -
    foldl :: (a -> b -> a) -> a -> [b] -> a
    -foldl f z [] = z
    -foldl f z (x:xs) = foldl f (f z x) xs
    -
    foldr :: (a -> b -> b) -> b -> [a] -> b
    -foldr f y []     = y
    -foldr f y (x:xs) = f x (foldr f y xs)
    +
    foldl :: (a -> b -> a) -> a -> [b] -> a
    +foldl f z [] = z
    +foldl f z (x:xs) = foldl f (f z x) xs
    +
    foldr :: (a -> b -> b) -> b -> [a] -> b
    +foldr f y []     = y
    +foldr f y (x:xs) = f x (foldr f y xs)

    As foldr f y (x:xs) ==> f x (foldr f y xs), it enables lazy evaluation to focus on f on the second step. Hence, foldr works nicely with lazy or short-circuiting operations:

    -
        foldr (&&) True [False,False,False]
    -==> False && (foldr (&&) True [False,False])
    -==> False
    -
        head (foldr (++) [] ["Hello","World","lorem","ipsum"])
    -==> head ("Hello" ++ (foldr (++) [] ["World","lorem","ipsum"]))
    -==> head ('H':("ello" ++ (foldr (++) [] ["World","lorem","ipsum"])))
    -==> 'H'
    +
        foldr (&&) True [False,False,False]
    +==> False && (foldr (&&) True [False,False])
    +==> False
    +
        head (foldr (++) [] ["Hello","World","lorem","ipsum"])
    +==> head ("Hello" ++ (foldr (++) [] ["World","lorem","ipsum"]))
    +==> head ('H':("ello" ++ (foldr (++) [] ["World","lorem","ipsum"])))
    +==> 'H'

    However foldl needs to process the whole list in order to produce a (WHNF) value. The reason is that foldl remains in the leftmost-outermost position for as long as its list argument remains non-empty. This makes foldl the priority for lazy evaluation. Only after the list becomes empty does the evaluation proceed into simplifying the folded values.

    -
        foldl (&&) True [False,False,False]
    -==> foldl (&&) (True&&False) [False,False]
    -==> foldl (&&) ((True&&False)&&False) [False]
    -==> foldl (&&) (((True&&False)&&False)&&False) []
    -==> ((True&&False)&&False)&&False
    -==> (    False    &&False)&&False
    -==>              False    &&False
    -==>                      False
    -
        head (foldl (++) [] ["Hello","World","lorem","ipsum"])
    -==> head (foldl (++) ([]++"Hello") ["World","lorem","ipsum"])
    -==> head (foldl (++) (([]++"Hello")++"World") ["lorem","ipsum"])
    -==> head (foldl (++) ((([]++"Hello")++"World")++"lorem") ["ipsum"])
    -==> head (foldl (++) (((([]++"Hello")++"World")++"lorem")++"ipsum") [])
    -==> head (((([]++"Hello")++"World")++"lorem")++"ipsum")
    --- head forces the last ++, which forces the next-to-last ++, and so on
    -==> head ((("Hello"++"World")++"lorem")++"ipsum")
    --- same happens again
    -==> head ((('H':("ello"++"World"))++"lorem")++"ipsum")
    --- for clarity, let's drop the "ello"++"World" expression which isn't needed
    -==> head ((('H':__)++"lorem")++"ipsum")
    --- now the next-to-last ++ can operate
    -==> head (('H':(__++"lorem"))++"ipsum")
    --- let's drop the __++"lorem" expression
    -==> head (('H':__)++"ipsum")
    --- now the last ++ can operate
    -==> head ('H':(__++"ipsum"))
    -==> 'H'
    +
        foldl (&&) True [False,False,False]
    +==> foldl (&&) (True&&False) [False,False]
    +==> foldl (&&) ((True&&False)&&False) [False]
    +==> foldl (&&) (((True&&False)&&False)&&False) []
    +==> ((True&&False)&&False)&&False
    +==> (    False    &&False)&&False
    +==>              False    &&False
    +==>                      False
    +
        head (foldl (++) [] ["Hello","World","lorem","ipsum"])
    +==> head (foldl (++) ([]++"Hello") ["World","lorem","ipsum"])
    +==> head (foldl (++) (([]++"Hello")++"World") ["lorem","ipsum"])
    +==> head (foldl (++) ((([]++"Hello")++"World")++"lorem") ["ipsum"])
    +==> head (foldl (++) (((([]++"Hello")++"World")++"lorem")++"ipsum") [])
    +==> head (((([]++"Hello")++"World")++"lorem")++"ipsum")
    +-- head forces the last ++, which forces the next-to-last ++, and so on
    +==> head ((("Hello"++"World")++"lorem")++"ipsum")
    +-- same happens again
    +==> head ((('H':("ello"++"World"))++"lorem")++"ipsum")
    +-- for clarity, let's drop the "ello"++"World" expression which isn't needed
    +==> head ((('H':__)++"lorem")++"ipsum")
    +-- now the next-to-last ++ can operate
    +==> head (('H':(__++"lorem"))++"ipsum")
    +-- let's drop the __++"lorem" expression
    +==> head (('H':__)++"ipsum")
    +-- now the last ++ can operate
    +==> head ('H':(__++"ipsum"))
    +==> 'H'

    So why use foldl at all? Let’s return to our first fold example again. Now, since + is a strict operation, both types of fold need to build up an expression with lots of +s. The Haskell implementation needs to track this expression in memory, which is why a problem like this is called a space leak.

    -
        foldr (+) 0 [1,2,3]
    -==> 1 + foldr (+) 0 [2,3]
    -==> 1 + (2 + foldr (+) 0 [3])
    -==> 1 + (2 + (3 + foldr (+) 0 []))
    -==> 1 + (2 + (3 + 0))
    -==> 1 + (2 + 3)
    -==> 1 + 5
    -==> 6
    -
        foldl (+) 0 [1,2,3]
    -==> foldl (+) (0+1) [2,3]
    -==> foldl (+) ((0+1)+2) [3]
    -==> foldl (+) (((0+1)+2)+3) []
    -==> ((0+1)+2)+3
    -==> (  1  +2)+3
    -==>       3  +3
    -==>          6
    +
        foldr (+) 0 [1,2,3]
    +==> 1 + foldr (+) 0 [2,3]
    +==> 1 + (2 + foldr (+) 0 [3])
    +==> 1 + (2 + (3 + foldr (+) 0 []))
    +==> 1 + (2 + (3 + 0))
    +==> 1 + (2 + 3)
    +==> 1 + 5
    +==> 6
    +
        foldl (+) 0 [1,2,3]
    +==> foldl (+) (0+1) [2,3]
    +==> foldl (+) ((0+1)+2) [3]
    +==> foldl (+) (((0+1)+2)+3) []
    +==> ((0+1)+2)+3
    +==> (  1  +2)+3
    +==>       3  +3
    +==>          6

    Now let’s instead look at what happens when we use foldl', a version of foldl that forces its second argument!

    -
        foldl' (+) 0 [1,2,3]
    -==> foldl' (+) (0+1) [2,3]
    --- force second argument
    -==> foldl' (+) 1 [2,3]
    -==> foldl' (+) (1+2) [3]
    --- force second argument
    -==> foldl' (+) 3 [3]
    -==> foldl' (+) (3+3) []
    --- force second argument
    -==> foldl' (+) 6 []
    -==> 6
    +
        foldl' (+) 0 [1,2,3]
    +==> foldl' (+) (0+1) [2,3]
    +-- force second argument
    +==> foldl' (+) 1 [2,3]
    +==> foldl' (+) (1+2) [3]
    +-- force second argument
    +==> foldl' (+) 3 [3]
    +==> foldl' (+) (3+3) []
    +-- force second argument
    +==> foldl' (+) 6 []
    +==> 6

    Now the work is performed incrementally while scanning the list. No space leak! Sometimes too much laziness can cause space leaks, and a bit of strictness can fix them.

    You can find foldl' in the Data.List module, and it works just like this. But how could one implement foldl'? We certainly know by now how to do it for a specific type, say Int. We just add a pattern match on the second argument that doesn’t change the semantics of the function.

    -
    foldl'Int :: (Int -> Int -> Int) -> Int -> [Int] -> Int
    -foldl'Int f z [] = z
    -foldl'Int f 0 (x:xs) = foldl'Int f (f 0 x) xs
    -foldl'Int f z (x:xs) = foldl'Int f (f z x) xs
    -
        foldl'Int (+) 0 [1,2,3]
    -==> foldl'Int (+) (0+1) [2,3]
    --- to be able to pick between the second and third equations, (0+1) is forced
    -==> foldl'Int (+) 1 [2,3]
    --- the third equation applies
    -==> foldl'Int (+) (1+2) [3]
    --- again, we need to pick between the second and third equations
    -==> foldl'Int (+) 3 [3]
    -==> foldl'Int (+) (3+3) []
    -==> 3+3
    -==> 6
    +
    foldl'Int :: (Int -> Int -> Int) -> Int -> [Int] -> Int
    +foldl'Int f z [] = z
    +foldl'Int f 0 (x:xs) = foldl'Int f (f 0 x) xs
    +foldl'Int f z (x:xs) = foldl'Int f (f z x) xs
    +
        foldl'Int (+) 0 [1,2,3]
    +==> foldl'Int (+) (0+1) [2,3]
    +-- to be able to pick between the second and third equations, (0+1) is forced
    +==> foldl'Int (+) 1 [2,3]
    +-- the third equation applies
    +==> foldl'Int (+) (1+2) [3]
    +-- again, we need to pick between the second and third equations
    +==> foldl'Int (+) 3 [3]
    +==> foldl'Int (+) (3+3) []
    +==> 3+3
    +==> 6

    To write a generic implementation of foldl' we need to introduce a new built-in function, seq. The call seq a b evaluates to b but forces a into WHNF. Here are some examples of using seq in GHCi. To demonstrate what gets evaluated, we use the special value undefined, which causes an error if something tries to evaluate it into WHNF.

    -
    Prelude> seq (not True) 3
    -3
    -Prelude> seq undefined 3
    -*** Exception: Prelude.undefined
    -Prelude> (seq (not True) 3) + 7
    -10
    -Prelude> (seq undefined 3) + 7
    -*** Exception: Prelude.undefined
    -Prelude> let f x = f x in seq (f 3) 3
    --- ...infinite recursion
    +
    Prelude> seq (not True) 3
    +3
    +Prelude> seq undefined 3
    +*** Exception: Prelude.undefined
    +Prelude> (seq (not True) 3) + 7
    +10
    +Prelude> (seq undefined 3) + 7
    +*** Exception: Prelude.undefined
    +Prelude> let f x = f x in seq (f 3) 3
    +-- ...infinite recursion

    As an example of using seq in a function, here’s a version of head that doesn’t work for infinite lists (since it evaluates the last element of the list):

    -
    strictHead :: [a] -> a
    -strictHead xs = seq (last xs) (head xs)
    +
    strictHead :: [a] -> a
    +strictHead xs = seq (last xs) (head xs)

    Let’s play around with it in GHCi:

    -
    Prelude> head [1,2,3]
    -1
    -Prelude> strictHead [1,2,3]
    -1
    -Prelude> head (1:2:undefined)
    -1
    -Prelude> strictHead (1:2:undefined)
    -*** Exception: Prelude.undefined
    -Prelude> head [1..]
    -1
    -Prelude> strictHead [1..]
    --- ...infinite recursion
    +
    Prelude> head [1,2,3]
    +1
    +Prelude> strictHead [1,2,3]
    +1
    +Prelude> head (1:2:undefined)
    +1
    +Prelude> strictHead (1:2:undefined)
    +*** Exception: Prelude.undefined
    +Prelude> head [1..]
    +1
    +Prelude> strictHead [1..]
    +-- ...infinite recursion

    Finally, here’s a definition for foldl'. Note how we need to introduce sharing of a new variable, z', to be able to make seq evaluate the new value and then use it in a recursive call. The new definition is also used in a more detailed evaluation of foldl' (+) 0 [1,2,3] below.

    -
    foldl' :: (a -> b -> a) -> a -> [b] -> a
    -foldl' f z [] = z
    -foldl' f z (x:xs) = let z' = f z x
    -                    in seq z' (foldl f z' xs)
    -
        foldl' (+) 0 [1,2,3]
    -==> seq (0+1) (foldl' (+) (0+1) [2,3])  -- seq forces first argument
    -          |                 |
    -          +-----sharing-----'
    -          |                 |
    -==> seq   1   (foldl' (+)   1   [2,3])  -- first argument to seq in WHNF, seq disappears
    -==> foldl' (+) 1 [2,3]
    -==> seq (1+2) (foldl' (+) (1+2) [3])
    -==> seq   3   (foldl' (+)   3   [3])
    -==> foldl' (+)   3   [3]
    -==> seq (3+3) (foldl' (+) (3+3) [])
    -==> seq   6   (foldl' (+)   6   [])
    -==> foldl' (+)   6   []
    -==> 6
    +
    foldl' :: (a -> b -> a) -> a -> [b] -> a
    +foldl' f z [] = z
    +foldl' f z (x:xs) = let z' = f z x
    +                    in seq z' (foldl f z' xs)
    +
        foldl' (+) 0 [1,2,3]
    +==> seq (0+1) (foldl' (+) (0+1) [2,3])  -- seq forces first argument
    +          |                 |
    +          +-----sharing-----'
    +          |                 |
    +==> seq   1   (foldl' (+)   1   [2,3])  -- first argument to seq in WHNF, seq disappears
    +==> foldl' (+) 1 [2,3]
    +==> seq (1+2) (foldl' (+) (1+2) [3])
    +==> seq   3   (foldl' (+)   3   [3])
    +==> foldl' (+)   3   [3]
    +==> seq (3+3) (foldl' (+) (3+3) [])
    +==> seq   6   (foldl' (+)   6   [])
    +==> foldl' (+)   6   []
    +==> 6

    We won’t dive deeper into this subject on this course, but it’s important that you’re aware that seq exists. You can find more about seq on the Haskell Wiki and learn more about when it is necessary to add strictness in Real World Haskell. Often it’s nicer to use bang patterns instead of seq, as discussed by FPComplete and Real World Haskell.

    10.7 Newtype Declarations

    Recall lecture 7. Sometimes we need boxed types. There’s a special keyword newtype that can be used instead of data when a boxed type is needed. newtype expects exactly one constructor, with exactly one field. For instance,

    -
    newtype Money = Cents Int
    +
    newtype Money = Cents Int

    However, the following won’t work, you need data:

    -
    -- the compiler won't accept these!
    -newtype Currency = Dollars Int | Euros Int
    -newtype Money = Money Int Int
    +
    -- the compiler won't accept these!
    +newtype Currency = Dollars Int | Euros Int
    +newtype Money = Money Int Int

    So what’s the difference? In terms of writing code, nothing. You work with a newtype exactly as you would with a data. However, the memory layout is different. Using data introduces an indirection layer (the constructor), but using newtype doesn’t. The indirection for data is necessary to support multiple constructors and multiple fields. An illustration:

    -
    code:                                 memory:
    -
    -data Money = Cents Int                x --> Cents --> 100
    -x = Cents 100
    -
    -
    -newtype Money = Cents Int             x --> 100
    -x = Cents 100
    +
    code:                                 memory:
    +
    +data Money = Cents Int                x --> Cents --> 100
    +x = Cents 100
    +
    +
    +newtype Money = Cents Int             x --> 100
    +x = Cents 100

    This difference has many repercussions. First of all, newtype is more efficient: the type can be said to “disappear” when compiling. The type is still checked though, so you get type safety without any performance impact. Secondly, newtypes are strict. Concretely, this means that Money x is in weak head normal form only if x is in WHNF. This can be witnessed in GHCi:

    -
    -- if we use data, Cents undefined is in WHNF
    -Prelude> data Money = Cents Int
    -Prelude> seq (Cents undefined) True
    -True
    --- if we use newtype, Cents undefined isn't in WHNF, and trying
    --- to make it so trips up in undefined
    -Prelude> newtype Money = Cents Int
    -Prelude> seq (Cents undefined) True
    -*** Exception: Prelude.undefined
    +
    -- if we use data, Cents undefined is in WHNF
    +Prelude> data Money = Cents Int
    +Prelude> seq (Cents undefined) True
    +True
    +-- if we use newtype, Cents undefined isn't in WHNF, and trying
    +-- to make it so trips up in undefined
    +Prelude> newtype Money = Cents Int
    +Prelude> seq (Cents undefined) True
    +*** Exception: Prelude.undefined

    So when should you use newtype? In general it’s best to use newtype whenever you have a single-field single-constructor datatype. However nothing will go catastrophically wrong if you always use data. The newtype pattern is also often used when you need to define a different type class instance for a type. Here’s an example that defines a number type with an inverted ordering

    -
    newtype Inverted = Inverted Int
    -  deriving (Show, Eq)
    -
    -instance Ord Inverted where
    -  compare (Inverted i) (Inverted j) = compare j i
    -
    Prelude Data.List> sort [1,2,3]
    -[1,2,3]
    -Prelude Data.List> sort [Inverted 1,Inverted 2,Inverted 3]
    -[Inverted 3,Inverted 2,Inverted 1]
    +
    newtype Inverted = Inverted Int
    +  deriving (Show, Eq)
    +
    +instance Ord Inverted where
    +  compare (Inverted i) (Inverted j) = compare j i
    +
    Prelude Data.List> sort [1,2,3]
    +[1,2,3]
    +Prelude Data.List> sort [Inverted 1,Inverted 2,Inverted 3]
    +[Inverted 3,Inverted 2,Inverted 1]

    10.8 Something Fun: Tying the Knot

    Now that we know about sharing and path copying, we can make our own cyclic datastructures. Remember the cycle examples from the list lecture?

    -
    Prelude> take 21 $ cycle "asdf"
    -"asdfasdfasdfasdfasdfa"
    +
    Prelude> take 21 $ cycle "asdf"
    +"asdfasdfasdfasdfasdfa"

    This is what it looks like in memory:

    Earlier it was said that Haskell data forms directed graphs in memory. This is an example of a directed graph with a cycle.

    How can we define structures like this? We just give a value a name, and refer to that name in the value itself. That is, the value is recursive or self-referential. This trick is known as tying the knot. A simple example:

    -
      code                     memory
    -
    -let xs = 1:2:xs      xs -> (1:) -> (2:) -+
    - in xs                      ^            |
    -                            +------------+
    +
      code                     memory
    +
    +let xs = 1:2:xs      xs -> (1:) -> (2:) -+
    + in xs                      ^            |
    +                            +------------+

    Note how we use the name xs inside the definition of xs. When we make a recursive definition like this, sharing causes it to turn into a cyclic structure in memory.

    A more fun example: a simple adventure game where the world is a self-referential structure. Note how the cyclic structure is built with local definitions that refer to each other.

    -
    data Room = Room String [(String,Room)]
    -
    -describe :: Room -> String
    -describe (Room s _) = s
    -
    -move :: Room -> String -> Maybe Room
    -move (Room _ directions) direction = lookup direction directions
    -
    -world :: Room
    -world = meadow
    -  where
    -    meadow = Room "It's a flowery meadow next to a cliff." [("Stay",meadow),("Enter cave",cave)]
    -    cave = Room "You are in a cave" [("Exit",meadow),("Go deeper",tunnel)]
    -    tunnel = Room "This is a very dark tunnel. It seems you can either go left or right."
    -                  [("Go back",cave),("Go left",pit),("Go right",treasure)]
    -    pit = Room "You fall into a pit. There is no way out." []
    -    treasure = Room "A green light from a terminal fills the room. The terminal says <<loop>>."
    -                    [("Go back",tunnel)]
    -
    -play :: Room -> [String] -> [String]
    -play room [] = [describe room]
    -play room (d:ds) = case move room d of Nothing -> [describe room]
    -                                       Just r -> describe room : play r ds
    -
    Prelude> play world ["Stay","Enter cave","Go deeper","Go back","Go deeper","Go right"]
    -["It's a flowery meadow next to a cliff.",
    - "It's a flowery meadow next to a cliff.",
    - "You are in a cave",
    - "This is a very dark tunnel. It seems you can either go left or right.",
    - "You are in a cave",
    - "This is a very dark tunnel. It seems you can either go left or right.",
    - "A green light from a computer terminal floods the room. The terminal says <<loop>>."]
    +
    data Room = Room String [(String,Room)]
    +
    +describe :: Room -> String
    +describe (Room s _) = s
    +
    +move :: Room -> String -> Maybe Room
    +move (Room _ directions) direction = lookup direction directions
    +
    +world :: Room
    +world = meadow
    +  where
    +    meadow = Room "It's a flowery meadow next to a cliff." [("Stay",meadow),("Enter cave",cave)]
    +    cave = Room "You are in a cave" [("Exit",meadow),("Go deeper",tunnel)]
    +    tunnel = Room "This is a very dark tunnel. It seems you can either go left or right."
    +                  [("Go back",cave),("Go left",pit),("Go right",treasure)]
    +    pit = Room "You fall into a pit. There is no way out." []
    +    treasure = Room "A green light from a terminal fills the room. The terminal says <<loop>>."
    +                    [("Go back",tunnel)]
    +
    +play :: Room -> [String] -> [String]
    +play room [] = [describe room]
    +play room (d:ds) = case move room d of Nothing -> [describe room]
    +                                       Just r -> describe room : play r ds
    +
    Prelude> play world ["Stay","Enter cave","Go deeper","Go back","Go deeper","Go right"]
    +["It's a flowery meadow next to a cliff.",
    + "It's a flowery meadow next to a cliff.",
    + "You are in a cave",
    + "This is a very dark tunnel. It seems you can either go left or right.",
    + "You are in a cave",
    + "This is a very dark tunnel. It seems you can either go left or right.",
    + "A green light from a computer terminal floods the room. The terminal says <<loop>>."]

    Here’s what the world of the game looks like in memory:

                   ,-----------------,
                    v                 |
    @@ -1493,70 +1494,70 @@ pit----->|Room "You fall..." []|    treasure--->|Room "A gr
     

    10.9 Something Fun: Debug.Trace

    Even though Haskell is a pure programming language, we can sometimes gain insights by sprinkling in a bit of impurity.

    We can use the function trace :: String -> a -> a from the module Debug.Trace to peek into Haskell evaluation. The expression trace "message" x is the same as x, but prints message when it is evaluated (forced). We can use trace to witness the laziness of the || operator:

    -
    Prelude> import Debug.Trace
    -Prelude Debug.Trace> trace "a" True
    -a
    -True
    -Prelude Debug.Trace> trace "a" False || trace "b" True
    -a
    -b
    -True
    -Prelude Debug.Trace> trace "a" True || trace "b" True
    -a
    -True
    +
    Prelude> import Debug.Trace
    +Prelude Debug.Trace> trace "a" True
    +a
    +True
    +Prelude Debug.Trace> trace "a" False || trace "b" True
    +a
    +b
    +True
    +Prelude Debug.Trace> trace "a" True || trace "b" True
    +a
    +True

    We can also have a look at when list elements are evaluated. Note how length doesn’t need to evaluate the elements of the list, and sum needs to evaluate all of them. (To be precise, head xs doesn’t actually evaluate the first element of xs, but returns it to GHCi, which evaluates it in order to show it.)

    -
    Prelude Debug.Trace> head [trace "first" 1, trace "second" 2, trace "third" 3]
    -first
    -1
    -Prelude Debug.Trace> last [trace "first" 1, trace "second" 2, trace "third" 3]
    -third
    -3
    -Prelude Debug.Trace> length [trace "first" 1, trace "second" 2, trace "third" 3]
    -3
    -Prelude Debug.Trace> sum [trace "first" 1, trace "second" 2, trace "third" 3]
    -third
    -second
    -first
    -6
    +
    Prelude Debug.Trace> head [trace "first" 1, trace "second" 2, trace "third" 3]
    +first
    +1
    +Prelude Debug.Trace> last [trace "first" 1, trace "second" 2, trace "third" 3]
    +third
    +3
    +Prelude Debug.Trace> length [trace "first" 1, trace "second" 2, trace "third" 3]
    +3
    +Prelude Debug.Trace> sum [trace "first" 1, trace "second" 2, trace "third" 3]
    +third
    +second
    +first
    +6

    Debug.Trace also offers useful variants of trace. A notable one is traceShowId x which prints show x and evaluates to x. Let’s verify the evaluation of our previous head-filter-map example using traceShowId. Note how even though we map traceShowId over the infinite list [0..], only 6 values are actually evaluated. The last 243 is the returned value, not a trace print.

    -
    Prelude Debug.Trace> head (filter (>100) (map (\x -> traceShowId (3^x)) [0..]))
    -1
    -3
    -9
    -27
    -81
    -243
    -243
    +
    Prelude Debug.Trace> head (filter (>100) (map (\x -> traceShowId (3^x)) [0..]))
    +1
    +3
    +9
    +27
    +81
    +243
    +243

    Debug.Trace is especially useful when you have an infinite recursion bug. Here’s an example:

    -
    -- computes sums like 7+5+3+1
    -sumEverySecond :: Int -> Int
    -sumEverySecond 0 = 0
    -sumEverySecond n = n + sumEverySecond (n-2)
    -
    sumEverySecond 6 ==> 12
    -sumEverySecond 7 ==> doesn't terminate
    +
    -- computes sums like 7+5+3+1
    +sumEverySecond :: Int -> Int
    +sumEverySecond 0 = 0
    +sumEverySecond n = n + sumEverySecond (n-2)
    +
    sumEverySecond 6 ==> 12
    +sumEverySecond 7 ==> doesn't terminate

    We can debug this by adding a trace to wrap the whole recursive case.

    -
    sumEverySecond :: Int -> Int
    -sumEverySecond 0 = 0
    -sumEverySecond n = trace ("sumEverySecond "++show n) (n + sumEverySecond (n-2))
    -
    Prelude Debug.Trace> sumEverySecond 6
    -sumEverySecond 6
    -sumEverySecond 4
    -sumEverySecond 2
    -12
    -Prelude Debug.Trace> sumEverySecond 7
    -sumEverySecond 7
    -sumEverySecond 5
    -sumEverySecond 3
    -sumEverySecond 1
    -sumEverySecond -1
    -sumEverySecond -3
    -sumEverySecond -5
    --- and so on
    +
    sumEverySecond :: Int -> Int
    +sumEverySecond 0 = 0
    +sumEverySecond n = trace ("sumEverySecond "++show n) (n + sumEverySecond (n-2))
    +
    Prelude Debug.Trace> sumEverySecond 6
    +sumEverySecond 6
    +sumEverySecond 4
    +sumEverySecond 2
    +12
    +Prelude Debug.Trace> sumEverySecond 7
    +sumEverySecond 7
    +sumEverySecond 5
    +sumEverySecond 3
    +sumEverySecond 1
    +sumEverySecond -1
    +sumEverySecond -3
    +sumEverySecond -5
    +-- and so on

    A ha! The problem is that our recursion base case of sumEverySecond 0 is not enough to stop the recursion.

    Finally, a word of caution. Using trace, and especially traceShowId, can cause things that would not otherwise get evaluated to get evaluated. For example:

    -
    Prelude Debug.Trace> let traceHead xs = head (traceShowId xs)
    -Prelude Debug.Trace> traceHead [0..]
    --- never terminates since it's trying to show an infinite list
    +
    Prelude Debug.Trace> let traceHead xs = head (traceShowId xs)
    +Prelude Debug.Trace> traceHead [0..]
    +-- never terminates since it's trying to show an infinite list

    So feel free to use Debug.Trace when working on the exercises, but try to leave trace calls out of your final answers. Some exercise sets check your imports and disallow Debug.Trace.

    We’ll see a more principled way of dealing with side effects in the next lecture!

    @@ -1590,7 +1591,7 @@ pit----->|Room "You fall..." []| treasure--->|Room "A gr

    What’s the next step when evaluating this expression?

    -
    head (map not (True:False:[]))
    +
    head (map not (True:False:[]))
    1. head (False : True : []) @@ -1621,8 +1622,8 @@ pit----->|Room "You fall..." []| treasure--->|Room "A gr

    Which of these statements about the following function is true?

    -
    f 0 x = 1+x
    -f _ x = 2+x
    +
    f 0 x = 1+x
    +f _ x = 2+x
    1. f is strict in its left argument @@ -1638,8 +1639,8 @@ None of the above

    Does this function work with infinite lists as input? Why?

    -
    f [] = []
    -f (x:xs) = x : map not xs
    +
    f [] = []
    +f (x:xs) = x : map not xs
    1. No, because it includes a [] case, which is never reached. @@ -1655,7 +1656,7 @@ Yes, because it calls map, which works with infinite lists.

    What about this one?

    -
    f xs = map (+(sum xs)) xs
    +
    f xs = map (+(sum xs)) xs
    1. No, because it uses map, which evaluates the whole list. @@ -1691,24 +1692,24 @@ Yes, because it calls map, which works with infinite lists.

      11.2 You’ve Been Fooled!

      Forget what we talked about functional programming and purity. Actually, Haskell is the world’s best imperative programming language! Let’s start:

      -
      questionnaire = do
      -  putStrLn "Write something!"
      -  s <- getLine
      -  putStrLn ("You wrote: "++s)
      -
      Prelude> questionnaire
      -Write something!
      -Haskell!
      -You wrote: Haskell!
      +
      questionnaire = do
      +  putStrLn "Write something!"
      +  s <- getLine
      +  putStrLn ("You wrote: "++s)
      +
      Prelude> questionnaire
      +Write something!
      +Haskell!
      +You wrote: Haskell!

      Reading input and writing output was easy enough. We can also read stuff over the network. Here’s a complete Haskell program that fetches a some words from a URL using HTTP and prints them.

      -
      import Network.HTTP
      -import Control.Monad
      -
      -main = do
      -  rsp <- simpleHTTP (getRequest "http://httpbin.org/base64/aGFza2VsbCBmb3IgZXZlcgo=")
      -  body <- getResponseBody rsp
      -  forM_ (words body) $ \w -> do
      -     putStr "word: "
      -     putStrLn w
      +
      import Network.HTTP
      +import Control.Monad
      +
      +main = do
      +  rsp <- simpleHTTP (getRequest "http://httpbin.org/base64/aGFza2VsbCBmb3IgZXZlcgo=")
      +  body <- getResponseBody rsp
      +  forM_ (words body) $ \w -> do
      +     putStr "word: "
      +     putStrLn w

      You can find this program in the course repository as exercises/Examples/FetchWords.hs, and you can run it like this:

      $ cd exercises/Examples
       $ stack runhaskell FetchWords.hs
      @@ -1716,10 +1717,10 @@ word: haskell
       word: for
       word: ever

      What’s going on here? Let’s look at the types:

      -
      Prelude> :t putStrLn
      -putStrLn :: String -> IO ()
      -Prelude> :t getLine
      -getLine :: IO String
      +
      Prelude> :t putStrLn
      +putStrLn :: String -> IO ()
      +Prelude> :t getLine
      +getLine :: IO String

      A value of type IO a is an operation that produces a value of type a. So getLine is an IO operation that produces a string. The () type is the so called unit type, its only value is (). It’s mostly used when an IO operation doesn’t return anything (but rather just has side effects).

      A comparison with Java (method) types might help:

      @@ -1749,97 +1750,97 @@ word: ever

      IO operations can be combined into bigger operations using do-notation.

      -
      do operation
      -   operation arg
      -   variable <- operationThatReturnsStuff
      -   let var2 = expression
      -   operationThatProducesTheResult var2
      +
      do operation
      +   operation arg
      +   variable <- operationThatReturnsStuff
      +   let var2 = expression
      +   operationThatProducesTheResult var2

      11.2.1 Examples

      You can find useful IO operations in the standard library modules Prelude and System.IO

      Here’s an IO operation that asks the user for a string, and prints out the length of the string.

      -
      query :: IO ()
      -query = do
      -  putStrLn "Write something!"                    -- run an operation, ignore produced value
      -  s <- getLine                                   -- run an operation, capture produced value
      -  let n = length s                               -- run a pure function
      -  putStrLn ("You wrote "++show n++" characters") -- run an operation, passing on the produced value
      -
      Prelude> query
      -Write something!
      -lorem ipsum
      -You wrote 11 characters
      +
      query :: IO ()
      +query = do
      +  putStrLn "Write something!"                    -- run an operation, ignore produced value
      +  s <- getLine                                   -- run an operation, capture produced value
      +  let n = length s                               -- run a pure function
      +  putStrLn ("You wrote "++show n++" characters") -- run an operation, passing on the produced value
      +
      Prelude> query
      +Write something!
      +lorem ipsum
      +You wrote 11 characters

      The value produced by the last line of a do block is the value produced by the whole block. Note how askForALine has the same type as getLine, IO String:

      -
      askForALine :: IO String
      -askForALine = do
      -  putStrLn "Please give me a line"
      -  getLine
      +
      askForALine :: IO String
      +askForALine = do
      +  putStrLn "Please give me a line"
      +  getLine

      In addition to IO operations like query you can also run IO operations that produce values, like askForALine, in GHCi. You can use <- to capture the result of the operation into a variable if you want.

      -
      Prelude> askForALine
      -Please give me a line
      -this is a line
      -"this is a line"
      -Prelude> line <- askForALine
      -Please give me a line
      -this is a line
      -Prelude> :t line
      -line :: String
      -Prelude> line
      -"this is a line"
      +
      Prelude> askForALine
      +Please give me a line
      +this is a line
      +"this is a line"
      +Prelude> line <- askForALine
      +Please give me a line
      +this is a line
      +Prelude> :t line
      +line :: String
      +Prelude> line
      +"this is a line"

      If you need to give your operation parameters, you can just make a function that returns an operation. Note how ask has a function type with a ->, just like a normal function. We also use normal function definition syntax to give the parameter the name question.

      -
      ask :: String -> IO String
      -ask question = do
      -  putStrLn question
      -  getLine
      -
      Prelude> ask "What is love?"
      -What is love?
      -Baby don't hurt me!
      -"Baby don't hurt me!"
      -Prelude> response <- ask "Who are you?"
      -Who are you?
      -The programmer.
      -Prelude> response
      -"The programmer."
      -Prelude> :t response
      -response :: String
      -Prelude> :t ask
      -ask :: String -> IO String
      -Prelude> :t ask "Who are you?"
      -ask "Who are you?" :: IO String
      +
      ask :: String -> IO String
      +ask question = do
      +  putStrLn question
      +  getLine
      +
      Prelude> ask "What is love?"
      +What is love?
      +Baby don't hurt me!
      +"Baby don't hurt me!"
      +Prelude> response <- ask "Who are you?"
      +Who are you?
      +The programmer.
      +Prelude> response
      +"The programmer."
      +Prelude> :t response
      +response :: String
      +Prelude> :t ask
      +ask :: String -> IO String
      +Prelude> :t ask "Who are you?"
      +ask "Who are you?" :: IO String

      11.3 The Subtle return

      The Haskell function return is named a bit misleadingly. In other languages return is a built-in keyword, but in Haskell it’s just a function. The return :: a -> IO a function takes a value and turns it into an operation, that produces the value.

      -
      produceThree :: IO Int
      -produceThree = return 3
      -
      -printThree :: IO ()
      -printThree = do
      -  three <- produceThree
      -  putStrLn (show three)
      +
      produceThree :: IO Int
      +produceThree = return 3
      +
      +printThree :: IO ()
      +printThree = do
      +  three <- produceThree
      +  putStrLn (show three)

      That doesn’t sound very useful does it? Combined with do-notation it is. Here we return a boolean according to whether the user answered Y or N:

      -
      yesNoQuestion :: String -> IO Bool
      -yesNoQuestion question = do
      -  putStrLn question
      -  s <- getLine
      -  return (s == "Y")
      -
      Prelude> yesNoQuestion "Fire the missiles?"
      -Fire the missiles?
      -Y
      -True
      -Prelude> answer <- yesNoQuestion "Are you sure?"
      -Are you sure?
      -N
      -Prelude> :t answer
      -answer :: Bool
      -Prelude> answer
      -False
      +
      yesNoQuestion :: String -> IO Bool
      +yesNoQuestion question = do
      +  putStrLn question
      +  s <- getLine
      +  return (s == "Y")
      +
      Prelude> yesNoQuestion "Fire the missiles?"
      +Fire the missiles?
      +Y
      +True
      +Prelude> answer <- yesNoQuestion "Are you sure?"
      +Are you sure?
      +N
      +Prelude> :t answer
      +answer :: Bool
      +Prelude> answer
      +False

      Note! This means that return does not stop execution of an operation (unlike return in Java or C). Remember that in do-blocks, the last line decides which value to produce. This means that this operation produces 2:

      -
      produceTwo :: IO Int
      -produceTwo = do return 1
      -                return 2
      -
      Prelude> produceTwo
      -2
      +
      produceTwo :: IO Int
      +produceTwo = do return 1
      +                return 2
      +
      Prelude> produceTwo
      +2

      Let’s look at this another way. The do notation allows us to cause a sequence of side-effects, and finally to produce a value.

      produceThree = do putStrLn "1"   -- side effect, produces (), which is ignored
                         return    2    -- no side effect, produces 2, which is ignored
      @@ -1851,344 +1852,344 @@ this line is ignored
       Prelude> final
       3

      Also note that these are the same operation:

      -
      do ...
      -   x <- op
      -   return x
      -
      do ...
      -   op
      +
      do ...
      +   x <- op
      +   return x
      +
      do ...
      +   op

      Since return is a function, you should remember to parenthesize any complex expressions:

      -
      return (f x : xs)
      --- alternatively:
      -return $ f x : xs
      +
      return (f x : xs)
      +-- alternatively:
      +return $ f x : xs

      11.4 do and Types

      Let’s look at the typing of do-notation in more detail. A do-block builds a value of type IO <something>. For example in

      -
      foo = do
      -  ...
      -  lastOp
      +
      foo = do
      +  ...
      +  lastOp

      The lastOp must be of type IO X (for some X). The type of foo will also be IO X. Let’s look at an example with parameters next:

      -
      bar x y = do
      -  ...
      -  lastOp arg
      +
      bar x y = do
      +  ...
      +  lastOp arg

      The lastOp must be of type Y -> IO X (so that lastOp arg has type IO X). The type of bar will be A -> B -> IO X (and inside bar we’ll have x :: A and y :: B).

      If we use return:

      -
      quux x = do
      -  ...
      -  return value
      +
      quux x = do
      +  ...
      +  return value

      The function quux will have type A -> IO B, where x :: A and value :: B.

      Let’s look at the typing of <- next. If op :: IO X and you have var <- op, var will have type X. We’ve seen this in many GHCi examples.

      The last line of a do cannot be foo <- bar. It can’t be let foo = bar either. The last line determines what the whole operation produces, so it must be an operation (for example, return something).

      Here’s a worked example:

      -
      alwaysFine :: IO Bool
      -alwaysFine = do
      -  putStrLn "What?" -- :: IO ()
      -  return 2         -- :: IO Int, produced value is discarded
      -  s <- getLine     -- getLine :: IO String, thus s :: String
      -  putStrLn s       -- putStrLn :: String -> IO (), thus putStrLn s :: IO ()
      -  let b = True     -- b :: Bool
      -  return b         -- :: IO Bool
      -                   -- Thus, alwaysFine :: IO Bool
      +
      alwaysFine :: IO Bool
      +alwaysFine = do
      +  putStrLn "What?" -- :: IO ()
      +  return 2         -- :: IO Int, produced value is discarded
      +  s <- getLine     -- getLine :: IO String, thus s :: String
      +  putStrLn s       -- putStrLn :: String -> IO (), thus putStrLn s :: IO ()
      +  let b = True     -- b :: Bool
      +  return b         -- :: IO Bool
      +                   -- Thus, alwaysFine :: IO Bool

      The typing rules guarantee that you can not “escape” IO. Even though <- gives you an X from an IO X, you can only use <- inside do. However a do always means a value of type IO Y. In other words: you can temporarily open the IO box, but you must return into it. “What happens in IO, stays in IO.”

      We’ll talk more about what this means later. For now, it’s enough to know that if you have a function with a non-IO type, like for example myFunction :: Int -> [String] -> String, the function can not have IO happening inside it. It is a pure function.

      11.5 Control Structures

      For the following examples, we’ll need two new operations.

      -
      print :: Show a => a -> IO ()   -- print a value using the show function
      -readLn :: Read a => IO a        -- get a line and convert it to a value using the read function
      +
      print :: Show a => a -> IO ()   -- print a value using the show function
      +readLn :: Read a => IO a        -- get a line and convert it to a value using the read function

      The usual tools of recursion, guards and if-then-else also work in the IO world. Here’s an IO operation that’s defined using a guard:

      -
      printDescription :: Int -> IO ()
      -printDescription n
      -  | even n    = putStrLn "even"
      -  | n==3      = putStrLn "three"
      -  | otherwise = print n
      -
      Prelude> printDescription 2
      -even
      -Prelude> printDescription 3
      -three
      -Prelude> printDescription 5
      -5
      +
      printDescription :: Int -> IO ()
      +printDescription n
      +  | even n    = putStrLn "even"
      +  | n==3      = putStrLn "three"
      +  | otherwise = print n
      +
      Prelude> printDescription 2
      +even
      +Prelude> printDescription 3
      +three
      +Prelude> printDescription 5
      +5

      Here’s an operation that prints all numbers in a list using recursion and pattern matching:

      -
      printList :: [Int] -> IO ()
      -printList [] = return () -- do nothing
      -printList (x:xs) = do print x
      -                      printList xs -- recursion
      -
      Prelude> printList [1,2,3]
      -1
      -2
      -3
      +
      printList :: [Int] -> IO ()
      +printList [] = return () -- do nothing
      +printList (x:xs) = do print x
      +                      printList xs -- recursion
      +
      Prelude> printList [1,2,3]
      +1
      +2
      +3

      Here are two slightly more complicated examples of recursive IO operations. They use the value produced by the recursive call. The operation readAndSum n reads n numbers from the user and prints their sum. The operation ask questions shows each string in questions to the user, reads a response, and returns a list of all the responses.

      -
      readAndSum :: Int -> IO Int
      -readAndSum 0 = return 0
      -readAndSum n = do
      -  i <- readLn            -- read one number
      -  s <- readAndSum (n-1)  -- recursion: read and sum rest of numbers
      -  return (i+s)           -- produce result
      -
      Prelude> s <- readAndSum 3
      -2
      -4
      -5
      -Prelude> s
      -11
      -
      ask :: [String] -> IO [String]
      -ask [] = return []
      -ask (question:questions) = do
      -  putStr question
      -  putStrLn "?"
      -  answer <- getLine         -- get one answer
      -  answers <- ask questions  -- recursion: get rest of answers
      -  return (answer:answers)   -- produce result
      -
      Prelude> replies <- ask ["What is your name","How old are you"]
      -What is your name?
      -Yog-Sothoth
      -How old are you?
      -The question is meaningless
      -Prelude> replies
      -["Yog-Sothoth","The question is meaningless"]
      +
      readAndSum :: Int -> IO Int
      +readAndSum 0 = return 0
      +readAndSum n = do
      +  i <- readLn            -- read one number
      +  s <- readAndSum (n-1)  -- recursion: read and sum rest of numbers
      +  return (i+s)           -- produce result
      +
      Prelude> s <- readAndSum 3
      +2
      +4
      +5
      +Prelude> s
      +11
      +
      ask :: [String] -> IO [String]
      +ask [] = return []
      +ask (question:questions) = do
      +  putStr question
      +  putStrLn "?"
      +  answer <- getLine         -- get one answer
      +  answers <- ask questions  -- recursion: get rest of answers
      +  return (answer:answers)   -- produce result
      +
      Prelude> replies <- ask ["What is your name","How old are you"]
      +What is your name?
      +Yog-Sothoth
      +How old are you?
      +The question is meaningless
      +Prelude> replies
      +["Yog-Sothoth","The question is meaningless"]

      Additionally, we have some IO-specific control structures, or rather, functions. These come from the module Control.Monad.

      -
      -- when b op performs op if b is true
      -when :: Bool -> IO () -> IO ()
      --- unless b op performs op if b is false
      -unless :: Bool -> IO () -> IO ()
      --- do something many times, collect results
      -replicateM :: Int -> IO a -> IO [a]
      --- do something many times, throw away the results
      -replicateM_ :: Int -> IO a -> IO ()
      --- do something for every list element
      -mapM :: (a -> IO b) -> [a] -> IO [b]
      --- do something for every list element, throw away the results
      -mapM_ :: (a -> IO b) -> [a] -> IO ()
      --- the same, but arguments flipped
      -forM  :: [a] -> (a -> IO b) -> IO [b]
      -forM_ :: [a] -> (a -> IO b) -> IO ()
      +
      -- when b op performs op if b is true
      +when :: Bool -> IO () -> IO ()
      +-- unless b op performs op if b is false
      +unless :: Bool -> IO () -> IO ()
      +-- do something many times, collect results
      +replicateM :: Int -> IO a -> IO [a]
      +-- do something many times, throw away the results
      +replicateM_ :: Int -> IO a -> IO ()
      +-- do something for every list element
      +mapM :: (a -> IO b) -> [a] -> IO [b]
      +-- do something for every list element, throw away the results
      +mapM_ :: (a -> IO b) -> [a] -> IO ()
      +-- the same, but arguments flipped
      +forM  :: [a] -> (a -> IO b) -> IO [b]
      +forM_ :: [a] -> (a -> IO b) -> IO ()

      Using these, we can rewrite our earlier examples:

      -
      printList :: [Int] -> IO ()
      -printList xs = mapM_ print xs
      -
      readAndSum n = do
      -  numbers <- replicateM n readLn
      -  return (sum numbers)
      -
      ask :: [String] -> IO [String]
      -ask questions = do
      -  forM questions askOne
      -
      -askOne :: String -> IO String
      -askOne question = do
      -  putStr question
      -  putStrLn "?"
      -  getLine
      +
      printList :: [Int] -> IO ()
      +printList xs = mapM_ print xs
      +
      readAndSum n = do
      +  numbers <- replicateM n readLn
      +  return (sum numbers)
      +
      ask :: [String] -> IO [String]
      +ask questions = do
      +  forM questions askOne
      +
      +askOne :: String -> IO String
      +askOne question = do
      +  putStr question
      +  putStrLn "?"
      +  getLine

      11.6 A Word About do and Indentation

      It’s easy to run into weird indentation problems when using do-notation. Here are some rules of thumb to help you get it right.

      The most important rule of do and indentation is all operations in a do-block must start in the same column.

      Some examples of this rule:

      -
      -- This is not OK, putStrLn is way too left
      -foo = do y <- getLine
      -   putStrLn y
      -
      --- This is not OK either
      -foo = do y <- getLine
      -           putStrLn y
      -
      --- This is OK
      -foo = do y <- getLine
      -         putStrLn y
      -
      --- This is also OK: putting a line break after do
      -foo = do
      -  y <- getLine
      -  putStrLn y
      +
      -- This is not OK, putStrLn is way too left
      +foo = do y <- getLine
      +   putStrLn y
      +
      +-- This is not OK either
      +foo = do y <- getLine
      +           putStrLn y
      +
      +-- This is OK
      +foo = do y <- getLine
      +         putStrLn y
      +
      +-- This is also OK: putting a line break after do
      +foo = do
      +  y <- getLine
      +  putStrLn y

      A related rule is when an operation goes over multiple lines, indent the follow-up lines. If you don’t indent, it’ll look like a new operation!

      -
      -- This is not OK, the string starts a new operation
      -quux = do putStrLn
      -          "this long string"
      -          print 1
      -
      --- This is OK
      -quux = do putStrLn
      -            "this long string"
      -          print 1
      +
      -- This is not OK, the string starts a new operation
      +quux = do putStrLn
      +          "this long string"
      +          print 1
      +
      +-- This is OK
      +quux = do putStrLn
      +            "this long string"
      +          print 1

      Here’s one more example, with nested do-blocks, and two different valid indentations.

      -
      -- This is OK
      -foo x = do quux
      -           y <- blorg
      -           when y (do thing
      -                      otherThing)
      -           return 3
      -
      --- This is also OK: starting putting a line break after do, using $
      -foo x = do
      -  quux
      -  y <- blorg
      -  when y $ do
      -    thing
      -    otherThing
      -  return 3
      +
      -- This is OK
      +foo x = do quux
      +           y <- blorg
      +           when y (do thing
      +                      otherThing)
      +           return 3
      +
      +-- This is also OK: starting putting a line break after do, using $
      +foo x = do
      +  quux
      +  y <- blorg
      +  when y $ do
      +    thing
      +    otherThing
      +  return 3

      11.7 Let’s Write a Program

      After all these short one-off examples, let’s turn to something a bit longer. Let’s write a program to fetch all type annotations from all .hs files. We use IO operations like readFile and listDirectory to read and find files, but also pure code like map and filter to do the actual processing. First off, here’s a recap of the library operations we’re using:

      -
      -- split string into lines
      -lines :: String -> [String]
      --- `isSuffixOf suf list` is true if list ends in suf
      -Data.List.isSuffixOf :: Eq a => [a] -> [a] -> Bool
      --- `isInfixOf inf list` is true if inf occurs inside list
      -Data.List.isInfixOf :: Eq a => [a] -> [a] -> Bool
      --- FilePath is just an alias for String
      -type FilePath = String
      --- get entire contents of file
      -readFile :: FilePath -> IO String
      --- list files in directory
      -System.Directory.listDirectory :: FilePath -> IO [FilePath]
      --- is the given file a directory?
      -System.Directory.doesDirectoryExist :: FilePath -> IO Bool
      +
      -- split string into lines
      +lines :: String -> [String]
      +-- `isSuffixOf suf list` is true if list ends in suf
      +Data.List.isSuffixOf :: Eq a => [a] -> [a] -> Bool
      +-- `isInfixOf inf list` is true if inf occurs inside list
      +Data.List.isInfixOf :: Eq a => [a] -> [a] -> Bool
      +-- FilePath is just an alias for String
      +type FilePath = String
      +-- get entire contents of file
      +readFile :: FilePath -> IO String
      +-- list files in directory
      +System.Directory.listDirectory :: FilePath -> IO [FilePath]
      +-- is the given file a directory?
      +System.Directory.doesDirectoryExist :: FilePath -> IO Bool

      And here’s the program itself. You can also find it in the course repository as exercises/Examples/ReadTypes.hs.

      -
      module Examples.ReadTypes where
      -
      -import Control.Monad (forM)
      -import Data.List (isInfixOf, isSuffixOf)
      -import System.Directory (listDirectory, doesDirectoryExist)
      -
      --- a line is a type signature if it contains :: but does not contain =
      -isTypeSignature :: String -> Bool
      -isTypeSignature s = not (isInfixOf "=" s) && isInfixOf "::" s
      -
      --- return list of types for a .hs file
      -readTypesFile :: FilePath -> IO [String]
      -readTypesFile file
      -  | isSuffixOf ".hs" file = do content <- readFile file
      -                               let ls = lines content
      -                               return (filter isTypeSignature ls)
      -  | otherwise             = return []
      -
      --- list children of directory, prepend directory name
      -qualifiedChildren :: String -> IO [String]
      -qualifiedChildren path = do childs <- listDirectory path
      -                            return (map (\name -> path++"/"++name) childs)
      -
      --- get type signatures for all entries in given directory
      --- note mutual recursion with readTypes
      -readTypesDir :: String -> IO [String]
      -readTypesDir path = do childs <- qualifiedChildren path
      -                       typess <- forM childs readTypes
      -                       return (concat typess)
      -
      --- recursively read types contained in a file or directory
      --- note mutual recursion with readTypesDir
      -readTypes :: String -> IO [String]
      -readTypes path = do isDir <- doesDirectoryExist path
      -                    if isDir then readTypesDir path else readTypesFile path
      -
      --- main is the IO action that gets run when you run the program
      -main :: IO ()
      -main = do ts <- readTypes "."
      -          mapM_ putStrLn ts
      +
      module Examples.ReadTypes where
      +
      +import Control.Monad (forM)
      +import Data.List (isInfixOf, isSuffixOf)
      +import System.Directory (listDirectory, doesDirectoryExist)
      +
      +-- a line is a type signature if it contains :: but does not contain =
      +isTypeSignature :: String -> Bool
      +isTypeSignature s = not (isInfixOf "=" s) && isInfixOf "::" s
      +
      +-- return list of types for a .hs file
      +readTypesFile :: FilePath -> IO [String]
      +readTypesFile file
      +  | isSuffixOf ".hs" file = do content <- readFile file
      +                               let ls = lines content
      +                               return (filter isTypeSignature ls)
      +  | otherwise             = return []
      +
      +-- list children of directory, prepend directory name
      +qualifiedChildren :: String -> IO [String]
      +qualifiedChildren path = do childs <- listDirectory path
      +                            return (map (\name -> path++"/"++name) childs)
      +
      +-- get type signatures for all entries in given directory
      +-- note mutual recursion with readTypes
      +readTypesDir :: String -> IO [String]
      +readTypesDir path = do childs <- qualifiedChildren path
      +                       typess <- forM childs readTypes
      +                       return (concat typess)
      +
      +-- recursively read types contained in a file or directory
      +-- note mutual recursion with readTypesDir
      +readTypes :: String -> IO [String]
      +readTypes path = do isDir <- doesDirectoryExist path
      +                    if isDir then readTypesDir path else readTypesFile path
      +
      +-- main is the IO action that gets run when you run the program
      +main :: IO ()
      +main = do ts <- readTypes "."
      +          mapM_ putStrLn ts

      We can run this program by going to the directory exercises/Examples and running:

      -
      $ stack runhaskell ReadTypes.hs
      -deposit :: String -> Int -> Bank -> Bank
      -withdraw :: String -> Int -> Bank -> (Int,Bank)
      -runBankOp :: BankOp a -> Bank -> (a,Bank)
      -... and so on
      +
      $ stack runhaskell ReadTypes.hs
      +deposit :: String -> Int -> Bank -> Bank
      +withdraw :: String -> Int -> Bank -> (Int,Bank)
      +runBankOp :: BankOp a -> Bank -> (a,Bank)
      +... and so on

      The exact output will vary according to the contents of the directory, of course.

      11.8 What Does It All Mean?

      Let’s return to the functional world. How can we reconcile IO operations with Haskell being a pure and lazy language? Something like putStrLn :: String -> IO () is a pure function that returns an operation. How is it pure? putStrLn x is the same when x is the same. In other words: an operation is a pure description of a chain of side effects. Only executing the operation causes those side effects. When a Haskell program is run, only one operation is executed - it’s called main :: IO (). Other operations can be run only by linking them up to main.

      When in GHCi, if an expression you type in evaluates to an operation, GHCi runs that operation for you. Here’s a demonstration of the purity of print:

      -
      Prelude> let x = print 1   -- creates operation, doesn't run it
      -Prelude> x                 -- runs the operation
      -1
      -Prelude> x                 -- runs it again!
      -1
      +
      Prelude> let x = print 1   -- creates operation, doesn't run it
      +Prelude> x                 -- runs the operation
      +1
      +Prelude> x                 -- runs it again!
      +1

      Operations are values just like numbers, lists and functions. We can write code that operates on operations. This function takes two operations, a and b, and returns an operation that asks the user which one he’d like to run.

      -
      choice :: IO x -> IO x -> IO x
      -choice a b =
      -  do putStr "a or b? "
      -     x <- getLine
      -     case x of "a" -> a
      -               "b" -> b
      -               _ -> do putStrLn "Wrong!"
      -                       choice a b
      -
      Prelude> choice (putStrLn "A!!!!") (putStrLn "B!!!!")
      -a or b? z
      -Wrong!
      -a or b? a
      -A!!!!
      +
      choice :: IO x -> IO x -> IO x
      +choice a b =
      +  do putStr "a or b? "
      +     x <- getLine
      +     case x of "a" -> a
      +               "b" -> b
      +               _ -> do putStrLn "Wrong!"
      +                       choice a b
      +
      Prelude> choice (putStrLn "A!!!!") (putStrLn "B!!!!")
      +a or b? z
      +Wrong!
      +a or b? a
      +A!!!!

      Using operations specified as parameters lets us write functions like mapM_, which we met earlier. The implementation is a recursive IO operation that takes another IO operation as a parameter. Conceptually complicated, but simple when you read the code:

      -
      mapM_ :: (a -> IO b) -> [a] -> IO ()
      -mapM_ op     [] = return ()       -- do nothing for an empty list
      -mapM_ op (x:xs) = do op x         -- run operation on first element
      -                     mapM_ op xs  -- run operation on rest of list, recursively
      -
      Prelude> mapM_ print [1,2,3]
      -1
      -2
      -3
      +
      mapM_ :: (a -> IO b) -> [a] -> IO ()
      +mapM_ op     [] = return ()       -- do nothing for an empty list
      +mapM_ op (x:xs) = do op x         -- run operation on first element
      +                     mapM_ op xs  -- run operation on rest of list, recursively
      +
      Prelude> mapM_ print [1,2,3]
      +1
      +2
      +3

      11.9 One More Thing: IORef

      So far the only side-effects we’ve been able to produce in IO have been terminal (getLine, print) and file (readFile, listDirectory) IO. Imperative programs written in Java, Python or C have other types of side effects too that we can’t express in pure Haskell. One of these is mutable (i.e. changeable) state. A pure function can not read mutable state, because otherwise two invocations of the same function might not return the same value.

      The Haskell type IORef a from the module Data.IORef is a mutable reference to a value of type a

      -
      newIORef :: a -> IO (IORef a)                -- create a new IORef containing a value
      -readIORef :: IORef a -> IO a                 -- produce value contained in IORef
      -writeIORef :: IORef a -> a -> IO ()          -- set value in IORef
      -modifyIORef :: IORef a -> (a -> a) -> IO ()  -- modify value contained in IORef with a pure function
      +
      newIORef :: a -> IO (IORef a)                -- create a new IORef containing a value
      +readIORef :: IORef a -> IO a                 -- produce value contained in IORef
      +writeIORef :: IORef a -> a -> IO ()          -- set value in IORef
      +modifyIORef :: IORef a -> (a -> a) -> IO ()  -- modify value contained in IORef with a pure function

      Here are some examples of using an IORef in GHCi:

      -
      Prelude> :m +Data.IORef
      -Prelude Data.IORef> myRef <- newIORef "banana"
      -Prelude Data.IORef> readIORef myRef
      -"banana"
      -Prelude Data.IORef> writeIORef myRef "apple"
      -Prelude Data.IORef> readIORef myRef
      -"apple"
      -Prelude Data.IORef> modifyIORef myRef reverse
      -Prelude Data.IORef> readIORef myRef
      -"elppa"
      +
      Prelude> :m +Data.IORef
      +Prelude Data.IORef> myRef <- newIORef "banana"
      +Prelude Data.IORef> readIORef myRef
      +"banana"
      +Prelude Data.IORef> writeIORef myRef "apple"
      +Prelude Data.IORef> readIORef myRef
      +"apple"
      +Prelude Data.IORef> modifyIORef myRef reverse
      +Prelude Data.IORef> readIORef myRef
      +"elppa"

      Here’s an example of using an IORef to sum the values in a list. Note the similarity with an imperative loop.

      -
      sumList :: [Int] -> IO Int
      -sumList xs = do r <- newIORef 0                       -- initialize r to 0
      -                forM_ xs (\x -> modifyIORef r (x+))   -- for every xs, add it to r
      -                readIORef r                           -- get last value of r
      +
      sumList :: [Int] -> IO Int
      +sumList xs = do r <- newIORef 0                       -- initialize r to 0
      +                forM_ xs (\x -> modifyIORef r (x+))   -- for every xs, add it to r
      +                readIORef r                           -- get last value of r

      Using IORef isn’t necessary most of the time. Haskell style prefers recursion, arguments and return values. However real world programs might need one or two IORefs occasionally.

      11.10 Summary of IO

      A value of type IO X is an IO operation that produces a value of type X when run. Operations are pure values. Only running the operation causes the side effects.

      IO operations can combined together using do-notation:

      -
      op :: X -> IO Y
      -op arg = do operation                 -- run operation
      -            operation2 arg            -- run operation with argument
      -            result <- operation3 arg  -- run operation with argument, store result
      -            let something = f result  -- run a pure function f, store result
      -            finalOperation            -- last operation produces the the return value
      +
      op :: X -> IO Y
      +op arg = do operation                 -- run operation
      +            operation2 arg            -- run operation with argument
      +            result <- operation3 arg  -- run operation with argument, store result
      +            let something = f result  -- run a pure function f, store result
      +            finalOperation            -- last operation produces the the return value

      The return x operation is an operation that always produces value x. When x :: a, return x :: IO a.

      Useful IO operations:

      -
      -- printing & reading
      -putStr :: String -> IO ()
      -putStrLn :: String -> IO ()
      -print :: Show a => a -> IO ()
      -getLine :: IO String
      -readLn :: Read a => IO a
      -
      --- control structures from Control.Monad
      -when :: Bool -> IO () -> IO ()   -- when b op performs op if b is true
      -unless :: Bool -> IO () -> IO () -- unless b op performs op if b is false
      -replicateM :: Int -> IO a -> IO [a]   -- do something many times, collect results
      -replicateM_ :: Int -> IO a -> IO ()   -- do something many times, throw away the results
      -mapM ::  (a -> IO b) -> [a] -> IO [b] -- do something for every list element
      -mapM_ :: (a -> IO b) -> [a] -> IO ()  -- do something for every list element, throw away the results
      -forM ::  [a] -> (a -> IO b) -> IO [b] -- the same, but arguments flipped
      -forM_ :: [a] -> (a -> IO b) -> IO ()
      -
      --- files
      -readFile :: FilePath -> IO String
      +
      -- printing & reading
      +putStr :: String -> IO ()
      +putStrLn :: String -> IO ()
      +print :: Show a => a -> IO ()
      +getLine :: IO String
      +readLn :: Read a => IO a
      +
      +-- control structures from Control.Monad
      +when :: Bool -> IO () -> IO ()   -- when b op performs op if b is true
      +unless :: Bool -> IO () -> IO () -- unless b op performs op if b is false
      +replicateM :: Int -> IO a -> IO [a]   -- do something many times, collect results
      +replicateM_ :: Int -> IO a -> IO ()   -- do something many times, throw away the results
      +mapM ::  (a -> IO b) -> [a] -> IO [b] -- do something for every list element
      +mapM_ :: (a -> IO b) -> [a] -> IO ()  -- do something for every list element, throw away the results
      +forM ::  [a] -> (a -> IO b) -> IO [b] -- the same, but arguments flipped
      +forM_ :: [a] -> (a -> IO b) -> IO ()
      +
      +-- files
      +readFile :: FilePath -> IO String

      11.11 Quiz

      What is the type of this IO operation?

      -
      foo x = do putStrLn x
      -           y <- getLine
      -           return (length y)
      +
      foo x = do putStrLn x
      +           y <- getLine
      +           return (length y)
      1. String -> IO String @@ -2204,11 +2205,11 @@ Prelude> final

      Which of these lines could be used in place of ????

      -
      quux :: String -> IO [String]
      -quux q = do y <- getLine
      -            z <- getLine
      -            putStrLn (y++z)
      -            ????
      +
      quux :: String -> IO [String]
      +quux q = do y <- getLine
      +            z <- getLine
      +            putStrLn (y++z)
      +            ????
      1. q <- getLine @@ -2224,10 +2225,10 @@ Prelude> final

      What values does blorg [1,2,3] print? That is, what values x does it call print x for. The value produced by blorg doesn’t count.

      -
      blorg [] = return 0
      -blorg (x:xs) = do m <- blorg xs
      -                  print x
      -                  return (m+x)
      +
      blorg [] = return 0
      +blorg (x:xs) = do m <- blorg xs
      +                  print x
      +                  return (m+x)
      1. It prints 1, 2, 3 @@ -2295,88 +2296,88 @@ Query the user for a number and return it.

        12.2.1 Preserving Structure

        Remember the map function for lists? Here’s the definition again:

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

        It applies a function g :: a -> b to each element of a list of type [a], returning a list of type [b]. Another way to express the type of map would be (a -> b) -> ([a] -> [b]). This is the same type because -> associates to right. The extra parentheses emphasize the fact that map converts the function g :: a -> b into a function map g :: [a] -> [b]. This means that map is a higher-order function that transforms functions to functions.

        As map is parametrically polymorphic, its definition doesn’t depend on the type of values stored in the list. Thus, every function of type a -> b is converted into a function of type [a] -> [b] using exactly the same logic. Using the definition above, we can see that:

        -
        map (|| True) [True, True, False]
        -  ==> [True || True, True || True, False || True]
        -  ==> [True, True, True]
        -
        -map (+1) [1,2,3]
        -  ==> [1 + 1, 2 + 1, 3 + 1]
        -  ==> [2, 3, 4]
        -
        -map (++"1") ["1", "2", "3"]
        -  ==> ["1" ++ "1", "2" ++ "1", "3" ++ "1"]
        -  ==> ["11", "21", "31"]
        +
        map (|| True) [True, True, False]
        +  ==> [True || True, True || True, False || True]
        +  ==> [True, True, True]
        +
        +map (+1) [1,2,3]
        +  ==> [1 + 1, 2 + 1, 3 + 1]
        +  ==> [2, 3, 4]
        +
        +map (++"1") ["1", "2", "3"]
        +  ==> ["1" ++ "1", "2" ++ "1", "3" ++ "1"]
        +  ==> ["11", "21", "31"]

        What’s notable here is that map preserves the structure of a list. The length of the list and the relative positions of the elements are the same. The general idea is demonstrated in the picture below.

        -
        Mapping a function g to a list
        +Mapping a function g to a list

        Let’s see if we can find other similar functions. A value of type Maybe a is kind of like a list of length at most 1. Let’s map over a Maybe! Can you see the similarity with the definition of map?

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

        Here too, the structure of the value is preserved. A Nothing turns into a Nothing, and a Just turns into a Just. Here too, we can think of the type as (a -> b) -> (Maybe a -> Maybe b), converting (or “lifting”) a normal function into a function that works on Maybes.

        One more example: consider binary trees.

        -
        data Tree a = Leaf | Node a (Tree a) (Tree a)
        -
        -mapTree :: (a -> b) -> Tree a -> Tree b
        -mapTree f Leaf = Leaf
        -mapTree f (Node val left right) = Node (f val) (mapTree f left) (mapTree f right)
        +
        data Tree a = Leaf | Node a (Tree a) (Tree a)
        +
        +mapTree :: (a -> b) -> Tree a -> Tree b
        +mapTree f Leaf = Leaf
        +mapTree f (Node val left right) = Node (f val) (mapTree f left) (mapTree f right)

        A binary tree might look like this:

        -
        A binary tree
        +A binary tree

        After mapTree g the tree would looke like this:

        -
        A binary tree after mapping g into its nodes
        +A binary tree after mapping g into its nodes

        12.2.2 The Functor Class

        Now we have three different structure-preserving mapping functions. Three similar operations over different types. Could we write a type class to capture this similarity?

        -
        map      :: (a -> b) ->      [a] ->      [b]
        -mapMaybe :: (a -> b) -> Maybe a  -> Maybe b
        -mapTree  :: (a -> b) -> Tree  a  -> Tree  b
        +
        map      :: (a -> b) ->      [a] ->      [b]
        +mapMaybe :: (a -> b) -> Maybe a  -> Maybe b
        +mapTree  :: (a -> b) -> Tree  a  -> Tree  b

        A naive attempt at writing a type class runs into problems. If we try to abstract over Maybe c, it seems we can’t write the right type for the map operation. We’d need to be able to change the type parameter c somehow.

        -
        class Mappable m where
        -  mapThing :: (a -> b) -> m -> m
        -
        -instance Mappable (Maybe c) where
        -  mapThing :: (a -> b) -> Maybe c -> Maybe c
        -  mapThing = ...
        +
        class Mappable m where
        +  mapThing :: (a -> b) -> m -> m
        +
        +instance Mappable (Maybe c) where
        +  mapThing :: (a -> b) -> Maybe c -> Maybe c
        +  mapThing = ...

        Luckily Haskell type classes have a feature we haven’t covered before. You can write classes for type constructors in addition to types. What does this mean? Let’s just have a look at the standard type class Functor that does what we tried to do with our Mappable.

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

        Note how the type parameter f is a type constructor: it’s being passed a and b arguments in different parts of the type of fmap. Now let’s see the instance for Maybe.

        -
        instance Functor Maybe where
        -  -- In this instance, the type of fmap is:
        -  -- fmap :: (a -> b) -> Maybe a -> Maybe b
        -  fmap f Nothing = Nothing
        -  fmap f (Just x) = Just (f x)
        +
        instance Functor Maybe where
        +  -- In this instance, the type of fmap is:
        +  -- fmap :: (a -> b) -> Maybe a -> Maybe b
        +  fmap f Nothing = Nothing
        +  fmap f (Just x) = Just (f x)

        Now fmap has the right type and we can implement it like mapMaybe! Note how we’ve declared instance Functor Maybe instead of instance Functor (Maybe a). The type Maybe a isn’t a functor, the type constructor Maybe is.

        The type constructor for lists is written []. It’s special syntax, just like other list syntax. However if the type [a] was written List a, the type constructor [] would mean List.

        -
        instance Functor [] where
        -  fmap = map
        +
        instance Functor [] where
        +  fmap = map

        Here’s the final of our examples, as a Functor instance.

        -
        data Tree a = Leaf | Node a (Tree a) (Tree a)
        -
        -instance Functor Tree where
        -  fmap _ Leaf = Leaf
        -  fmap f (Node val left right) = Node (f val) (fmap f left) (fmap f right)
        +
        data Tree a = Leaf | Node a (Tree a) (Tree a)
        +
        +instance Functor Tree where
        +  fmap _ Leaf = Leaf
        +  fmap f (Node val left right) = Node (f val) (fmap f left) (fmap f right)

        Sidenote: the term functor comes originally from a branch of mathematics called category theory. However, to work with Haskell you don’t need to know any category theory. As you progress in learning Haskell, you might get interested in category theory, and it can be a valuable source of new ideas for programming. Category theory can feel intimidating, so it’s good to know that you can get on fine without it. For now, when you see functor, you can just think “something I can map over”, or perhaps “a container”.

        Let’s zoom out a bit. When we have an instance Functor MyFun we know that we can map a type X into a new type MyFun X (since MyFun is a type constructor), but also that we can lift a function f that takes an X argument into a function fmap f that takes a MyFun X argument! So you could say we’re mapping both on the type level and the value level.

        Oh right, one more thing. Once you’ve gotten the hang of fmap you might find yourself using it quite a bit. For code that uses fmap heavily it can be nice to use its infix alias, <$>. Consider the symmetry between $ and <$> in these examples:

        -
        (+1) <$> [1,2,3]    ==>  [2,3,4]
        -not <$> Just False  ==>  Just True
        -
        -reverse . tail  $       "hello"       ==>  "olle"
        -reverse . tail <$> Just "hello"       ==>  Just "olle"
        --- which is the same as
        -fmap (reverse . tail) (Just "hello")  ==>  Just "olle"
        +
        (+1) <$> [1,2,3]    ==>  [2,3,4]
        +not <$> Just False  ==>  Just True
        +
        +reverse . tail  $       "hello"       ==>  "olle"
        +reverse . tail <$> Just "hello"       ==>  Just "olle"
        +-- which is the same as
        +fmap (reverse . tail) (Just "hello")  ==>  Just "olle"
      @@ -2387,33 +2388,33 @@ Query the user for a number and return it.
    2. fmap (f . g) === fmap f . fmap g

    Don’t worry if that sounded abstract! The first law says that a functor maps id :: a -> a into id :: f a -> f a. (id is the identity function, meaning that id x = x.) Let’s be concrete and see how it works for the list [1,2,3]:

    -
    fmap id [1,2,3] ==> map id [1,2,3]
    -                ==> map id (1:[2,3])
    -                ==> id 1 : map id [2,3]
    -                ==> 1 : map id [2,3]
    -                ==> 1 : id 2 : map id [3]
    -                ==> 1 : 2 : id 3 : map id []
    -                ==> 1 : 2 : 3 : []
    -                === [1,2,3]
    +
    fmap id [1,2,3] ==> map id [1,2,3]
    +                ==> map id (1:[2,3])
    +                ==> id 1 : map id [2,3]
    +                ==> 1 : map id [2,3]
    +                ==> 1 : id 2 : map id [3]
    +                ==> 1 : 2 : id 3 : map id []
    +                ==> 1 : 2 : 3 : []
    +                === [1,2,3]

    On the other hand,

    -
    id [1,2,3] ==> [1,2,3]
    +
    id [1,2,3] ==> [1,2,3]

    Hence, the result of fmap id [1,2,3] was the same as the result of id [1,2,3], so the first functor law holds in this case. It’s not hard to show that the first functor law holds for any list whatsoever.

    The first functor law is really a very simple proposition if you think about it. It just says that if we apply fmap to a function that changes nothing (id) the resulting function (fmap id) again changes nothing. Thus, the act of applying fmap itself preserves the structure of the functor.

    How about the second functor law? For lists, consider what happens if we fmap the function negate.(*2) (remember, negate maps x to -x and (*2) multiplies its argument by 2):

    -
    fmap (negate.(*2)) [1,2,3] ==> map (negate.(*2)) [1,2,3]
    -                           ==> (negate.(*2)) 1 : map (negate.(*2)) [2,3]
    -                           ==> negate (1 * 2)  : map (negate.(*2)) [2,3]
    -                           ==> -2 : map (negate.(*2)) [2,3]
    -                           ==> -2 : (negate.(*2)) 2 : map (negate.(*2)) [3]
    -                           ==> -2 : -4 : map (negate.(*2)) [3]
    -                           ==> -2 : -4 : (negate.(*2)) 3 : map (negate.(*2)) []
    -                           ==> -2 : -4 : -6 : []
    -                           ==> [-2,-4,-6]
    +
    fmap (negate.(*2)) [1,2,3] ==> map (negate.(*2)) [1,2,3]
    +                           ==> (negate.(*2)) 1 : map (negate.(*2)) [2,3]
    +                           ==> negate (1 * 2)  : map (negate.(*2)) [2,3]
    +                           ==> -2 : map (negate.(*2)) [2,3]
    +                           ==> -2 : (negate.(*2)) 2 : map (negate.(*2)) [3]
    +                           ==> -2 : -4 : map (negate.(*2)) [3]
    +                           ==> -2 : -4 : (negate.(*2)) 3 : map (negate.(*2)) []
    +                           ==> -2 : -4 : -6 : []
    +                           ==> [-2,-4,-6]

    Let’s consider the right-hand side of the second functor law in this case:

    -
    (fmap negate . fmap (*2)) [1,2,3] ==> (map negate . map (*2)) [1,2,3]
    -                                  ==> map negate (map (*2) [1,2,3])
    -                                  ==> map negate [2,4,6]
    -                                  ==> [-2,-4,-6]
    +
    (fmap negate . fmap (*2)) [1,2,3] ==> (map negate . map (*2)) [1,2,3]
    +                                  ==> map negate (map (*2) [1,2,3])
    +                                  ==> map negate [2,4,6]
    +                                  ==> [-2,-4,-6]

    The second functor law turns out to hold in this particular case. In fact, it holds in all cases (exercise!).

    In general, the second functor law says that composing two functions first and then applying fmap must yield the same result as performing fmap on those functions and then composing the resulting function. In other words, the order of applying fmap and composing doesn’t matter. (These two operations are said to commute.)

    There are also higher-order functions that fail to satisfy functor laws. Consider the function badMap:

    -
    badMap :: (a -> b) -> [a] -> [b]
    -badMap f [] = []
    -badMap f (x:y:xs) = f x : badMap f xs
    -badMap f (x:xs) = f x : badMap f xs
    +
    badMap :: (a -> b) -> [a] -> [b]
    +badMap f [] = []
    +badMap f (x:y:xs) = f x : badMap f xs
    +badMap f (x:xs) = f x : badMap f xs

    This function violates the first functor law. For instance:

    -
    badMap id [1,2,3] ==> badMap id (1:2:[3])
    -                  ==> id 1 : badMap id [3]
    -                  ==> 1 : badMap id [3]
    -                  ==> 1 : badMap id (3:[])
    -                  ==> 1 : id 3 : badMap id []
    -                  ==> 1 : 3 : []
    -                  ==> [1,3]
    +
    badMap id [1,2,3] ==> badMap id (1:2:[3])
    +                  ==> id 1 : badMap id [3]
    +                  ==> 1 : badMap id [3]
    +                  ==> 1 : badMap id (3:[])
    +                  ==> 1 : id 3 : badMap id []
    +                  ==> 1 : 3 : []
    +                  ==> [1,3]

    Applying badMap id to the list [1,2,3] changed the list as the element 2 was dropped.

    As mentioned, Haskell compilers can’t detect if a functor obeys its laws or not. A Haskell compiler would happily accept an instance Functor [] that used badMap instead of map as the fmap implementation. This is a limitation of the type system of Haskell. There are technologies such as LiquidHaskell or dependently typed languages such as Agda, Idris, Coq, or Lean that could actually enforce functor laws so that unlawful functor instances wouldn’t compile. These technologies are outside the scope of this course, however.

    12.4 Sidenote: Kinds

    Remember that Functor was a class for type constructors. If we try to define an instance of Functor for a type, we get an error:

    -
    Prelude> instance Functor Int where
    -
    -<interactive>:1:18: error:
    -    • Expected kind ‘* -> *’, but ‘Int’ has kind ‘*’
    -    • In the first argument of ‘Functor’, namely ‘Int’
    -      In the instance declaration for ‘Functor Int’
    +
    Prelude> instance Functor Int where
    +
    +<interactive>:1:18: error:
    +    • Expected kind ‘* -> *’, but ‘Int’ has kind ‘*’
    +    • In the first argument of ‘Functor’, namely ‘Int’
    +      In the instance declaration for ‘Functor Int’

    The error message talks about kinds. Kinds are types of types. A type like Int, Bool or Maybe Int that can contain values has kind *. A type constructor has a kind that looks like a function, for example, Maybe has kind * -> *. This means that the Maybe type constructor must be applied to a type of kind * to get a type of kind *.

    We can ask GHCi for the kinds of types:

    -
    Prelude> :kind Int
    -Int :: *
    -Prelude> :kind Maybe
    -Maybe :: * -> *
    -Prelude> :kind Maybe Int
    -Maybe Int :: *
    +
    Prelude> :kind Int
    +Int :: *
    +Prelude> :kind Maybe
    +Maybe :: * -> *
    +Prelude> :kind Maybe Int
    +Maybe Int :: *

    If we ask GHCi for info about the Functor class, it tells us that instances of Functor must have kind * -> *:

    -
    Prelude> :info Functor
    -class Functor (f :: * -> *) where
    -  fmap :: (a -> b) -> f a -> f b
    -...
    +
    Prelude> :info Functor
    +class Functor (f :: * -> *) where
    +  fmap :: (a -> b) -> f a -> f b
    +...

    Here are some examples of even more complex kinds.

    -
    -- multiple type parameters
    -Prelude> :kind Either
    -Either :: * -> * -> *
    -Prelude> data Either3 a b c = Left a | Middle b | Right c
    -Prelude> :kind Either3
    -Either3 :: * -> * -> * -> *
    --- a type parameter of kind *->*
    -Prelude> data IntInside f = IntInside (f Int)
    -Prelude> :kind IntInside
    -IntInside :: (* -> *) -> *
    +
    -- multiple type parameters
    +Prelude> :kind Either
    +Either :: * -> * -> *
    +Prelude> data Either3 a b c = Left a | Middle b | Right c
    +Prelude> :kind Either3
    +Either3 :: * -> * -> * -> *
    +-- a type parameter of kind *->*
    +Prelude> data IntInside f = IntInside (f Int)
    +Prelude> :kind IntInside
    +IntInside :: (* -> *) -> *

    You won’t bump into kinds that much in Haskell programming, but sometimes you’ll see error messages that talk about kinds, so it’s good to know what they are.

    12.5 Foldable, Again

    We briefly covered the class Foldable, which occurs in many type signatures of basic functions, in part 1. For example:

    -
    length :: Foldable t => t a -> Int
    -sum :: (Foldable t, Num a) => t a -> a
    -minimum :: (Foldable t, Ord a) => t a -> a
    -foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
    +
    length :: Foldable t => t a -> Int
    +sum :: (Foldable t, Num a) => t a -> a
    +minimum :: (Foldable t, Ord a) => t a -> a
    +foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m

    From these type signatures we can see that Foldable, just like Functor, is a class for type constructors (things of kind * -> *). The essence of Foldable is to be a class for things you can fold over. The class definition could be as simple as

    -
    class Foldable (t :: *->*) where
    -  foldr :: (a -> b -> b) -> b -> t a -> b
    +
    class Foldable (t :: *->*) where
    +  foldr :: (a -> b -> b) -> b -> t a -> b

    However, for performance reasons, the class contains many methods (you can see them yourself by checking :info Foldable in GHCi!), but when we’re defining an instance for Foldable it’s enought to define just foldr.

    Another way of thinking of the Foldable class is processing elements left-to-right, in other words, if Functor was the class for containers, Foldable is the class for ordered containers.

    As an example, let’s implement Functor and Foldable for our own pair type.

    -
    data Pair a = Pair a a
    -  deriving Show
    -
    -instance Functor Pair where
    -  -- fmap f applies f to all values
    -  fmap f (Pair x y) = Pair (f x) (f y)
    -
    -instance Foldable Pair where
    -  -- just like applying foldr over a list of length 2
    -  foldr f initialValue (Pair x y) = f x (f y initialValue)
    -
    --- an example function that uses both instances
    -doubleAndCount :: (Functor f, Foldable f) => f Int -> Int
    -doubleAndCount = sum . fmap (*2)
    +
    data Pair a = Pair a a
    +  deriving Show
    +
    +instance Functor Pair where
    +  -- fmap f applies f to all values
    +  fmap f (Pair x y) = Pair (f x) (f y)
    +
    +instance Foldable Pair where
    +  -- just like applying foldr over a list of length 2
    +  foldr f initialValue (Pair x y) = f x (f y initialValue)
    +
    +-- an example function that uses both instances
    +doubleAndCount :: (Functor f, Foldable f) => f Int -> Int
    +doubleAndCount = sum . fmap (*2)

    Now, we can use Pair almost wherever we can use a list:

    -
    fmap (+1) (Pair 3 6)   ==> Pair 4 7
    -fmap (+1) [3,6]        ==> [4,7]
    -
    -foldr (*) 1 (Pair 3 6) ==> 18
    -foldr (*) 1 [3,6]      ==> 18
    -
    -length (Pair 3 6)      ==> 2
    -length [3,6]           ==> 2
    -
    -minimum (Pair 3 6)     ==> 3
    -minimum [3,6]          ==> 3
    -
    -doubleAndCount (Pair 3 6)  ==> 18
    -doubleAndCount [3,6]       ==> 18
    +
    fmap (+1) (Pair 3 6)   ==> Pair 4 7
    +fmap (+1) [3,6]        ==> [4,7]
    +
    +foldr (*) 1 (Pair 3 6) ==> 18
    +foldr (*) 1 [3,6]      ==> 18
    +
    +length (Pair 3 6)      ==> 2
    +length [3,6]           ==> 2
    +
    +minimum (Pair 3 6)     ==> 3
    +minimum [3,6]          ==> 3
    +
    +doubleAndCount (Pair 3 6)  ==> 18
    +doubleAndCount [3,6]       ==> 18

    Other types we’ve met that are Foldable include Data.Map and Data.Array.

    @@ -2547,10 +2548,10 @@ this topic connects to equational reasoning discussed in chapter 10.

    Which code snippet completes the next Functor instance?

    -
    data Container x = Things x [x]
    -
    -instance Functor Container where
    -  ????
    +
    data Container x = Things x [x]
    +
    +instance Functor Container where
    +  ????
    1. fmap f (Things x ys) = Things (f x) [f x] @@ -2578,7 +2579,7 @@ this topic connects to equational reasoning discussed in chapter 10.

    What’s the kind of Foo?

    -
    data Foo x = FooConst
    +
    data Foo x = FooConst
    1. * @@ -2591,7 +2592,7 @@ this topic connects to equational reasoning discussed in chapter 10.

    What’s the kind of Bar?

    -
    data Bar = Baz | Qux Int
    +
    data Bar = Baz | Qux Int
    1. * @@ -2619,10 +2620,10 @@ this topic connects to equational reasoning discussed in chapter 10.

    Which code snippet completes the next Foldable instance?

    -
    data Container x = Things x [x]
    -
    -instance Foldable Container where
    -  ????
    +
    data Container x = Things x [x]
    +
    +instance Foldable Container where
    +  ????
    1. foldr f z (Things x ys) = f x z @@ -2658,751 +2659,751 @@ this topic connects to equational reasoning discussed in chapter 10.

      13.1 Example 1: Maybes

      When working with many Maybe values, the code tends to become a bit messy. Let’s look at some examples. First, we combine some functions returning Maybe String. Note the nested case we need in stealSecret: It’s not fun to write.

      -
      -- Try to login with a password.
      --- `Just username` on success, `Nothing` otherwise.
      -login :: String -> Maybe String
      -login "f4bulous!" = Just "unicorn73"
      -login "swordfish" = Just "megahacker"
      -login _           = Nothing
      -
      --- Get a secret associated with a user.
      --- Not all users have secrets.
      -secret :: String -> Maybe String
      -secret "megahacker" = Just "I like roses"
      -secret _            = Nothing
      -
      --- Login and return the user's secret, if any
      -stealSecret :: String -> Maybe String
      -stealSecret password =
      -  case login password of
      -    Nothing -> Nothing
      -    Just user -> case secret user of
      -                   Nothing -> Nothing
      -                   Just s -> Just ("Stole secret: "++s)
      -
      stealSecret "swordfish"  ==>  Just "Stole secret: I like roses"
      -stealSecret "f4bulous!"  ==>  Nothing
      -stealSecret "wrong_password"  ==>  Nothing
      +
      -- Try to login with a password.
      +-- `Just username` on success, `Nothing` otherwise.
      +login :: String -> Maybe String
      +login "f4bulous!" = Just "unicorn73"
      +login "swordfish" = Just "megahacker"
      +login _           = Nothing
      +
      +-- Get a secret associated with a user.
      +-- Not all users have secrets.
      +secret :: String -> Maybe String
      +secret "megahacker" = Just "I like roses"
      +secret _            = Nothing
      +
      +-- Login and return the user's secret, if any
      +stealSecret :: String -> Maybe String
      +stealSecret password =
      +  case login password of
      +    Nothing -> Nothing
      +    Just user -> case secret user of
      +                   Nothing -> Nothing
      +                   Just s -> Just ("Stole secret: "++s)
      +
      stealSecret "swordfish"  ==>  Just "Stole secret: I like roses"
      +stealSecret "f4bulous!"  ==>  Nothing
      +stealSecret "wrong_password"  ==>  Nothing

      Next up, we modify a list of pairs. We use the Maybe-returning function lookup from the Prelude. Here we have an if inside a case instead of a nested case.

      -
      -- Get the value corresponding to a key from a key-value list.
      -lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
      -
      -- Set the value of key to val in the given key-value list,
      --- but only if val is larger than the current value!
      -increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      -increase key val assocs =
      -  case lookup key assocs
      -  of Nothing -> Nothing
      -     Just x -> if (val < x)
      -                then Nothing
      -                else Just ((key,val) : delete (key,x) assocs)
      +
      -- Get the value corresponding to a key from a key-value list.
      +lookup :: (Eq a) => a -> [(a, b)] -> Maybe b
      +
      -- Set the value of key to val in the given key-value list,
      +-- but only if val is larger than the current value!
      +increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      +increase key val assocs =
      +  case lookup key assocs
      +  of Nothing -> Nothing
      +     Just x -> if (val < x)
      +                then Nothing
      +                else Just ((key,val) : delete (key,x) assocs)

      This type of code is pretty common, and usually repeats the same pattern: if any intermediate result is Nothing, the whole result is Nothing. Let’s try to make writing code like this easier by defining a chaining operator ?>. The chaining operator takes a result and the next step of computation, and only runs the next step if the result was a Just value.

      -
      (?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
      --- if we failed, don't even bother running the next step:
      -Nothing ?> _ = Nothing
      --- otherwise run the next step:
      -Just x  ?> f = f x
      +
      (?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
      +-- if we failed, don't even bother running the next step:
      +Nothing ?> _ = Nothing
      +-- otherwise run the next step:
      +Just x  ?> f = f x

      The chaining operator streamlines our examples nicely. Note how we can define simple helper functions that take care of one step of the computation instead of writing one big expression.

      -
      stealSecret :: String -> Maybe String
      -stealSecret password =
      -    login password ?>
      -    secret ?>
      -    decorate
      -  where decorate s = Just ("Stole secret: "++s)
      -
      increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      -increase key val assocs =
      -    lookup key assocs ?>
      -    check ?>
      -    buildResult
      -  where check x
      -           | val < x   = Nothing
      -           | otherwise = Just x
      -        buildResult x = Just ((key,val) : delete (key,x) assocs)
      +
      stealSecret :: String -> Maybe String
      +stealSecret password =
      +    login password ?>
      +    secret ?>
      +    decorate
      +  where decorate s = Just ("Stole secret: "++s)
      +
      increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      +increase key val assocs =
      +    lookup key assocs ?>
      +    check ?>
      +    buildResult
      +  where check x
      +           | val < x   = Nothing
      +           | otherwise = Just x
      +        buildResult x = Just ((key,val) : delete (key,x) assocs)

      Here’s another example: safe list indexing built from safeHead and safeTail:

      -
      safeHead :: [a] -> Maybe a
      -safeHead [] = Nothing
      -safeHead (x:xs) = Just x
      -
      -safeTail :: [a] -> Maybe [a]
      -safeTail [] = Nothing
      -safeTail (x:xs) = Just xs
      -
      -safeThird :: [a] -> Maybe a
      -safeThird xs = safeTail xs ?> safeTail ?> safeHead
      -
      -safeNth :: Int -> [a] -> Maybe a
      -safeNth 0 xs = safeHead xs
      -safeNth n xs = safeTail xs ?> safeNth (n-1)
      -
      safeThird [1,2,3,4]
      -  ==> Just 3
      -safeThird [1,2]
      -  ==> Nothing
      -safeNth 5 [1..10]
      -  ==> Just 6
      -safeNth 11 [1..10]
      -  ==> Nothing
      +
      safeHead :: [a] -> Maybe a
      +safeHead [] = Nothing
      +safeHead (x:xs) = Just x
      +
      +safeTail :: [a] -> Maybe [a]
      +safeTail [] = Nothing
      +safeTail (x:xs) = Just xs
      +
      +safeThird :: [a] -> Maybe a
      +safeThird xs = safeTail xs ?> safeTail ?> safeHead
      +
      +safeNth :: Int -> [a] -> Maybe a
      +safeNth 0 xs = safeHead xs
      +safeNth n xs = safeTail xs ?> safeNth (n-1)
      +
      safeThird [1,2,3,4]
      +  ==> Just 3
      +safeThird [1,2]
      +  ==> Nothing
      +safeNth 5 [1..10]
      +  ==> Just 6
      +safeNth 11 [1..10]
      +  ==> Nothing

      PS. note that ?> associates to the left as is the default in Haskell. That means that op ?> f ?> g means (op ?> f) ?> g. The alternative, op ?> (f ?> g) would not even type check!

      Sidenote: this ?> operator expresses the if-result pattern that’s very common in other languages. Here is how one would write op val ?> f in Python and Java.

      -
      # Python
      -x = op(val)
      -if x:
      -  f x
      -
      // Java
      -Object x = op(val);
      -if (x != null) {
      -  f(x);
      -}
      +
      # Python
      +x = op(val)
      +if x:
      +  f x
      +
      // Java
      +Object x = op(val);
      +if (x != null) {
      +  f(x);
      +}

      The difference between the if-result pattern and our ?> is that we use the Nothing value to explicitly signal failure, instead of relying on the fact that any variable can be None (or False) in Python, or that any Object reference can be null in Java.

      13.2 Example 2: Logging

      Let’s explore the concept of chaining with another example: logging. The type Logger represents a value plus a list of log messages (produced by the computation that produced the value).

      -
      -- Logger definition
      -data Logger a = Logger [String] a  deriving Show
      -
      -getVal :: Logger a -> a
      -getVal (Logger _ a) = a
      -getLog :: Logger a -> [String]
      -getLog (Logger s _) = s
      -
      --- Primitive operations:
      -nomsg :: a -> Logger a
      -nomsg x = Logger [] x        -- a value, no message
      -
      -annotate :: String -> a -> Logger a
      -annotate s x = Logger [s] x  -- a value and a message
      -
      -msg :: String -> Logger ()
      -msg s = Logger [s] ()        -- just a message
      +
      -- Logger definition
      +data Logger a = Logger [String] a  deriving Show
      +
      +getVal :: Logger a -> a
      +getVal (Logger _ a) = a
      +getLog :: Logger a -> [String]
      +getLog (Logger s _) = s
      +
      +-- Primitive operations:
      +nomsg :: a -> Logger a
      +nomsg x = Logger [] x        -- a value, no message
      +
      +annotate :: String -> a -> Logger a
      +annotate s x = Logger [s] x  -- a value and a message
      +
      +msg :: String -> Logger ()
      +msg s = Logger [s] ()        -- just a message

      Here’s a login function that logs some details about the usernames and passwords it processes. Note how we run into complicated code in login when we need to handle multiple Logger values.

      -
      validateUser :: String -> Logger Bool
      -validateUser "paul.atreides" = annotate "Valid user" True
      -validateUser "ninja" = nomsg True
      -validateUser u = annotate ("Invalid user: "++u) False
      -
      -checkPassword :: String -> String -> Logger Bool
      -checkPassword "paul.atreides" "muad'dib" = annotate "Password ok" True
      -checkPassword "ninja"         ""         = annotate "Password ok" True
      -checkPassword _               pass       = annotate ("Password wrong: "++pass) False
      -
      -login :: String -> String -> Logger Bool
      -login user password =
      -  let validation = validateUser user
      -  in if (getVal validation)
      -       then let check = checkPassword user password
      -            in Logger (getLog validation ++ getLog check) (getVal check)
      -       else validation
      -
      login "paul.atreides" "muad'dib"
      -  ==> Logger ["Valid user","Password ok"] True
      -login "paul.atreides" "arrakis"
      -  ==> Logger ["Valid user","Password wrong: arrakis"] False
      -login "ninja" ""
      -  ==> Logger ["Password ok"] True
      -login "leto.atreides" "paul"
      -  ==> Logger ["Invalid user: leto.atreides"] False
      +
      validateUser :: String -> Logger Bool
      +validateUser "paul.atreides" = annotate "Valid user" True
      +validateUser "ninja" = nomsg True
      +validateUser u = annotate ("Invalid user: "++u) False
      +
      +checkPassword :: String -> String -> Logger Bool
      +checkPassword "paul.atreides" "muad'dib" = annotate "Password ok" True
      +checkPassword "ninja"         ""         = annotate "Password ok" True
      +checkPassword _               pass       = annotate ("Password wrong: "++pass) False
      +
      +login :: String -> String -> Logger Bool
      +login user password =
      +  let validation = validateUser user
      +  in if (getVal validation)
      +       then let check = checkPassword user password
      +            in Logger (getLog validation ++ getLog check) (getVal check)
      +       else validation
      +
      login "paul.atreides" "muad'dib"
      +  ==> Logger ["Valid user","Password ok"] True
      +login "paul.atreides" "arrakis"
      +  ==> Logger ["Valid user","Password wrong: arrakis"] False
      +login "ninja" ""
      +  ==> Logger ["Password ok"] True
      +login "leto.atreides" "paul"
      +  ==> Logger ["Invalid user: leto.atreides"] False

      Let’s try to streamline this code by defining a chaining operator for Logger. The important thing when doing multiple Logger operations is to preserve all the logs. Here’s a chaining operator, #>, and an example of how it can be used to log some arithmetic computations.

      -
      (#>) :: Logger a -> (a -> Logger b) -> Logger b
      -Logger la a #> f = let Logger lb b = f a  -- feed value to next step
      -                   in Logger (la++lb) b   -- bundle result with all messages
      -
      -- square a number and log a message about it
      -square :: Int -> Logger Int
      -square val = annotate (show val ++ "^2") (val^2)
      -
      --- add 1 to a number and log a message about it
      -add :: Int -> Logger Int
      -add val = annotate (show val ++ "+1") (val+1)
      -
      --- double a number and log a message about it
      -double :: Int -> Logger Int
      -double val = annotate (show val ++ "*2") (val*2)
      -
      --- compute the expression 2*(x^2+1) with logging
      -compute :: Int -> Logger Int
      -compute x =
      -    square x
      -    #> add
      -    #> double
      -
      compute 3
      -  ==> Logger ["3^2","9+1","10*2"] 20
      +
      (#>) :: Logger a -> (a -> Logger b) -> Logger b
      +Logger la a #> f = let Logger lb b = f a  -- feed value to next step
      +                   in Logger (la++lb) b   -- bundle result with all messages
      +
      -- square a number and log a message about it
      +square :: Int -> Logger Int
      +square val = annotate (show val ++ "^2") (val^2)
      +
      +-- add 1 to a number and log a message about it
      +add :: Int -> Logger Int
      +add val = annotate (show val ++ "+1") (val+1)
      +
      +-- double a number and log a message about it
      +double :: Int -> Logger Int
      +double val = annotate (show val ++ "*2") (val*2)
      +
      +-- compute the expression 2*(x^2+1) with logging
      +compute :: Int -> Logger Int
      +compute x =
      +    square x
      +    #> add
      +    #> double
      +
      compute 3
      +  ==> Logger ["3^2","9+1","10*2"] 20

      We can streamline login quite a bit by using #>. Note how we don’t need to worry about combining logs together. Also note how we use a lambda expression instead of defining a helper function.

      -
      login :: String -> String -> Logger Bool
      -login user password =
      -  validateUser user
      -  #>
      -  \valid -> if valid then checkPassword user password
      -                     else nomsg False
      +
      login :: String -> String -> Logger Bool
      +login user password =
      +  validateUser user
      +  #>
      +  \valid -> if valid then checkPassword user password
      +                     else nomsg False

      To ramp things up a bit, let’s use Logger in a recursive list processing function. Here’s a logging version of filter. Note how the code chains a log message before the recursive call in order to keep the order of log entries nice.

      -
      -- sometimes you don't need the previous value:
      -(##>) :: Logger a -> Logger b -> Logger b
      -Logger la _ ##> Logger lb b = Logger (la++lb) b
      -
      -filterLog :: (Eq a, Show a) => (a -> Bool) -> [a] -> Logger [a]
      -filterLog f [] = nomsg []
      -filterLog f (x:xs)
      -   | f x       = msg ("keeping "++show x) ##> filterLog f xs #> (\xs' -> nomsg (x:xs'))
      -   | otherwise = msg ("dropping "++show x) ##> filterLog f xs
      -
      filterLog (>0) [1,-2,3,-4,0]
      -  ==> Logger ["keeping 1","dropping -2","keeping 3","dropping -4","dropping 0"] [1,3]
      +
      -- sometimes you don't need the previous value:
      +(##>) :: Logger a -> Logger b -> Logger b
      +Logger la _ ##> Logger lb b = Logger (la++lb) b
      +
      +filterLog :: (Eq a, Show a) => (a -> Bool) -> [a] -> Logger [a]
      +filterLog f [] = nomsg []
      +filterLog f (x:xs)
      +   | f x       = msg ("keeping "++show x) ##> filterLog f xs #> (\xs' -> nomsg (x:xs'))
      +   | otherwise = msg ("dropping "++show x) ##> filterLog f xs
      +
      filterLog (>0) [1,-2,3,-4,0]
      +  ==> Logger ["keeping 1","dropping -2","keeping 3","dropping -4","dropping 0"] [1,3]

      13.3 Example 3: Keeping State

      In the previous example we just wrote some state (the log). Sometimes we need computations that change some sort of shared state. Let’s look at accounts in a small bank. We’ll first define a datatype for the state of the bank: the balances of all accounts, as a map from account name to balance.

      -
      import qualified Data.Map as Map
      -
      -data Bank = Bank (Map.Map String Int)
      -  deriving Show
      +
      import qualified Data.Map as Map
      +
      +data Bank = Bank (Map.Map String Int)
      +  deriving Show

      Here’s how we can deposit some money to an account. We use the function adjust from Data.Map to modify the map.

      -
      -- Apply a function to one value in a map
      -Map.adjust :: Ord k => (a -> a) -> k -> Map.Map k a -> Map.Map k a
      -
      deposit :: String -> Int -> Bank -> Bank
      -deposit accountName amount (Bank accounts) =
      -  Bank (Map.adjust (\x -> x+amount) accountName accounts)
      +
      -- Apply a function to one value in a map
      +Map.adjust :: Ord k => (a -> a) -> k -> Map.Map k a -> Map.Map k a
      +
      deposit :: String -> Int -> Bank -> Bank
      +deposit accountName amount (Bank accounts) =
      +  Bank (Map.adjust (\x -> x+amount) accountName accounts)

      Withdrawing money is a bit more complicated, since we want to handle some special cases like the account not existing, or the account not having enough money. We use the library function findWithDefault to help us along.

      -
      -- Fetch the value corresponding to a key from a map,
      --- or a default value in case the key does not exist
      -Map.findWithDefault :: Ord k => a -> k -> Map.Map k a -> a
      -
      withdraw :: String -> Int -> Bank -> (Int,Bank)
      -withdraw accountName amount (Bank accounts) =
      -  let -- balance is 0 for a nonexistant account
      -      balance = Map.findWithDefault 0 accountName accounts
      -      -- can't withdraw over balance
      -      withdrawal = min amount balance
      -      newAccounts = Map.adjust (\x -> x-withdrawal) accountName accounts
      -  in (withdrawal, Bank newAccounts)
      +
      -- Fetch the value corresponding to a key from a map,
      +-- or a default value in case the key does not exist
      +Map.findWithDefault :: Ord k => a -> k -> Map.Map k a -> a
      +
      withdraw :: String -> Int -> Bank -> (Int,Bank)
      +withdraw accountName amount (Bank accounts) =
      +  let -- balance is 0 for a nonexistant account
      +      balance = Map.findWithDefault 0 accountName accounts
      +      -- can't withdraw over balance
      +      withdrawal = min amount balance
      +      newAccounts = Map.adjust (\x -> x-withdrawal) accountName accounts
      +  in (withdrawal, Bank newAccounts)

      Finally, let’s write a function that takes at most 100 money from one account, splits the money in half, and deposits it in two accounts. Pay attention to how we need to carefully thread the different versions of the bank, bank, bank1, bank2 and bank3 to make sure all transactions happen in the right order.

      -
      share :: String -> String -> String -> Bank -> Bank
      -share from to1 to2 bank =
      -  let (amount,bank1) = withdraw from 100 bank
      -      half = div amount 2
      -      -- carefully preserve all money, even if amount was an odd number
      -      rest = amount-half
      -      bank2 = deposit to1 half bank1
      -      bank3 = deposit to2 rest bank2
      -  in bank3
      -
      share "wotan" "siegfried" "brunhilde"
      -      (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",1000)]))
      -   ==> Bank (Map.fromList [("brunhilde",50),("siegfried",50),("wotan",900)])
      -
      -share "wotan" "siegfried" "brunhilde"
      -      (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",91)]))
      -   ==> Bank (Map.fromList [("brunhilde",46),("siegfried",45),("wotan",0)])
      +
      share :: String -> String -> String -> Bank -> Bank
      +share from to1 to2 bank =
      +  let (amount,bank1) = withdraw from 100 bank
      +      half = div amount 2
      +      -- carefully preserve all money, even if amount was an odd number
      +      rest = amount-half
      +      bank2 = deposit to1 half bank1
      +      bank3 = deposit to2 rest bank2
      +  in bank3
      +
      share "wotan" "siegfried" "brunhilde"
      +      (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",1000)]))
      +   ==> Bank (Map.fromList [("brunhilde",50),("siegfried",50),("wotan",900)])
      +
      +share "wotan" "siegfried" "brunhilde"
      +      (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",91)]))
      +   ==> Bank (Map.fromList [("brunhilde",46),("siegfried",45),("wotan",0)])

      Code like this turns up often in Haskell when you’re doing serial updates to one value, while also performing some other computations on the side. It’s easy to make a mistake, and the type system won’t help you if you e.g. reuse the bank1 value. Let’s rewrite share so that we don’t need to refer to the bank itself. We can again use the same chaining idea to accomplish this.

      -
      -- `BankOp a` is an operation that transforms a Bank value,
      --- while returning a value of type `a`
      -data BankOp a = BankOp (Bank -> (a,Bank))
      -
      --- running a BankOp on a Bank
      -runBankOp :: BankOp a -> Bank -> (a,Bank)
      -runBankOp (BankOp f) bank = f bank
      -
      --- Running one BankOp after another
      -(+>>) :: BankOp a -> BankOp b -> BankOp b
      -op1 +>> op2 = BankOp combined
      -  where combined bank = let (_,bank1) = runBankOp op1 bank
      -                        in runBankOp op2 bank1
      -
      --- Running a parameterized BankOp, using the value returned
      --- by a previous BankOp.  The implementation is a bit tricky
      --- but it's enough to understand how +> is used for now.
      -(+>) :: BankOp a -> (a -> BankOp b) -> BankOp b
      -op +> parameterized = BankOp combined
      -  where combined bank = let (a,bank1) = runBankOp op bank
      -                        in runBankOp (parameterized a) bank1
      -
      --- Make a BankOp out of deposit.
      --- There is no return value so we use ().
      -depositOp :: String -> Int -> BankOp ()
      -depositOp accountName amount = BankOp depositHelper
      -  where depositHelper bank = ((), deposit accountName amount bank)
      -
      --- Make a BankOp out of withdraw. Note how
      ---   withdraw accountName amount :: Bank -> (Int,Bank)
      --- is almost a BankOp already!
      -withdrawOp :: String -> Int -> BankOp Int
      -withdrawOp accountName amount = BankOp (withdraw accountName amount)
      +
      -- `BankOp a` is an operation that transforms a Bank value,
      +-- while returning a value of type `a`
      +data BankOp a = BankOp (Bank -> (a,Bank))
      +
      +-- running a BankOp on a Bank
      +runBankOp :: BankOp a -> Bank -> (a,Bank)
      +runBankOp (BankOp f) bank = f bank
      +
      +-- Running one BankOp after another
      +(+>>) :: BankOp a -> BankOp b -> BankOp b
      +op1 +>> op2 = BankOp combined
      +  where combined bank = let (_,bank1) = runBankOp op1 bank
      +                        in runBankOp op2 bank1
      +
      +-- Running a parameterized BankOp, using the value returned
      +-- by a previous BankOp.  The implementation is a bit tricky
      +-- but it's enough to understand how +> is used for now.
      +(+>) :: BankOp a -> (a -> BankOp b) -> BankOp b
      +op +> parameterized = BankOp combined
      +  where combined bank = let (a,bank1) = runBankOp op bank
      +                        in runBankOp (parameterized a) bank1
      +
      +-- Make a BankOp out of deposit.
      +-- There is no return value so we use ().
      +depositOp :: String -> Int -> BankOp ()
      +depositOp accountName amount = BankOp depositHelper
      +  where depositHelper bank = ((), deposit accountName amount bank)
      +
      +-- Make a BankOp out of withdraw. Note how
      +--   withdraw accountName amount :: Bank -> (Int,Bank)
      +-- is almost a BankOp already!
      +withdrawOp :: String -> Int -> BankOp Int
      +withdrawOp accountName amount = BankOp (withdraw accountName amount)

      Let’s see how chaining works with these bank operations.

      -
      Prelude> let bank = Bank (Map.fromList [("edsger",10),("grace",50)])
      -
      --- Running a number of operations using +>>
      -
      -Prelude> runBankOp (depositOp "edsger" 1) bank
      -((),Bank (fromList [("edsger",11),("grace",50)]))
      -
      -Prelude> runBankOp (depositOp "edsger" 1 +>> depositOp "grace" 1) bank
      -((),Bank (fromList [("edsger",11),("grace",51)]))
      -
      -Prelude> runBankOp (depositOp "edsger" 1 +>> depositOp "grace" 1 +>> withdrawOp "edsger" 11) bank
      -(11,Bank (fromList [("edsger",0),("grace",51)]))
      -
      --- Using +> to implement a transfer from one account to the other:
      -
      -Prelude> runBankOp (withdrawOp "edsger" 5 +> depositOp "grace") bank
      -((),Bank (fromList [("edsger",5),("grace",55)]))
      -
      -Prelude> runBankOp (withdrawOp "edsger" 100 +> depositOp "grace") bank
      -((),Bank (fromList [("edsger",0),("grace",60)]))
      +
      Prelude> let bank = Bank (Map.fromList [("edsger",10),("grace",50)])
      +
      +-- Running a number of operations using +>>
      +
      +Prelude> runBankOp (depositOp "edsger" 1) bank
      +((),Bank (fromList [("edsger",11),("grace",50)]))
      +
      +Prelude> runBankOp (depositOp "edsger" 1 +>> depositOp "grace" 1) bank
      +((),Bank (fromList [("edsger",11),("grace",51)]))
      +
      +Prelude> runBankOp (depositOp "edsger" 1 +>> depositOp "grace" 1 +>> withdrawOp "edsger" 11) bank
      +(11,Bank (fromList [("edsger",0),("grace",51)]))
      +
      +-- Using +> to implement a transfer from one account to the other:
      +
      +Prelude> runBankOp (withdrawOp "edsger" 5 +> depositOp "grace") bank
      +((),Bank (fromList [("edsger",5),("grace",55)]))
      +
      +Prelude> runBankOp (withdrawOp "edsger" 100 +> depositOp "grace") bank
      +((),Bank (fromList [("edsger",0),("grace",60)]))

      Note how a value of type BankOp represents a process that transforms the bank. The initial state of the bank must be supplied using runBankOp. This makes sense because BankOp transformations can be composed, unlike Bank states. Having to use runBankOp makes the distinction between defining operations and executing them clearer.

      Now that we’re familiar with manipulating BankOp values, we can implement share as a BankOp. We implement a helper distributeOp to make the code a bit neater.

      -
      -- distribute amount to two accounts
      -distributeOp :: String -> String -> Int -> BankOp ()
      -distributeOp to1 to2 amount =
      -  depositOp to1 half
      -  +>>
      -  depositOp to2 rest
      -  where half = div amount 2
      -        rest = amount - half
      -
      -shareOp :: String -> String -> String -> BankOp ()
      -shareOp from to1 to2 =
      -  withdrawOp from 100
      -  +>
      -  distributeOp to1 to2
      -
      runBankOp (shareOp "wotan" "siegfried" "brunhilde")
      -          (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",1000)]))
      -  ==> ((),Bank (Map.fromList [("brunhilde",50),("siegfried",50),("wotan",900)]))
      -
      -runBankOp (shareOp "wotan" "siegfried" "brunhilde")
      -          (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",91)]))
      -  ==> ((),Bank (Map.fromList [("brunhilde",46),("siegfried",45),("wotan",0)]))
      +
      -- distribute amount to two accounts
      +distributeOp :: String -> String -> Int -> BankOp ()
      +distributeOp to1 to2 amount =
      +  depositOp to1 half
      +  +>>
      +  depositOp to2 rest
      +  where half = div amount 2
      +        rest = amount - half
      +
      +shareOp :: String -> String -> String -> BankOp ()
      +shareOp from to1 to2 =
      +  withdrawOp from 100
      +  +>
      +  distributeOp to1 to2
      +
      runBankOp (shareOp "wotan" "siegfried" "brunhilde")
      +          (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",1000)]))
      +  ==> ((),Bank (Map.fromList [("brunhilde",50),("siegfried",50),("wotan",900)]))
      +
      +runBankOp (shareOp "wotan" "siegfried" "brunhilde")
      +          (Bank (Map.fromList [("brunhilde",0),("siegfried",0),("wotan",91)]))
      +  ==> ((),Bank (Map.fromList [("brunhilde",46),("siegfried",45),("wotan",0)]))

      That was pretty clean wasn’t it? We don’t need to mention the bank at all, we can almost program as if in an imperative language while staying completely pure.

      You can find all of this code in the course repository under exercises/Examples/Bank.hs.

      13.4 Finally: The Monad Type Class

      We’ve now seen three different types with a chaining operation:

      -
      (?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
      -(#>) :: Logger a -> (a -> Logger b) -> Logger b
      -(+>) :: BankOp a -> (a -> BankOp b) -> BankOp b
      +
      (?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
      +(#>) :: Logger a -> (a -> Logger b) -> Logger b
      +(+>) :: BankOp a -> (a -> BankOp b) -> BankOp b

      Just like previously with map and Functor, there is a type class that captures this pattern. Note that Monad is a class for type constructors, just like Functor.

      -
      class Monad m where
      -  (>>=) :: m a -> (a -> m b) -> m b
      +
      class Monad m where
      +  (>>=) :: m a -> (a -> m b) -> m b

      There are some additional operations in Monad too:

      -
        -- lift a normal value into the monad
      -  return :: a -> m a
      -  -- simpler chaining (like our ##>)
      -  (>>) :: m a -> m b -> m b
      -  a >> b  =  a >>= \_ -> b     -- remember: _ means ignored argument
      +
        -- lift a normal value into the monad
      +  return :: a -> m a
      +  -- simpler chaining (like our ##>)
      +  (>>) :: m a -> m b -> m b
      +  a >> b  =  a >>= \_ -> b     -- remember: _ means ignored argument

      Recall that the Functor class was about a generic map operation. Similarly, the Monad class is just about a generic chaining operation.

      -
      fmap :: Functor f => (a->b) -> f a -> f b
      -(>>=) :: Monad m => m a -> (a -> m b) -> m b
      +
      fmap :: Functor f => (a->b) -> f a -> f b
      +(>>=) :: Monad m => m a -> (a -> m b) -> m b

      The expression operation >>= next takes a monadic operation operation :: m a, and does some further computation with the value that it produces using next :: a -> m b. If this feels too abstract, just recall how chaining works for Maybe:

      -
      (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
      --- if we failed, don't even bother running the next step
      -Nothing >>= _ = Nothing
      --- otherwise run the next step
      -Just x  >>= f = f x
      +
      (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
      +-- if we failed, don't even bother running the next step
      +Nothing >>= _ = Nothing
      +-- otherwise run the next step
      +Just x  >>= f = f x

      13.5 Maybe is a Monad!

      Here’s the full Monad instance for Maybe and some examples.

      -
      instance  Monad Maybe  where
      -    (Just x) >>= k      = k x
      -    Nothing  >>= _      = Nothing
      -
      -    (Just _) >>  k      = k
      -    Nothing  >>  _      = Nothing
      -
      -    return x            = Just x
      -
      Just 1 >>= \x -> return (x+1)
      -  ==> Just 2
      -Just "HELLO" >>= (\x -> return (length x)) >>= (\x -> return (x+1))
      -  ==> Just 6
      -Just "HELLO" >>= \x -> Nothing
      -  ==> Nothing
      -Just "HELLO" >> Just 2
      -  ==> Just 2
      -Just 2 >> Nothing
      -  ==> Nothing
      +
      instance  Monad Maybe  where
      +    (Just x) >>= k      = k x
      +    Nothing  >>= _      = Nothing
      +
      +    (Just _) >>  k      = k
      +    Nothing  >>  _      = Nothing
      +
      +    return x            = Just x
      +
      Just 1 >>= \x -> return (x+1)
      +  ==> Just 2
      +Just "HELLO" >>= (\x -> return (length x)) >>= (\x -> return (x+1))
      +  ==> Just 6
      +Just "HELLO" >>= \x -> Nothing
      +  ==> Nothing
      +Just "HELLO" >> Just 2
      +  ==> Just 2
      +Just 2 >> Nothing
      +  ==> Nothing

      Here are the stealSecret and increase examples rewritten with monad operations. The changes are ?> to >>= and Just to return.

      -
      stealSecret :: String -> Maybe String
      -stealSecret password =
      -    login password >>=
      -    secret >>=
      -    decorate
      -  where decorate s = return ("Stole secret: "++s)
      -
      -- Set the value of key to val in the given key-value list,
      --- but only if val is larger than the current value!
      -increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      -increase key val assocs =
      -    lookup key assocs >>=
      -    check >>=
      -    buildResult
      -  where check x
      -           | val < x   = Nothing
      -           | otherwise = return x
      -        buildResult x = return ((key,val) : delete (key,x) assocs)
      +
      stealSecret :: String -> Maybe String
      +stealSecret password =
      +    login password >>=
      +    secret >>=
      +    decorate
      +  where decorate s = return ("Stole secret: "++s)
      +
      -- Set the value of key to val in the given key-value list,
      +-- but only if val is larger than the current value!
      +increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      +increase key val assocs =
      +    lookup key assocs >>=
      +    check >>=
      +    buildResult
      +  where check x
      +           | val < x   = Nothing
      +           | otherwise = return x
      +        buildResult x = return ((key,val) : delete (key,x) assocs)

      13.6 The Return of do

      Here’s an example of what a complex monad operation might look like.

      -
      f = op1 >>= continue
      -  where continue  x   = op2 >> op3 >>= continue2 x
      -        continue2 x y = op4 >> op5 x y
      +
      f = op1 >>= continue
      +  where continue  x   = op2 >> op3 >>= continue2 x
      +        continue2 x y = op4 >> op5 x y

      Let’s see what happens when we transform this code a bit. First off, let’s inline the definitions.

      -
      f = op1 >>= (\x ->
      -               op2 >>
      -               op3 >>= (\y ->
      -                          op4 >>
      -                          op5 x y))
      +
      f = op1 >>= (\x ->
      +               op2 >>
      +               op3 >>= (\y ->
      +                          op4 >>
      +                          op5 x y))

      Due to lambda expressions continuing to the end of the expression, we can omit the parentheses. Let’s also indent differently.

      -
      f = op1 >>= \x ->
      -    op2 >>
      -    op3 >>= \y ->
      -    op4 >>
      -    op5 x y
      +
      f = op1 >>= \x ->
      +    op2 >>
      +    op3 >>= \y ->
      +    op4 >>
      +    op5 x y

      Now we can notice the similarity with do notation. The do block below is actually the same code!

      -
      f = do x <- op1
      -       op2
      -       y <- op3
      -       op4
      -       op5 x y
      +
      f = do x <- op1
      +       op2
      +       y <- op3
      +       op4
      +       op5 x y

      To clarify, do notation is just a nicer syntax for the monad operations (>>= and >>) and lambdas. Here’s how do notation gets transformed into monad operations. Note! the definition is recursive.

      -
      do x <- op a       ~~~>       op a >>= \x -> do ...
      -   ...
      -
      do op a            ~~~>       op a >> do ...
      -   ...
      -
      do let x = expr    ~~~>       let x = expr in do ...
      -   ...
      -
      do finalOp         ~~~>       finalOp
      +
      do x <- op a       ~~~>       op a >>= \x -> do ...
      +   ...
      +
      do op a            ~~~>       op a >> do ...
      +   ...
      +
      do let x = expr    ~~~>       let x = expr in do ...
      +   ...
      +
      do finalOp         ~~~>       finalOp

      Here’s safeNth using do notation:

      -
      safeHead :: [a] -> Maybe a
      -safeHead [] = Nothing
      -safeHead (x:xs) = Just x
      -
      -safeTail :: [a] -> Maybe [a]
      -safeTail [] = Nothing
      -safeTail (x:xs) = Just xs
      -
      -safeNth :: Int -> [a] -> Maybe a
      -safeNth 0 xs = safeHead xs
      -safeNth n xs = do t <- safeTail xs
      -                  safeNth (n-1) t
      +
      safeHead :: [a] -> Maybe a
      +safeHead [] = Nothing
      +safeHead (x:xs) = Just x
      +
      +safeTail :: [a] -> Maybe [a]
      +safeTail [] = Nothing
      +safeTail (x:xs) = Just xs
      +
      +safeNth :: Int -> [a] -> Maybe a
      +safeNth 0 xs = safeHead xs
      +safeNth n xs = do t <- safeTail xs
      +                  safeNth (n-1) t

      Here is increase one last time, now with do notation

      -
      -- Set the value of key to val in the given key-value list,
      --- but only if val is larger than the current value!
      -increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      -increase key val assocs =
      -  do oldVal <- lookup key assocs
      -     check oldVal
      -     return ((key,val) : delete (key,oldVal) assocs)
      -  where check x
      -           | val < x   = Nothing
      -           | otherwise = return x
      +
      -- Set the value of key to val in the given key-value list,
      +-- but only if val is larger than the current value!
      +increase :: Eq a => a -> Int -> [(a,Int)] -> Maybe [(a,Int)]
      +increase key val assocs =
      +  do oldVal <- lookup key assocs
      +     check oldVal
      +     return ((key,val) : delete (key,oldVal) assocs)
      +  where check x
      +           | val < x   = Nothing
      +           | otherwise = return x

      13.7 Logger is a Monad!

      We should be able to write a Monad instance for Logger ourselves, by setting >>= to #>. However, due to recent changes in the Haskell language we must implement Functor and Applicative instances to be allowed to implement the Monad instance. Functor we’ve already met, but what’s Applicative? We’ll find out later. Let’s implement the instances:

      -
      import Control.Monad
      -
      -data Logger a = Logger [String] a  deriving Show
      -
      -msg :: String -> Logger ()
      -msg s = Logger [s] ()
      -
      --- The Functor instance just maps over the stored value
      -instance Functor Logger where
      -  fmap f (Logger log x) = Logger log (f x)
      -
      --- This is an Applicative instance that works for any
      --- monad, you can just ignore it for now. We'll get back
      --- to Applicative later.
      -instance Applicative Logger where
      -  pure = return
      -  (<*>) = ap
      -
      --- Finally, the Monad instance
      -instance Monad Logger where
      -  return x = Logger [] x
      -  Logger la a >>= f = Logger (la++lb) b
      -    where Logger lb b = f a
      +
      import Control.Monad
      +
      +data Logger a = Logger [String] a  deriving Show
      +
      +msg :: String -> Logger ()
      +msg s = Logger [s] ()
      +
      +-- The Functor instance just maps over the stored value
      +instance Functor Logger where
      +  fmap f (Logger log x) = Logger log (f x)
      +
      +-- This is an Applicative instance that works for any
      +-- monad, you can just ignore it for now. We'll get back
      +-- to Applicative later.
      +instance Applicative Logger where
      +  pure = return
      +  (<*>) = ap
      +
      +-- Finally, the Monad instance
      +instance Monad Logger where
      +  return x = Logger [] x
      +  Logger la a >>= f = Logger (la++lb) b
      +    where Logger lb b = f a

      We don’t need the nomsg operation any more since it’s just return. We can also reimplement the annotate operation using monad operations.

      -
      nomsg :: a -> Logger a
      -nomsg x = return x
      -
      -annotate :: String -> a -> Logger a
      -annotate s x = msg s >> return x
      +
      nomsg :: a -> Logger a
      +nomsg x = return x
      +
      +annotate :: String -> a -> Logger a
      +annotate s x = msg s >> return x

      Here are the compute and filterLog examples rewritten using do-notation. Note how nice filterLog is with do-notation.

      -
      compute x = do
      -  a <- annotate "^2" (x*x)
      -  b <- annotate "+1" (a+1)
      -  annotate "*2" (b*2)
      -
      -filterLog :: (Show a) => (a -> Bool) -> [a] -> Logger [a]
      -filterLog f [] = return []
      -filterLog f (x:xs)
      -   | f x       = do msg ("keeping "++show x)
      -                    xs' <- filterLog f xs
      -                    return (x:xs')
      -   | otherwise = do msg ("dropping "++show x)
      -                    filterLog f xs
      -
      compute 3
      -  ==> Logger ["^2","+1","*2"] 20
      -filterLog (>0) [1,-2,3,-4,0]
      -  ==> Logger ["keeping 1","dropping -2","keeping 3","dropping -4","dropping 0"] [1,3]
      +
      compute x = do
      +  a <- annotate "^2" (x*x)
      +  b <- annotate "+1" (a+1)
      +  annotate "*2" (b*2)
      +
      +filterLog :: (Show a) => (a -> Bool) -> [a] -> Logger [a]
      +filterLog f [] = return []
      +filterLog f (x:xs)
      +   | f x       = do msg ("keeping "++show x)
      +                    xs' <- filterLog f xs
      +                    return (x:xs')
      +   | otherwise = do msg ("dropping "++show x)
      +                    filterLog f xs
      +
      compute 3
      +  ==> Logger ["^2","+1","*2"] 20
      +filterLog (>0) [1,-2,3,-4,0]
      +  ==> Logger ["keeping 1","dropping -2","keeping 3","dropping -4","dropping 0"] [1,3]

      13.8 The State Monad

      Haskell’s State monad is a generalized version of our BankOp type. The State type is parameterized by two types, the first being the type of the state, and the second the type of the value produced. State Bank a would be equivalent to our BankOp a. You can find the State monad in the module Control.Monad.Trans.State of the transformers package. Here’s a simplified implementation of State.

      -
      data State s a = State (s -> (a,s))
      -
      -runState (State f) s = f s
      -
      --- operation that overwrites the state (and produces ())
      -put :: s -> State s ()
      -put state = State (\oldState -> ((),state))
      -
      --- operation that produces the current state
      -get :: State s s
      -get = State (\state -> (state,state))
      -
      --- operation that modifies the current state with a function (and produces ())
      -modify :: (s -> s) -> State s ()
      -modify f = State (\state -> ((), f state))
      -
      --- Functor and Applicative instances skipped
      -
      -instance Monad (State s) where
      -  return x = State (\s -> (x,s))
      -
      -  op >>= f = State h
      -    where h state0 = let (val,state1) = runState op state0
      -                         op2 = f val
      -                     in runState op2 state1
      +
      data State s a = State (s -> (a,s))
      +
      +runState (State f) s = f s
      +
      +-- operation that overwrites the state (and produces ())
      +put :: s -> State s ()
      +put state = State (\oldState -> ((),state))
      +
      +-- operation that produces the current state
      +get :: State s s
      +get = State (\state -> (state,state))
      +
      +-- operation that modifies the current state with a function (and produces ())
      +modify :: (s -> s) -> State s ()
      +modify f = State (\state -> ((), f state))
      +
      +-- Functor and Applicative instances skipped
      +
      +instance Monad (State s) where
      +  return x = State (\s -> (x,s))
      +
      +  op >>= f = State h
      +    where h state0 = let (val,state1) = runState op state0
      +                         op2 = f val
      +                     in runState op2 state1

      Note how we declare an instance Monad (State s). We’re using a partially-applied type constructor because instances of Monad can only be declared for type constructors that take one more type parameter. This might be a bit clearer if you look at how m, Maybe and State occur in the type of >>= below.

      -
      class Monad m where
      -  (>>=) :: m a -> (a -> m b) -> m b
      -
      -instance Monad Maybe where
      -  (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
      -
      -instance Monad (State s) where
      -  (>>=) :: State s a -> (a -> State s b) -> State s b
      +
      class Monad m where
      +  (>>=) :: m a -> (a -> m b) -> m b
      +
      +instance Monad Maybe where
      +  (>>=) :: Maybe a -> (a -> Maybe b) -> Maybe b
      +
      +instance Monad (State s) where
      +  (>>=) :: State s a -> (a -> State s b) -> State s b

      Let’s look at some examples of working with State. To start off, let’s consider computations of type State Int a, which represent working with a simple counter.

      -
      -- adds i to the value of the counter
      -add :: Int -> State Int ()
      -add i = do old <- get
      -           put (old+i)
      -
      runState (add 1 >> add 3 >> add 5 >> add 6) 0
      -  ==> ((),15)
      -
      example :: State Int Int
      -example = do add 3           -- increment state by 3
      -             value <- get    -- value is current state, i.e. initial+3
      -             add 1000        -- increment state by 1000
      -             put (value + 1) -- overwrite state with value+1, i.e. initial+4
      -             return value    -- produce value, i.e. intial+3
      -
      runState example 1
      -  ==> (4,5)           -- initial is 1, state is initial+4=5, produces initial+3=4
      +
      -- adds i to the value of the counter
      +add :: Int -> State Int ()
      +add i = do old <- get
      +           put (old+i)
      +
      runState (add 1 >> add 3 >> add 5 >> add 6) 0
      +  ==> ((),15)
      +
      example :: State Int Int
      +example = do add 3           -- increment state by 3
      +             value <- get    -- value is current state, i.e. initial+3
      +             add 1000        -- increment state by 1000
      +             put (value + 1) -- overwrite state with value+1, i.e. initial+4
      +             return value    -- produce value, i.e. intial+3
      +
      runState example 1
      +  ==> (4,5)           -- initial is 1, state is initial+4=5, produces initial+3=4

      Note how a value of type State s a represents a process that transforms the state (just like BankOp). The initial state must be supplied using runState. Again, having to use runState makes the distinction between defining operations and executing them clearer.

      A state can replace an accumulator parameter when processing a list. Here are two examples: finding the largest element of a list, and finding values in a list that occur directly after a 0.

      -
      findLargest :: Ord a => [a] -> State a ()
      -findLargest [] = return ()
      -findLargest (x:xs) = do
      -  modify (\y -> max x y)  -- update state with max of current value and previous largest value
      -  findLargest xs          -- process rest of list
      -
      runState (findLargest [1,2,7,3]) 0  ==>  ((),7)
      -
      -- store the given value in the state list
      -remember :: a -> State [a] ()
      -remember x = modify (x:)
      -
      -valuesAfterZero :: [Int] -> [Int]
      -valuesAfterZero xs = runState (go xs) []
      -  where go :: [a] -> State [a] ()
      -        go (0:y:xs) = do remember y
      -                         go (y:xs)
      -        go (x:xs) = go xs
      -        go [] = return ()
      -
      valuesAfterZero [0,1,2,3,0,4,0,5,0,0,6]
      -  ==> ((),[6,0,5,4,1])
      +
      findLargest :: Ord a => [a] -> State a ()
      +findLargest [] = return ()
      +findLargest (x:xs) = do
      +  modify (\y -> max x y)  -- update state with max of current value and previous largest value
      +  findLargest xs          -- process rest of list
      +
      runState (findLargest [1,2,7,3]) 0  ==>  ((),7)
      +
      -- store the given value in the state list
      +remember :: a -> State [a] ()
      +remember x = modify (x:)
      +
      +valuesAfterZero :: [Int] -> [Int]
      +valuesAfterZero xs = runState (go xs) []
      +  where go :: [a] -> State [a] ()
      +        go (0:y:xs) = do remember y
      +                         go (y:xs)
      +        go (x:xs) = go xs
      +        go [] = return ()
      +
      valuesAfterZero [0,1,2,3,0,4,0,5,0,0,6]
      +  ==> ((),[6,0,5,4,1])

      13.9 The Return of mapM

      The control structures from the IO lecture work in all monads. Here are their real types.

      -
      when :: Monad m => Bool -> m () -> m ()        -- conditional operation
      -unless :: Monad m => Bool -> m () -> m ()      -- same, but condition is flipped
      -replicateM :: Monad m => Int -> m a -> m [a]   -- do something many times
      -replicateM_ :: Monad m => Int -> m a -> m ()   -- same, but ignore the results
      -mapM :: Monad m => (a -> m b) -> [a] -> m [b]  -- do something on a list's elements
      -mapM_ :: Monad m => (a -> m b) -> [a] -> m ()  -- same, but ignore the results
      -forM  :: Monad m => [a] -> (a -> m b) -> m [b] -- mapM but arguments reversed
      -forM_ :: Monad m => [a] -> (a -> m b) -> m ()  -- same, but ignore the results
      +
      when :: Monad m => Bool -> m () -> m ()        -- conditional operation
      +unless :: Monad m => Bool -> m () -> m ()      -- same, but condition is flipped
      +replicateM :: Monad m => Int -> m a -> m [a]   -- do something many times
      +replicateM_ :: Monad m => Int -> m a -> m ()   -- same, but ignore the results
      +mapM :: Monad m => (a -> m b) -> [a] -> m [b]  -- do something on a list's elements
      +mapM_ :: Monad m => (a -> m b) -> [a] -> m ()  -- same, but ignore the results
      +forM  :: Monad m => [a] -> (a -> m b) -> m [b] -- mapM but arguments reversed
      +forM_ :: Monad m => [a] -> (a -> m b) -> m ()  -- same, but ignore the results

      As we can see here, we can use mapM over all of the monads we’ve met so far:

      -
      mapM (\x -> if (x>0) then Just (x-1) else Nothing) [1,2,3]  ==>  Just [0,1,2]
      -mapM (\x -> if (x>0) then Just (x-1) else Nothing) [1,0,3]  ==>  Nothing
      -
      -mapM (\x -> msg "increment" >> msg (show x) >> return (x+1)) [1,2,3]
      -  ==> Logger ["increment","1","increment","2","increment","3"] [2,3,4]
      -
      -runState (mapM (\x -> modify (x+) >> return (x+1)) [1,2,3]) 0
      -  ==> ([2,3,4],6)
      +
      mapM (\x -> if (x>0) then Just (x-1) else Nothing) [1,2,3]  ==>  Just [0,1,2]
      +mapM (\x -> if (x>0) then Just (x-1) else Nothing) [1,0,3]  ==>  Nothing
      +
      +mapM (\x -> msg "increment" >> msg (show x) >> return (x+1)) [1,2,3]
      +  ==> Logger ["increment","1","increment","2","increment","3"] [2,3,4]
      +
      +runState (mapM (\x -> modify (x+) >> return (x+1)) [1,2,3]) 0
      +  ==> ([2,3,4],6)

      Some more examples:

      -
      safeHead :: [a] -> Maybe a
      -safeHead [] = Nothing
      -safeHead (x:xs) = Just x
      -firsts :: [[a]] -> Maybe [a]
      -firsts xs = forM xs safeHead
      -
      firsts [[1,2,3],[4,5],[6]] ==> Just [1,4,6]
      -firsts [[1,2,3],[],[6]]    ==> Nothing
      -
      -- an abbreviated version of an example from the last section
      -findLargest :: Ord a => [a] -> State a ()
      -findLargest xs = mapM_ update xs
      -  where update x = modify (\y -> max x y)
      -
      runState (findLargest [1,2,7,3]) 0  ==>  ((),7)
      -
      let increment = modify (+1) >> get
      -    ops = replicateM 4 increment
      -in runState ops 0
      -  ==> ([1,2,3,4],4)
      +
      safeHead :: [a] -> Maybe a
      +safeHead [] = Nothing
      +safeHead (x:xs) = Just x
      +firsts :: [[a]] -> Maybe [a]
      +firsts xs = forM xs safeHead
      +
      firsts [[1,2,3],[4,5],[6]] ==> Just [1,4,6]
      +firsts [[1,2,3],[],[6]]    ==> Nothing
      +
      -- an abbreviated version of an example from the last section
      +findLargest :: Ord a => [a] -> State a ()
      +findLargest xs = mapM_ update xs
      +  where update x = modify (\y -> max x y)
      +
      runState (findLargest [1,2,7,3]) 0  ==>  ((),7)
      +
      let increment = modify (+1) >> get
      +    ops = replicateM 4 increment
      +in runState ops 0
      +  ==> ([1,2,3,4],4)

      Here’s filter reimplemented using the State monad:

      -
      rememberElements :: (a -> Bool) -> [a] -> State [a] ()
      -rememberElements f xs = mapM_ maybePut xs
      -  where maybePut x = when (f x) (modify (++[x]))
      -
      -sfilter :: (a -> Bool) -> [a] -> [a]
      -sfilter f xs = finalState
      -  where (_, finalState) = runState (rememberElements f xs) []
      -
      sfilter even [1,2,3,4,5]
      -  ==> [2,4]
      +
      rememberElements :: (a -> Bool) -> [a] -> State [a] ()
      +rememberElements f xs = mapM_ maybePut xs
      +  where maybePut x = when (f x) (modify (++[x]))
      +
      +sfilter :: (a -> Bool) -> [a] -> [a]
      +sfilter f xs = finalState
      +  where (_, finalState) = runState (rememberElements f xs) []
      +
      sfilter even [1,2,3,4,5]
      +  ==> [2,4]

      We can write our own operations that work for all monads. This is made possible by type classes, as we’ve seen before. If you only use monad operations like return and do-notation, the type system will infer a generic type for your function.

      -
      mywhen b op = if b then op else return ()
      -
      -mymapM_ op [] = return ()
      -mymapM_ op (x:xs) = do op x
      -                       mymapM_ op xs
      -
      *Main> :t mywhen
      -mywhen :: (Monad m) => Bool -> m () -> m ()
      -*Main> :t mymapM_
      -mymapM_ :: (Monad m) => (t -> m a) -> [t] -> m ()
      +
      mywhen b op = if b then op else return ()
      +
      +mymapM_ op [] = return ()
      +mymapM_ op (x:xs) = do op x
      +                       mymapM_ op xs
      +
      *Main> :t mywhen
      +mywhen :: (Monad m) => Bool -> m () -> m ()
      +*Main> :t mymapM_
      +mymapM_ :: (Monad m) => (t -> m a) -> [t] -> m ()

      We can use these generic operations in each of our example monads:

      -
      perhapsDecrease :: Int -> Maybe Int
      -perhapsDecrease x = do
      -  mywhen (x<=0) Nothing
      -  return (x-1)
      -
      perhapsDecrease 2  ==>  Just 1
      -perhapsDecrease 0  ==>  Nothing
      -
      search :: (Show a, Eq a) => a -> [a] -> Logger ()
      -search x ys = mymapM_ look ys
      -  where look y = mywhen (x==y) (msg ("Found "++show y))
      -
      search 3 [1,2,3,4,3,2]  ==>  Logger ["Found 3","Found 3"] ()
      -
      sumPositive :: [Int] -> State Int ()
      -sumPositive xs = mymapM_ f xs
      -  where f x = when (x>0) (modify (x+))
      -
      runState (sumPositive [1,-4,2,3]) 0  ==>  ((),6)
      +
      perhapsDecrease :: Int -> Maybe Int
      +perhapsDecrease x = do
      +  mywhen (x<=0) Nothing
      +  return (x-1)
      +
      perhapsDecrease 2  ==>  Just 1
      +perhapsDecrease 0  ==>  Nothing
      +
      search :: (Show a, Eq a) => a -> [a] -> Logger ()
      +search x ys = mymapM_ look ys
      +  where look y = mywhen (x==y) (msg ("Found "++show y))
      +
      search 3 [1,2,3,4,3,2]  ==>  Logger ["Found 3","Found 3"] ()
      +
      sumPositive :: [Int] -> State Int ()
      +sumPositive xs = mymapM_ f xs
      +  where f x = when (x>0) (modify (x+))
      +
      runState (sumPositive [1,-4,2,3]) 0  ==>  ((),6)

      13.10 Monads are Functors

      One useful operation hasn’t yet been introduced: liftM.

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

      The liftM operation makes it easy to write code with pure and monadic parts.

      -
      liftM negate (Just 3)
      -  ==> Just (-3)
      -
      -liftM sort $ firsts [[4,6],[2,1,0],[3,3,3]]
      -  ==> Just [2,3,4]
      -
      -runState (liftM negate get) 3
      -  ==> (-3,3)
      +
      liftM negate (Just 3)
      +  ==> Just (-3)
      +
      +liftM sort $ firsts [[4,6],[2,1,0],[3,3,3]]
      +  ==> Just [2,3,4]
      +
      +runState (liftM negate get) 3
      +  ==> (-3,3)

      Does the type of liftM look familiar? It’s just like the type of fmap! In fact, it’s easy to define a functor instance for a monad: just set fmap = liftM. Since every Monad needs to be a Functor these days, modern Haskell style prefers fmap over liftM.

      -
      fmap :: Functor f => (a->b) -> f a -> f b
      -
      fmap negate (Just 3)
      -  ==> Just (-3)
      -
      -fmap sort $ firsts [[4,6],[2,1,0],[3,3,3]]
      -  ==> Just [2,3,4]
      -
      -runState (fmap negate get) 3
      -  ==> (-3,3)
      +
      fmap :: Functor f => (a->b) -> f a -> f b
      +
      fmap negate (Just 3)
      +  ==> Just (-3)
      +
      +fmap sort $ firsts [[4,6],[2,1,0],[3,3,3]]
      +  ==> Just [2,3,4]
      +
      +runState (fmap negate get) 3
      +  ==> (-3,3)

      13.11 One More Monad

      The list monad (that is, the Monad instance for []) represents computations with multiple return values. It’s useful for searching through alternatives. Here’s a first example. For every x we produce both x and -x:

      -
      [1,2,3] >>= \x -> [-x,x]
      -  ==> [-1,1,-2,2,-3,3]
      +
      [1,2,3] >>= \x -> [-x,x]
      +  ==> [-1,1,-2,2,-3,3]

      We can filter out unsuitable values by produing an empty list:

      -
      [1,2,3] >>= \x -> if x>1 then [x] else []
      -  ==> [2,3]
      +
      [1,2,3] >>= \x -> if x>1 then [x] else []
      +  ==> [2,3]

      If we’re using do-notation the list monad starts to look more like a looping construct:

      -
      do word <- ["Blue", "Green"]
      -   number <- [1,2,3]
      -   return (word ++ show number)
      -  ==> ["Blue1","Blue2","Blue3","Green1","Green2","Green3"]
      +
      do word <- ["Blue", "Green"]
      +   number <- [1,2,3]
      +   return (word ++ show number)
      +  ==> ["Blue1","Blue2","Blue3","Green1","Green2","Green3"]

      More interesting example: find all the pairs in a list that sum to k. (The same element twice counts as a pair.)

      -
      findSum :: [Int] -> Int -> [(Int,Int)]
      -findSum xs k = do a <- xs
      -                  b <- xs
      -                  if (a+b==k) then [(a,b)] else []
      -
      findSum [1,2,3,4,5] 5
      -  ==> [(1,4),(2,3),(3,2),(4,1)]
      +
      findSum :: [Int] -> Int -> [(Int,Int)]
      +findSum xs k = do a <- xs
      +                  b <- xs
      +                  if (a+b==k) then [(a,b)] else []
      +
      findSum [1,2,3,4,5] 5
      +  ==> [(1,4),(2,3),(3,2),(4,1)]

      A final, more complex example. We find all palindromes from a string using the list monad, and then find the longest one.

      -
      import Data.List (sortBy)
      -
      -substrings :: String -> [String]
      -substrings xs = do start <- [0..length xs - 1]
      -                   end <- [start+1..length xs - 1]
      -                   return $ drop start $ take end $ xs
      -
      -palindromesIn :: String -> [String]
      -palindromesIn xs = do s <- substrings xs
      -                      if (s==reverse s) then return s else []
      -
      -longestPalindrome xs = head . sortBy f $ palindromesIn xs
      -  where f s s' = compare (length s') (length s)  -- longer is smaller
      -
      palindromesIn "aabbacddcaca"
      -  ==> ["a","aa","a","abba","b","bb","b","a","acddca","c","cddc","d","dd","d","c","cac","a","c"]
      -longestPalindrome "aabbacddcaca"
      -  ==> "acddca"
      +
      import Data.List (sortBy)
      +
      +substrings :: String -> [String]
      +substrings xs = do start <- [0..length xs - 1]
      +                   end <- [start+1..length xs - 1]
      +                   return $ drop start $ take end $ xs
      +
      +palindromesIn :: String -> [String]
      +palindromesIn xs = do s <- substrings xs
      +                      if (s==reverse s) then return s else []
      +
      +longestPalindrome xs = head . sortBy f $ palindromesIn xs
      +  where f s s' = compare (length s') (length s)  -- longer is smaller
      +
      palindromesIn "aabbacddcaca"
      +  ==> ["a","aa","a","abba","b","bb","b","a","acddca","c","cddc","d","dd","d","c","cac","a","c"]
      +longestPalindrome "aabbacddcaca"
      +  ==> "acddca"

      Here’s the surprisingly simple implementation of the list monad:

      -
      instance Monad [] where
      -  return x = [x]                  -- an operation that produces one value
      -  lis >>= f = concat (map f lis)  -- compute f for all values, combine the results
      +
      instance Monad [] where
      +  return x = [x]                  -- an operation that produces one value
      +  lis >>= f = concat (map f lis)  -- compute f for all values, combine the results

      We’ve actually seen the list monad previously in the guise of list comprehensions. Compare this reimplementation of findSum to the earlier one that uses do-notation.

      -
      findSum :: [Int] -> Int -> [(Int,Int)]
      -findSum xs k = [(a,b) | a <- xs, b <- xs, a+b==k ]
      +
      findSum :: [Int] -> Int -> [(Int,Int)]
      +findSum xs k = [(a,b) | a <- xs, b <- xs, a+b==k ]

      13.12 Oh Right, IO

      As you’ve probably guessed by now, IO is a monad. However the implementations of the IO type and instance Monad IO are compiler built-ins. You couldn’t implement the IO monad just using standard Haskell, unlike Maybe monad, State monad and other monads we’ve seen.

      However, true side effects fit the monad pattern just like State and Maybe. Just like with other monads, we’re separating the pure definitions of operations from the process of running the operations. As a bonus, you can use all the generic monad operations (mapM and friends) with IO.

      Here are some examples of writing IO using monad operations.

      -
      printTwoThings :: IO ()
      -printTwoThings = putStrLn "One!" >> print 2
      -
      -echo :: IO ()
      -echo = getLine >>= putStrLn
      -
      -verboseEcho :: IO ()
      -verboseEcho = getLine >>= \s -> putStrLn ("You wrote: " ++ s)
      -
      -query :: String -> IO String
      -query question = putStrLn question >> getLine
      -
      -confirm :: String -> IO Bool
      -confirm question = putStrLn question >> fmap interpret getLine
      -    where interpret "Y" = True
      -          interpret _ = False
      -
      Prelude> printTwoThings
      -One!
      -2
      -
      -Prelude> verboseEcho
      -The Iliad
      -You wrote: The Iliad
      -
      -Prelude> answer <- query "Why am I here?"
      -Why am I here?
      -Good question!
      -Prelude> answer
      -"Good question!"
      -
      -Prelude> b <- confirm "Fire warheads?"
      -Fire warheads?
      -no no no no
      -Prelude> b
      -False
      -Prelude> b <- confirm "Make love, not war?"
      -Make love, not war?
      -Y
      -Prelude> b
      -True
      +
      printTwoThings :: IO ()
      +printTwoThings = putStrLn "One!" >> print 2
      +
      +echo :: IO ()
      +echo = getLine >>= putStrLn
      +
      +verboseEcho :: IO ()
      +verboseEcho = getLine >>= \s -> putStrLn ("You wrote: " ++ s)
      +
      +query :: String -> IO String
      +query question = putStrLn question >> getLine
      +
      +confirm :: String -> IO Bool
      +confirm question = putStrLn question >> fmap interpret getLine
      +    where interpret "Y" = True
      +          interpret _ = False
      +
      Prelude> printTwoThings
      +One!
      +2
      +
      +Prelude> verboseEcho
      +The Iliad
      +You wrote: The Iliad
      +
      +Prelude> answer <- query "Why am I here?"
      +Why am I here?
      +Good question!
      +Prelude> answer
      +"Good question!"
      +
      +Prelude> b <- confirm "Fire warheads?"
      +Fire warheads?
      +no no no no
      +Prelude> b
      +False
      +Prelude> b <- confirm "Make love, not war?"
      +Make love, not war?
      +Y
      +Prelude> b
      +True

      13.13 Monads in Other Languages

      @@ -3419,38 +3420,38 @@ this topic connects to equational reasoning discussed in chapter 10.

      13.13.2 Streams

      Java Streams have a monadlike API too. Streams are about producing many values incrementally. Just like with Optional, the method Stream.flatMap lets us take a Stream<T>, combine it with a Function<T,Stream<U>> and get a Stream<U>.

      As an example, if lines is a Stream<String>, words takes a String and returns a Stream<String> and readInt takes a String and returns an Integer, we can write:

      -
      Stream<Integer> parseNumbers(Stream<String> lines) {
      -    return lines.flatMap(words).map(read);
      -}
      +
      Stream<Integer> parseNumbers(Stream<String> lines) {
      +    return lines.flatMap(words).map(read);
      +}

      This corresponds to the following Haskell list monad code:

      -
      parseNumbers :: [String] -> [Int]
      -parseNumbers strings = fmap read (strings >>= words)
      -
      parseNumbers ["123 456","7 89"]  ==>  [123,456,7,89]
      +
      parseNumbers :: [String] -> [Int]
      +parseNumbers strings = fmap read (strings >>= words)
      +
      parseNumbers ["123 456","7 89"]  ==>  [123,456,7,89]

      13.13.3 Promises

      There is much disagreement about whether Promises in JavaScript really are monads or not. However, some similarities are obvious.

      First, consider the similarities between Promise.then and >>=. Both take an operation (promise or monadic operation), and combine it with a function that returns a new operation.

      -
      function concatPromises(promise1, promise2) {
      -  return promise1.then(value1 => promise2.then(value2 => value1+value2));
      -}
      -
      >> concatPromises(Promise.resolve("abc"), Promise.resolve("def")).then(console.log)
      -abcdef
      -
      concatMonadic :: Monad m => m String -> m String -> m String
      -concatMonadic op1 op2 = op1 >>= (\value1 -> op2 >>= (\value2 -> return (value1++value2)))
      -
      Prelude> concatMonadic (Just "abc") (Just "def")
      -Just "abcdef"
      +
      function concatPromises(promise1, promise2) {
      +  return promise1.then(value1 => promise2.then(value2 => value1+value2));
      +}
      +
      >> concatPromises(Promise.resolve("abc"), Promise.resolve("def")).then(console.log)
      +abcdef
      +
      concatMonadic :: Monad m => m String -> m String -> m String
      +concatMonadic op1 op2 = op1 >>= (\value1 -> op2 >>= (\value2 -> return (value1++value2)))
      +
      Prelude> concatMonadic (Just "abc") (Just "def")
      +Just "abcdef"

      Next, let’s consider the similarities between async/await and do-notation. Both are nicer syntaxes for working with the raw Promise.then or >>= mechanisms. We reimplement concatPromises using async/await, and concatMonadic using do-notation. Their behaviour stays the same.

      -
      async function concatPromises(promise1, promise2) {
      -  let value1 = await promise1;
      -  let value2 = await promise2;
      -  return value1+value2;
      -}
      -
      concatMonadic :: Monad m => m String -> m String -> m String
      -concatMonadic op1 op2 = do
      -  value1 <- op1
      -  value2 <- op2
      -  return (value1++value2)
      +
      async function concatPromises(promise1, promise2) {
      +  let value1 = await promise1;
      +  let value2 = await promise2;
      +  return value1+value2;
      +}
      +
      concatMonadic :: Monad m => m String -> m String -> m String
      +concatMonadic op1 op2 = do
      +  value1 <- op1
      +  value2 <- op2
      +  return (value1++value2)
    @@ -3499,9 +3500,9 @@ this topic connects to equational reasoning discussed in chapter 10.

    13.16 Quiz

    What is the expression equivalent to the following do block?

    -
    do y <- z
    -   s y
    -   return (f y)
    +
    do y <- z
    +   s y
    +   return (f y)
    1. z >> \y -> s y >> return (f y) @@ -3587,128 +3588,128 @@ None of the above

      14.1.1 Examples with Text

      Let’s go through a short GHCi session demonstrating the use of Data.Text. As the documentation says, the Data.Text module is designed to be imported qualified. We can convert a String into a Text with the function T.pack. Note how a value of type Text gets printed just like a String.

      -
      Prelude> import qualified Data.Text as T
      -Prelude T> :t T.pack
      -T.pack :: String -> T.Text
      -Prelude T> let phrase = T.pack "brevity is the soul of wit"
      -Prelude T> :t phrase
      -phrase :: T.Text
      -Prelude T> phrase
      -"brevity is the soul of wit"
      +
      Prelude> import qualified Data.Text as T
      +Prelude T> :t T.pack
      +T.pack :: String -> T.Text
      +Prelude T> let phrase = T.pack "brevity is the soul of wit"
      +Prelude T> :t phrase
      +phrase :: T.Text
      +Prelude T> phrase
      +"brevity is the soul of wit"

      We can use the functions from Data.Text to operate on values of Text. Many of these are named like their counterparts for Strings or lists from Prelude.

      -
      Prelude T> :t T.length
      -T.length :: T.Text -> Int
      -Prelude T> T.length phrase
      -26
      -Prelude T> T.head phrase
      -'b'
      -Prelude T> T.take 4 phrase
      -"brev"
      -Prelude T> :t T.words
      -T.words :: T.Text -> [T.Text]
      -Prelude T> T.words phrase
      -["brevity","is","the","soul","of","wit"]
      -Prelude T> :t T.map
      -T.map :: (Char -> Char) -> T.Text -> T.Text
      -Prelude T> T.map (\c -> if c=='o' then '0' else c) phrase
      -"brevity is the s0ul 0f wit"
      +
      Prelude T> :t T.length
      +T.length :: T.Text -> Int
      +Prelude T> T.length phrase
      +26
      +Prelude T> T.head phrase
      +'b'
      +Prelude T> T.take 4 phrase
      +"brev"
      +Prelude T> :t T.words
      +T.words :: T.Text -> [T.Text]
      +Prelude T> T.words phrase
      +["brevity","is","the","soul","of","wit"]
      +Prelude T> :t T.map
      +T.map :: (Char -> Char) -> T.Text -> T.Text
      +Prelude T> T.map (\c -> if c=='o' then '0' else c) phrase
      +"brevity is the s0ul 0f wit"

      A useful detail is that Text has a Monoid instance that glues Text values together. You can also use the functions T.append and T.concat.

      -
      Prelude T> phrase <> phrase
      -"brevity is the soul of witbrevity is the soul of wit"
      -Prelude T> T.append phrase phrase
      -"brevity is the soul of witbrevity is the soul of wit"
      -Prelude T> T.concat [phrase,phrase,phrase]
      -"brevity is the soul of witbrevity is the soul of witbrevity is the soul of wit"
      +
      Prelude T> phrase <> phrase
      +"brevity is the soul of witbrevity is the soul of wit"
      +Prelude T> T.append phrase phrase
      +"brevity is the soul of witbrevity is the soul of wit"
      +Prelude T> T.concat [phrase,phrase,phrase]
      +"brevity is the soul of witbrevity is the soul of witbrevity is the soul of wit"

      If you want to write a recursive function that pattern matches on a Text like you would on a String, you can use the function T.uncons :: T.Text -> Maybe (Char, T.Text) to split a Text into a head and a tail. Here’s a simple example:

      -
      countLetter :: Char -> T.Text -> Int
      -countLetter c t =
      -  case T.uncons t of
      -    Nothing -> 0
      -    Just (x,rest) -> (if x == c then 1 else 0) + countLetter c rest
      -
      Prelude T> countLetter 't' phrase
      -3
      +
      countLetter :: Char -> T.Text -> Int
      +countLetter c t =
      +  case T.uncons t of
      +    Nothing -> 0
      +    Just (x,rest) -> (if x == c then 1 else 0) + countLetter c rest
      +
      Prelude T> countLetter 't' phrase
      +3

      14.1.1.1 Strictness and Laziness

      Note that Data.Text implements the strict Text type. You need to use Data.Text.Lazy for the lazy variant. As mentioned earlier, one difference between these two types is that the strict type does not work for infinite strings:

      -
      Prelude T> T.head (T.pack (repeat 'x'))
      --- never returns
      -Prelude T> import qualified Data.Text.Lazy as TL
      -Prelude T TL> TL.head (TL.pack (repeat 'x'))
      -'x'
      +
      Prelude T> T.head (T.pack (repeat 'x'))
      +-- never returns
      +Prelude T> import qualified Data.Text.Lazy as TL
      +Prelude T TL> TL.head (TL.pack (repeat 'x'))
      +'x'

      Another practical problem that sometimes crops is that you can end up with a mismatch between strict and lazy Texts when using libraries. You can usually fix this by using toStrict or fromStrict as needed.

      -
      Prelude T TL> let lazyPhrase = TL.pack "brevity is the soul of wit"
      -Prelude T TL> :t lazyPhrase
      -lazyPhrase :: TL.Text
      -Prelude T TL> :t phrase
      -phrase :: T.Text
      -Prelude T TL> lazyPhrase == phrase
      -
      -<interactive>: error:
      -    • Couldn't match expected type ‘TL.Text’
      -                  with actual type ‘T.Text’
      -      NB: ‘T.Text’ is defined in ‘Data.Text.Internal’
      -          ‘TL.Text’ is defined in ‘Data.Text.Internal.Lazy’
      -    • In the second argument of ‘(==)’, namely ‘phrase’
      -      In the expression: lazyPhrase == phrase
      -      In an equation for ‘it’: it = lazyPhrase == phrase
      -
      -Prelude T TL> :t TL.toStrict
      -TL.toStrict :: TL.Text -> T.Text
      -Prelude T TL> :t TL.fromStrict
      -TL.fromStrict :: T.Text -> TL.Text
      -Prelude T TL> TL.toStrict lazyPhrase == phrase
      -True
      +
      Prelude T TL> let lazyPhrase = TL.pack "brevity is the soul of wit"
      +Prelude T TL> :t lazyPhrase
      +lazyPhrase :: TL.Text
      +Prelude T TL> :t phrase
      +phrase :: T.Text
      +Prelude T TL> lazyPhrase == phrase
      +
      +<interactive>: error:
      +    • Couldn't match expected type ‘TL.Text’
      +                  with actual type ‘T.Text’
      +      NB: ‘T.Text’ is defined in ‘Data.Text.Internal’
      +          ‘TL.Text’ is defined in ‘Data.Text.Internal.Lazy’
      +    • In the second argument of ‘(==)’, namely ‘phrase’
      +      In the expression: lazyPhrase == phrase
      +      In an equation for ‘it’: it = lazyPhrase == phrase
      +
      +Prelude T TL> :t TL.toStrict
      +TL.toStrict :: TL.Text -> T.Text
      +Prelude T TL> :t TL.fromStrict
      +TL.fromStrict :: T.Text -> TL.Text
      +Prelude T TL> TL.toStrict lazyPhrase == phrase
      +True

      14.1.2 Examples with ByteString

      We can walk through pretty much the same GHCi session using ByteString instead of Text. However, note how the ByteString is built up from Word8 values and not Char values. A Char can represent an arbitrary unicode codepoint like for a character like 'Å', but a Word8 represents a byte: a number from 0 to 255. Unfortunately and somewhat confusingly, ByteString values get printed like Strings.

      -
      Prelude> import Data.Word
      -Prelude Data.Word> import qualified Data.ByteString as B
      -Prelude Data.Word B> let binary = B.pack [99,111,102,102,101,101]
      -Prelude Data.Word B> :t binary
      -binary :: B.ByteString
      -Prelude Data.Word B> :t B.pack
      -B.pack :: [Word8] -> B.ByteString
      -Prelude Data.Word B> binary
      -"coffee"
      -Prelude Data.Word B> :t B.length
      -B.length :: B.ByteString -> Int
      -Prelude Data.Word B> B.length binary
      -6
      -Prelude Data.Word B> :t B.head
      -B.head :: B.ByteString -> Word8
      -Prelude Data.Word B> B.head binary
      -99
      -Prelude Data.Word B> B.take 4 binary
      -"coff"
      -Prelude Data.Word B> :t B.map
      -B.map :: (Word8 -> Word8) -> B.ByteString -> B.ByteString
      -Prelude Data.Word B> B.map (+1) binary
      -"dpggff"
      +
      Prelude> import Data.Word
      +Prelude Data.Word> import qualified Data.ByteString as B
      +Prelude Data.Word B> let binary = B.pack [99,111,102,102,101,101]
      +Prelude Data.Word B> :t binary
      +binary :: B.ByteString
      +Prelude Data.Word B> :t B.pack
      +B.pack :: [Word8] -> B.ByteString
      +Prelude Data.Word B> binary
      +"coffee"
      +Prelude Data.Word B> :t B.length
      +B.length :: B.ByteString -> Int
      +Prelude Data.Word B> B.length binary
      +6
      +Prelude Data.Word B> :t B.head
      +B.head :: B.ByteString -> Word8
      +Prelude Data.Word B> B.head binary
      +99
      +Prelude Data.Word B> B.take 4 binary
      +"coff"
      +Prelude Data.Word B> :t B.map
      +B.map :: (Word8 -> Word8) -> B.ByteString -> B.ByteString
      +Prelude Data.Word B> B.map (+1) binary
      +"dpggff"

      The same caveats apply to the differences between strict and lazy ByteString as for Text:

      -
      Prelude B Data.Char> B.head (B.pack (repeat 99))
      --- never returns
      -Prelude Data.Word B> import qualified Data.ByteString.Lazy as BL
      -Prelude Data.Word B BL> BL.head (BL.pack (repeat 99))
      -99
      -Prelude Data.Word B BL> binary == BL.pack [99]
      -
      -<interactive>: error:
      -    • Couldn't match expected type ‘B.ByteString’
      -                  with actual type ‘BL.ByteString’
      -      NB: ‘BL.ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
      -          ‘B.ByteString’ is defined in ‘Data.ByteString.Internal’
      -    • In the second argument of ‘(==)’, namely ‘BL.pack [99]’
      -      In the expression: binary == BL.pack [99]
      -      In an equation for ‘it’: it = binary == BL.pack [99]
      -
      -Prelude Data.Word B BL> :t BL.toStrict
      -BL.toStrict :: BL.ByteString -> B.ByteString
      -Prelude Data.Word B BL> :t BL.fromStrict
      -BL.fromStrict :: B.ByteString -> BL.ByteString
      -Prelude Data.Word B BL> binary == BL.toStrict (BL.pack [99])
      -False
      +
      Prelude B Data.Char> B.head (B.pack (repeat 99))
      +-- never returns
      +Prelude Data.Word B> import qualified Data.ByteString.Lazy as BL
      +Prelude Data.Word B BL> BL.head (BL.pack (repeat 99))
      +99
      +Prelude Data.Word B BL> binary == BL.pack [99]
      +
      +<interactive>: error:
      +    • Couldn't match expected type ‘B.ByteString’
      +                  with actual type ‘BL.ByteString’
      +      NB: ‘BL.ByteString’ is defined in ‘Data.ByteString.Lazy.Internal’
      +          ‘B.ByteString’ is defined in ‘Data.ByteString.Internal’
      +    • In the second argument of ‘(==)’, namely ‘BL.pack [99]’
      +      In the expression: binary == BL.pack [99]
      +      In an equation for ‘it’: it = binary == BL.pack [99]
      +
      +Prelude Data.Word B BL> :t BL.toStrict
      +BL.toStrict :: BL.ByteString -> B.ByteString
      +Prelude Data.Word B BL> :t BL.fromStrict
      +BL.fromStrict :: B.ByteString -> BL.ByteString
      +Prelude Data.Word B BL> binary == BL.toStrict (BL.pack [99])
      +False

      14.1.3 Sidenote: Encodings

      @@ -3720,35 +3721,35 @@ None of the above

      If we read the same stream of bits using a different encoding, we’ll see other characters. For example the string above would be interpreted as “Ha∫keλ!” using the Latin-1 text encoding.

      By the way, when dealing with raw binary data, it’s often convenient to use the hexadecimal number system which uses a single symbol 0, 1, …, 9, A, B, …, F to represent all of the sixteen possible combinations of four bits. We don’t need hexadecimal in this course but you can check out Wikipedia if you’re interested in learning more about hexadecimal.

      We can explore the same example using code. The function Data.Text.Encoding.encodeUtf8 :: Text -> ByteString encodes the characters in a Text into bytes in a ByteString using UTF-8.

      -
      Prelude> import qualified Data.Text as T
      -Prelude T> import qualified Data.ByteString as B
      -Prelude T B> T.length (T.pack "haskell")
      -7
      -Prelude T B> T.length (T.pack "Ha∫keλ!")
      -7
      -Prelude T B> import Data.Text.Encoding
      -Prelude T B Data.Text.Encoding> encodeUtf8 (T.pack "haskell")
      -"haskell"
      -Prelude T B Data.Text.Encoding> encodeUtf8 (T.pack "Ha∫keλ!")
      -"Ha\226\136\171ke\206\187!"
      -Prelude T B Data.Text.Encoding> B.length (encodeUtf8 (T.pack "haskell"))
      -7
      -Prelude T B Data.Text.Encoding> B.length (encodeUtf8 (T.pack "Ha∫keλ!"))
      -10
      +
      Prelude> import qualified Data.Text as T
      +Prelude T> import qualified Data.ByteString as B
      +Prelude T B> T.length (T.pack "haskell")
      +7
      +Prelude T B> T.length (T.pack "Ha∫keλ!")
      +7
      +Prelude T B> import Data.Text.Encoding
      +Prelude T B Data.Text.Encoding> encodeUtf8 (T.pack "haskell")
      +"haskell"
      +Prelude T B Data.Text.Encoding> encodeUtf8 (T.pack "Ha∫keλ!")
      +"Ha\226\136\171ke\206\187!"
      +Prelude T B Data.Text.Encoding> B.length (encodeUtf8 (T.pack "haskell"))
      +7
      +Prelude T B Data.Text.Encoding> B.length (encodeUtf8 (T.pack "Ha∫keλ!"))
      +10

      If we are processing ASCII text, that is, characters that can be represented using single bytes, we can use Text and ByteString interchangeably. There are namespaces Data.ByteString.Char8 and Data.ByteString.Lazy.Char8 that offer functions that operate on ByteStrings using Char values instead of Word8. However, care must be taken to make sure all the characters really are plain ASCII characters, or surprising things will happen.

      -
      Prelude T B> import qualified Data.ByteString.Char8 as B8
      -Prelude T B B8> B8.pack "abc"
      -"abc"
      -Prelude T B B8> :t B8.pack
      -B8.pack :: String -> B.ByteString
      -Prelude T B B8> :t B.pack
      -B.pack :: [Word8] -> B.ByteString
      -Prelude T B B8> B8.cons 'a' (B8.pack "bc")
      -"abc"
      -Prelude T B B8> putStrLn (B8.unpack (B8.pack "€λ훈"))  -- non-ASCII characters get truncated
      -¬»È
      -Prelude T B B8> putStrLn (T.unpack (T.pack "€λ훈"))
      -€λ훈
      +
      Prelude T B> import qualified Data.ByteString.Char8 as B8
      +Prelude T B B8> B8.pack "abc"
      +"abc"
      +Prelude T B B8> :t B8.pack
      +B8.pack :: String -> B.ByteString
      +Prelude T B B8> :t B.pack
      +B.pack :: [Word8] -> B.ByteString
      +Prelude T B B8> B8.cons 'a' (B8.pack "bc")
      +"abc"
      +Prelude T B B8> putStrLn (B8.unpack (B8.pack "€λ훈"))  -- non-ASCII characters get truncated
      +¬»È
      +Prelude T B B8> putStrLn (T.unpack (T.pack "€λ훈"))
      +€λ훈
    @@ -3765,12 +3766,12 @@ None of the above
  • Unlike in other languages, return is not a keyword and does not cause an operation to stop executing. Instead, return x is the operation that always produces x and does nothing else.
  • This is what do-notation looks like:
  • -
    foo y = do
    -  operation1         -- run an operation
    -  val <- operation2  -- run an operation and keep the produced value
    -  operation3 val y   -- run an operation with parameters
    -  mapM_ (\x -> operation4 val x) things  -- use a generic monad operation and a lambda
    -  operation5 val     -- the final line of the do decides which value the whole block produces
    +
    foo y = do
    +  operation1         -- run an operation
    +  val <- operation2  -- run an operation and keep the produced value
    +  operation3 val y   -- run an operation with parameters
    +  mapM_ (\x -> operation4 val x) things  -- use a generic monad operation and a lambda
    +  operation5 val     -- the final line of the do decides which value the whole block produces
    @@ -3778,34 +3779,34 @@ None of the above

    Sometimes it feels like everything in the world happens over HTTP and Web Apis. Your web browser, your smartphone apps, your bank, your coffee pot, and even your doorbell, all talk to servers using the HTTP protocol.

    Let’s look at how we can set up a simple HTTP server in Haskell. The standard low level components for this are called WAI and Warp. WAI, the Web Application Interface gives us a way to define how HTTP requests are handled. Warp is a simple HTTP server that runs the logic we have defined using WAI. That probably sounds a bit abstract right now, but a simple example will help.

    The file exercises/Examples/HelloServer.hs implements a HTTP server that always responds with “Hello World!”. You can try it out by going to the exercises/Examples directory and running it with stack runhaskell HelloServer.hs. After that you can visit http://localhost:3421 in your browser to see the response from the server.

    -
    module Examples.HelloServer where
    -
    -import qualified Data.ByteString.Lazy.Char8 as BL
    -import Network.HTTP.Types.Status (status200)
    -import Network.Wai (Application, responseLBS)
    -import Network.Wai.Handler.Warp (run)
    -
    -port :: Int
    -port = 3421
    -
    -main :: IO ()
    -main = run port application
    -
    --- type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
    -application :: Application
    -application request respond =
    -  respond (responseLBS status200 [] (BL.pack "Hello World!"))
    +
    module Examples.HelloServer where
    +
    +import qualified Data.ByteString.Lazy.Char8 as BL
    +import Network.HTTP.Types.Status (status200)
    +import Network.Wai (Application, responseLBS)
    +import Network.Wai.Handler.Warp (run)
    +
    +port :: Int
    +port = 3421
    +
    +main :: IO ()
    +main = run port application
    +
    +-- type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
    +application :: Application
    +application request respond =
    +  respond (responseLBS status200 [] (BL.pack "Hello World!"))

    Let’s look at the types in this example. There’s a lot going on here. First off, Application is a type alias for a thing that implements the logic of a web server. The run function from Warp can run Applications:

    -
    run :: Port -> Application -> IO ()
    -type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived
    +
    run :: Port -> Application -> IO ()
    +type Application = Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived

    We’ll talk about what Request and Response are soon, but from this type we can see that an Application is an IO operation that takes as arguments a request of type Request, and an IO operation respond :: Response -> IO ResponseReceived. Arguments like respond are called callbacks in many contexts. They allow us to call back to the library who called the application. The Application operation has to produce the same special ResponseReceived type that respond. You can think of this type as a token proving that respond was called by the Application.

    That might sound intimidating: but looking at the code things are relatively simple: our server is an Application and takes two parameters: request and respond.

    WAI uses lots of types like Port, Request, Response, Status to represent HTTP concepts. It’s useful to look these up in the documentation when you bump into them. For example Port is just an alias for Int. As another example, we can see the responseLBS function has the type

    -
    responseLBS :: Status -> ResponseHeaders -> ByteString -> Response
    +
    responseLBS :: Status -> ResponseHeaders -> ByteString -> Response

    where Status is defined in Network.HTTP.Types.Status, ResponseHeaders is a type alias for [Header] from Network.HTTP.Types.Header, ByteString is a lazy ByteString, and the result type Response is defined by Network.WAI.

    Finally, note that we take a shortcut and use the function Data.ByteString.Lazy.Char8.pack to convert a String into a ByteString. This only works for ASCII text.

    A web server that always responds with the same text isn’t that interesting. Let’s look at how we can give different responses to different requests next. There are many parts in a HTTP request, but for this lecture we’re going to focus on the path. In a URL like http://example.com/abcd/ef/file, the /abcd/ef/file part is the path. WAI has the function

    -
    pathInfo :: Request -> [Text]
    +
    pathInfo :: Request -> [Text]

    This gives us the path of the requested URL, split at the / characters.

    The file exercises/Examples/PathServer.hs implements a web server that has three different pages:

      @@ -3823,67 +3824,67 @@ None of the above

      In case you’re not familiar with SQL, don’t worry, you won’t need to write any queries of your own in the exercises. If you’d like to learn a bit of SQL now, there are lots of tutorials on the web. See W3Schools, SQL Zoo or Codecademy.

      There are many libraries for Haskell for using SQLite, but we’ll look at one called sqlite-simple here. Let’s explore the library in GHCi for a bit.

      All the functions live inside Database.SQLite.Simple. You can open a database by giving a filename to open, which is an IO operation that produces a Connection.

      -
      Prelude> import Database.SQLite.Simple
      -Prelude Database.SQLite.Simple> :t open
      -open :: String -> IO Connection
      -Prelude Database.SQLite.Simple> db <- open "example.sqlite"
      +
      Prelude> import Database.SQLite.Simple
      +Prelude Database.SQLite.Simple> :t open
      +open :: String -> IO Connection
      +Prelude Database.SQLite.Simple> db <- open "example.sqlite"

      To run an SQL query you can use IO operation query_, which takes a Connection and a Query, and produces a list of results. The Query type is just a simple newtype around Text. The result type of query_is polymorphic: you can read any type that satisfies the FromRow type class from the database. If this feels confusing, compare it to the type of read: Read a => String -> a. The FromRow class is like Read for this database: it represents types that can be read out of the database. Anyway, let’s read the number 1 from the database:

      -
      Prelude Database.SQLite.Simple> :t query_
      -query_ :: FromRow r => Connection -> Query -> IO [r]
      -Prelude Database.SQLite.Simple> :info Query
      -newtype Query = Query {fromQuery :: Data.Text.Internal.Text}
      -    -- Defined in ‘Database.SQLite.Simple.Types’
      -    -- ... rest of output omitted
      -Prelude Database.SQLite.Simple> import qualified Data.Text as T
      -Prelude Database.SQLite.Simple T> let q = Query (T.pack "SELECT 1;")
      -Prelude Database.SQLite.Simple T> res <- query_ db q :: IO [[Int]]
      -Prelude Database.SQLite.Simple T> res
      -[[1]]
      +
      Prelude Database.SQLite.Simple> :t query_
      +query_ :: FromRow r => Connection -> Query -> IO [r]
      +Prelude Database.SQLite.Simple> :info Query
      +newtype Query = Query {fromQuery :: Data.Text.Internal.Text}
      +    -- Defined in ‘Database.SQLite.Simple.Types’
      +    -- ... rest of output omitted
      +Prelude Database.SQLite.Simple> import qualified Data.Text as T
      +Prelude Database.SQLite.Simple T> let q = Query (T.pack "SELECT 1;")
      +Prelude Database.SQLite.Simple T> res <- query_ db q :: IO [[Int]]
      +Prelude Database.SQLite.Simple T> res
      +[[1]]

      By the way, all of these initial examples use simple SELECT x, y, z; queries that just return constant data. We’ll worry about actual tables in the database later.

      Without the type signature, we get an error from GHCi, which can’t decide which type we want to read out from the database:

      -
      Prelude Database.SQLite.Simple T> res <- query_ db q
      -
      -<interactive>:17:8: error:
      -    • Ambiguous type variable ‘r0’ arising from a use of ‘query_’
      -      prevents the constraint ‘(FromRow r0)’ from being solved.
      -      Probable fix: use a type annotation to specify what ‘r0’ should be.
      -      -- rest of error omitted
      +
      Prelude Database.SQLite.Simple T> res <- query_ db q
      +
      +<interactive>:17:8: error:
      +    • Ambiguous type variable ‘r0’ arising from a use of ‘query_’
      +      prevents the constraint ‘(FromRow r0)’ from being solved.
      +      Probable fix: use a type annotation to specify what ‘r0’ should be.
      +      -- rest of error omitted

      Before we go on, let’s have a closer look at FromRow. If you’ve bumped into SQL before, you know that an SQL query returns a number of rows, and each row consists of a number of values (also called columns). To be able to interpret the result of an SQL query into Haskell data, we need a way to interpret these values and rows. Thus sqlite-simple defines two classes, FromField and FromRow, and a bunch of instances like the following. (You can find these instances from the docs or by asking GHCi with :info FromRow etc.)

      -
      instance FromField Int
      -instance FromField Bool
      -instance FromField String
      -instance FromField Text
      -instance FromField a => FromRow [a]
      -instance (FromField a, FromField b) => FromRow (a,b)
      -instance (FromField a, FromField b, FromField c) => FromRow (a,b,c)
      +
      instance FromField Int
      +instance FromField Bool
      +instance FromField String
      +instance FromField Text
      +instance FromField a => FromRow [a]
      +instance (FromField a, FromField b) => FromRow (a,b)
      +instance (FromField a, FromField b, FromField c) => FromRow (a,b,c)

      In essence, basic Haskell datatypes fulfill the FromField class, and various Haskell collections fulfill the FromRow class. Our earlier example was using the FromRow [a] and FromField Int instances to get a [[Int]] out of query_. Here’s a simple query that uses some other datatypes:

      -
      Prelude Database.SQLite.Simple T> let q = Query (T.pack "SELECT 1, true, 'string';")
      -Prelude Database.SQLite.Simple T> query_ db q :: IO [(Int,Bool,String)]
      -[(1,True,"string")]
      +
      Prelude Database.SQLite.Simple T> let q = Query (T.pack "SELECT 1, true, 'string';")
      +Prelude Database.SQLite.Simple T> query_ db q :: IO [(Int,Bool,String)]
      +[(1,True,"string")]

      What happens if the SQL and Haskell types don’t match? Well, you get a runtime error, just like if you try to invoke read "True" :: Int.

      -
      Prelude Database.SQLite.Simple T> query_ db q :: IO [(Int,Int,Int)]
      -*** Exception: ConversionFailed {errSQLType = "TEXT", errHaskellType = "Int", errMessage = "need an int"}
      +
      Prelude Database.SQLite.Simple T> query_ db q :: IO [(Int,Int,Int)]
      +*** Exception: ConversionFailed {errSQLType = "TEXT", errHaskellType = "Int", errMessage = "need an int"}

      To mirror the FromRow and FromField classes, sqlite-simple also defines the ToRow and ToField classes for writing into the database. Here’s the type of the query function, which allows us to use parameterized queries.

      -
      query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r]
      +
      query :: (ToRow q, FromRow r) => Connection -> Query -> q -> IO [r]

      And here are some instances for ToRow and ToField:

      -
      instance ToField Int
      -instance ToField Bool
      -instance ToField String
      -instance ToField Text
      -instance ToField Int
      -
      -instance ToField a => ToRow [a]
      -instance (ToField a, ToField b) => ToRow (a, b)
      -instance (ToField a, ToField b, ToField c) => ToRow (a, b, c)
      +
      instance ToField Int
      +instance ToField Bool
      +instance ToField String
      +instance ToField Text
      +instance ToField Int
      +
      +instance ToField a => ToRow [a]
      +instance (ToField a, ToField b) => ToRow (a, b)
      +instance (ToField a, ToField b, ToField c) => ToRow (a, b, c)

      Parameterized queries use a ? character to denote slots where parameters can be passed in. Here’s a simple example:

      -
      Prelude Database.SQLite.Simple T> let input = (1,"hello") :: (Int,String)
      -Prelude Database.SQLite.Simple T> let parameterized = Query (T.pack "SELECT ?+1, true, ?;")
      -Prelude Database.SQLite.Simple T> query db parameterized input :: IO [(Int,Bool,String)]
      -[(2,True,"hello")]
      +
      Prelude Database.SQLite.Simple T> let input = (1,"hello") :: (Int,String)
      +Prelude Database.SQLite.Simple T> let parameterized = Query (T.pack "SELECT ?+1, true, ?;")
      +Prelude Database.SQLite.Simple T> query db parameterized input :: IO [(Int,Bool,String)]
      +[(2,True,"hello")]

      That’s pretty much all you need to know about sqlite-simple: open, query_, query, FromRow, ToRow. Oh right, one more thing. There are the functions execute and execute_ if you don’t need the result of the query. They’re useful for inserting things into the database, for example.

      -
      execute_ :: Connection -> Query -> IO ()
      -execute :: ToRow q => Connection -> Query -> q -> IO ()
      +
      execute_ :: Connection -> Query -> IO ()
      +execute :: ToRow q => Connection -> Query -> q -> IO ()

      You’ll find an example program that uses sqlite-simple to maintain a phone book under exercises/Examples/Phonebook.hs. The program keeps the phonebook in a file called phonebook.db and works like this (run from the exercises/Examples directory in the course repository):

      $ stack runhaskell Phonebook.hs
       (a)dd or (q)uery?
      @@ -3937,50 +3938,50 @@ bob
       
    • Necessity: there just is no way to define a Monad instance for your type, but there is an Applicative instance. This is rare.

    So what is an Applicative? Let’s look at a definition.

    -
    class Functor f => Applicative f where
    -  pure :: a -> f a
    -  liftA2 :: (a -> b -> c) -> f a -> f b -> f c
    -  -- other operations omitted for now
    +
    class Functor f => Applicative f where
    +  pure :: a -> f a
    +  liftA2 :: (a -> b -> c) -> f a -> f b -> f c
    +  -- other operations omitted for now

    So an Applicative is a Functor that allows us to build singleton values via pure, and combine two values into one using liftA2. This adds a lot of power compared to a bare functor. Computations using functors are necessarily linear: fmap :: (a -> b) -> f a -> f b takes in one functorial value, and outputs another. By contrast, pure takes in no functorial value and outputs one, and liftA2 takes in two and returns one.

    Sidenote: the term Applicative comes from the term Applicative Functor, which sounds like it comes from Category Theory, but was actually introduced in a programming paper.

    That’s enough abstract mumbo jumbo for now. Let’s look at what sort of computations we can express using Applicative operations (and fmap). We’ll start with the Maybe applicative. Here’s a streamlined definition:

    -
    instance Applicative Maybe where
    -  pure x = Just x
    -  liftA2 f (Just x) (Just y) = Just (f x y)
    -  liftA2 f _        _        = Nothing
    +
    instance Applicative Maybe where
    +  pure x = Just x
    +  liftA2 f (Just x) (Just y) = Just (f x y)
    +  liftA2 f _        _        = Nothing

    You’ll see the definition uses the same type of failure propagation as the Monad Maybe instance. Let’s use this when parsing monetary values:

    -
    data Currency = EUR | USD
    -  deriving (Show, Eq)
    -data Money = Money Int Currency
    -  deriving (Show, Eq)
    -
    -parseCurrency :: String -> Maybe Currency
    -parseCurrency "e" = pure EUR
    -parseCurrency "€" = pure EUR
    -parseCurrency "$" = pure USD
    -parseCurrency _ = Nothing
    -
    -parseAmount :: String -> Maybe Int
    -parseAmount = readMaybe
    -
    -parseMoney :: String -> String -> Maybe Money
    -parseMoney amountString currencyString =
    -  liftA2 Money (parseAmount amountString) (parseCurrency currencyString)
    -
    parseMoney "123" "€"  ==> Just (Money 123 EUR)
    -parseMoney "45" "$"   ==> Just (Money 45 USD)
    -parseMoney "4x" "€"   ==> Nothing
    -parseMoney "45" "£"   ==> Nothing
    +
    data Currency = EUR | USD
    +  deriving (Show, Eq)
    +data Money = Money Int Currency
    +  deriving (Show, Eq)
    +
    +parseCurrency :: String -> Maybe Currency
    +parseCurrency "e" = pure EUR
    +parseCurrency "€" = pure EUR
    +parseCurrency "$" = pure USD
    +parseCurrency _ = Nothing
    +
    +parseAmount :: String -> Maybe Int
    +parseAmount = readMaybe
    +
    +parseMoney :: String -> String -> Maybe Money
    +parseMoney amountString currencyString =
    +  liftA2 Money (parseAmount amountString) (parseCurrency currencyString)
    +
    parseMoney "123" "€"  ==> Just (Money 123 EUR)
    +parseMoney "45" "$"   ==> Just (Money 45 USD)
    +parseMoney "4x" "€"   ==> Nothing
    +parseMoney "45" "£"   ==> Nothing

    That worked out nicely. However, if we try to expand on this we soon run into the limits of Applicative. For example, consider this sumMoney function that sums Money values but fails if they are not in the same currency:

    -
    sumMoney :: Money -> Money -> Maybe Money
    -sumMoney (Money a c) (Money b c')
    -    | c == c'   = Just (Money (a+b) c)
    -    | otherwise = Nothing
    +
    sumMoney :: Money -> Money -> Maybe Money
    +sumMoney (Money a c) (Money b c')
    +    | c == c'   = Just (Money (a+b) c)
    +    | otherwise = Nothing

    We can’t apply it to two Maybe Money values using Applicative operations. We need the Maybe monad for that:

    -
    example :: Maybe Money
    -example = do x <- parseMoney "123" "€"
    -             y <- parseMoney "45" "$"
    -             sumMoney x y
    +
    example :: Maybe Money
    +example = do x <- parseMoney "123" "€"
    +             y <- parseMoney "45" "$"
    +             sumMoney x y

    If we try to use liftA2, we’re stuck with a Maybe (Maybe Money) type. In addition, we can now get two different types of failures: Nothing and Just Nothing, depending on what level the error happens on. This would be a clear case for switching to a Monad instance.

    liftA2 sumMoney (parseMoney "123" "e") (parseMoney "45" "€")
       ==> Just (Just (Money 168 EUR))
    @@ -3993,154 +3994,154 @@ liftA2 sumMoney (parseMoney "123" "e") (parseMoney "xxx
     

    15.2 The List Applicative

    Let’s have a look at the applicative instances for another Functor we’ve seen. The Applicative instance for the list functor goes through all possible combinations of values (just like the list monad). Here’s the instance:

    -
    instance Applicative [] where
    -  pure x = [x]
    -  liftA2 f xs ys = [f x y | x <- xs, y <- ys]
    +
    instance Applicative [] where
    +  pure x = [x]
    +  liftA2 f xs ys = [f x y | x <- xs, y <- ys]

    And here’s an example: generating some phrases.

    -
    things :: [String]
    -things = ["tangerine","bandit","diamond"]
    -
    -fruits :: [String]
    -fruits = ["apple", "tangerine"]
    -
    -phrases :: [String]
    -phrases = liftA2 combine things fruits
    -  where combine t f = "a " ++ t ++ " the size of a " ++ f
    -
    -bunches = liftA2 copy [1,2,3] fruits
    -  where copy n f = unwords (replicate n f)
    -
    phrases ==> ["a tangerine the size of a apple",
    -             "a tangerine the size of a tangerine",
    -             "a bandit the size of a apple",
    -             "a bandit the size of a tangerine",
    -             "a diamond the size of a apple",
    -             "a diamond the size of a tangerine"]
    -
    -bunches ==> ["apple","tangerine",
    -             "apple apple","tangerine tangerine",
    -             "apple apple apple","tangerine tangerine tangerine"]
    +
    things :: [String]
    +things = ["tangerine","bandit","diamond"]
    +
    +fruits :: [String]
    +fruits = ["apple", "tangerine"]
    +
    +phrases :: [String]
    +phrases = liftA2 combine things fruits
    +  where combine t f = "a " ++ t ++ " the size of a " ++ f
    +
    +bunches = liftA2 copy [1,2,3] fruits
    +  where copy n f = unwords (replicate n f)
    +
    phrases ==> ["a tangerine the size of a apple",
    +             "a tangerine the size of a tangerine",
    +             "a bandit the size of a apple",
    +             "a bandit the size of a tangerine",
    +             "a diamond the size of a apple",
    +             "a diamond the size of a tangerine"]
    +
    +bunches ==> ["apple","tangerine",
    +             "apple apple","tangerine tangerine",
    +             "apple apple apple","tangerine tangerine tangerine"]

    15.3 New Operators

    There are a handful of operators for Applicatives that are quite handy. They are <$>, <*>, <* and *>.

    Let’s start with <$>, which is just an infix version of fmap:

    -
    (<$>) :: Functor f => (a -> b) -> f a -> f b
    -f <$> x = fmap f x
    -
    not <$> Just True   ==> Just False
    -not <$> Nothing     ==> Nothing
    -negate <$> [1,2,3]  ==> [-1,-2,-3]
    +
    (<$>) :: Functor f => (a -> b) -> f a -> f b
    +f <$> x = fmap f x
    +
    not <$> Just True   ==> Just False
    +not <$> Nothing     ==> Nothing
    +negate <$> [1,2,3]  ==> [-1,-2,-3]

    That’s kinda nice on its own, but it really gets to shine when combined with this Applicative operator:

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

    The type tells you what <*> does: it’s function application lifted to an applicative. Here are some standalone examples:

    -
    Just not <*> Just True    ==> Just False
    -Nothing  <*> Just True    ==> Nothing
    -Just not <*> Nothing      ==> Nothing
    -[(+1),(*2)] <*> [10,100]  ==> [11,101,20,200]
    +
    Just not <*> Just True    ==> Just False
    +Nothing  <*> Just True    ==> Nothing
    +Just not <*> Nothing      ==> Nothing
    +[(+1),(*2)] <*> [10,100]  ==> [11,101,20,200]

    The real magic happens when we combine <$> and <*>: we can then lift functions of arbitrarily many argments to an Applicative!

    -
    say :: String -> Int -> String -> String
    -say x i y = x ++ " has " ++ show i ++ " " ++ y
    -
    say <$> Just "haskell" <*> Just 99 <*> Just "operators"
    -  ==> Just "haskell has 99 operators"
    -say <$> Nothing <*> Just 99 <*> Just "operators"
    -  ==> Nothing
    -say <$> ["bob","jake"] <*> [2,3] <*> ["bananas","cars"]
    -  ==> ["bob has 2 bananas",
    -       "bob has 2 cars",
    -       "bob has 3 bananas",
    -       "bob has 3 cars",
    -       "jake has 2 bananas",
    -       "jake has 2 cars",
    -       "jake has 3 bananas",
    -       "jake has 3 cars"]
    +
    say :: String -> Int -> String -> String
    +say x i y = x ++ " has " ++ show i ++ " " ++ y
    +
    say <$> Just "haskell" <*> Just 99 <*> Just "operators"
    +  ==> Just "haskell has 99 operators"
    +say <$> Nothing <*> Just 99 <*> Just "operators"
    +  ==> Nothing
    +say <$> ["bob","jake"] <*> [2,3] <*> ["bananas","cars"]
    +  ==> ["bob has 2 bananas",
    +       "bob has 2 cars",
    +       "bob has 3 bananas",
    +       "bob has 3 cars",
    +       "jake has 2 bananas",
    +       "jake has 2 cars",
    +       "jake has 3 bananas",
    +       "jake has 3 cars"]

    What’s going on here? Let’s step through the evaluation. The key is that each <*> partially applies one more argument to the function.

    -
        say <$> Just "haskell" <*> Just 99 <*> Just "operators"
    -=== ((say <$> Just "haskell") <*> Just 99) <*> Just "operators"
    -=== (fmap say (Just "haskell") <*> Just 99) <*> Just "operators"
    -==> (Just (say "haskell") <*> Just 99) <*> Just "operators"
    -==> Just (say "haskell" 99) <*> Just "operators"
    -==> Just (say "haskell" 99 "operators")
    -==> Just "haskell has 99 operators"
    +
        say <$> Just "haskell" <*> Just 99 <*> Just "operators"
    +=== ((say <$> Just "haskell") <*> Just 99) <*> Just "operators"
    +=== (fmap say (Just "haskell") <*> Just 99) <*> Just "operators"
    +==> (Just (say "haskell") <*> Just 99) <*> Just "operators"
    +==> Just (say "haskell" 99) <*> Just "operators"
    +==> Just (say "haskell" 99 "operators")
    +==> Just "haskell has 99 operators"

    Perhaps looking at the types will make it clearer:

    -
    say <$> Just "haskell"                                  :: Maybe (Int -> String -> String)
    -say <$> Just "haskell" <*> Just 99                      :: Maybe (       String -> String)
    -say <$> Just "haskell" <*> Just 99 <*> Just "operators" :: Maybe (                 String)
    +
    say <$> Just "haskell"                                  :: Maybe (Int -> String -> String)
    +say <$> Just "haskell" <*> Just 99                      :: Maybe (       String -> String)
    +say <$> Just "haskell" <*> Just 99 <*> Just "operators" :: Maybe (                 String)

    The next two operators are a bit simpler:

    -
    (*>) :: Applicative f => f a -> f b -> f b
    -x *> y = liftA2 (\a b -> b) x y
    -
    -(<*) :: Applicative f => f a -> f b -> f a
    -x <* y = liftA2 (\a b -> a) x y
    +
    (*>) :: Applicative f => f a -> f b -> f b
    +x *> y = liftA2 (\a b -> b) x y
    +
    +(<*) :: Applicative f => f a -> f b -> f a
    +x <* y = liftA2 (\a b -> a) x y

    You can compare the types to a more familiar operator:

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

    What the operators <* and *> mean is: run both of these operations, but only keep one result. The arrow points to the result that’s kept:

    -
    Just 1 *> Just 2  ==> Just 2
    -Just 1 <* Just 2  ==> Just 1
    -Just 1 <* Nothing ==> Nothing
    -Nothing <* Just 2 ==> Nothing
    +
    Just 1 *> Just 2  ==> Just 2
    +Just 1 <* Just 2  ==> Just 1
    +Just 1 <* Nothing ==> Nothing
    +Nothing <* Just 2 ==> Nothing

    These operators might seem trivial, but they’re useful when combining checks. For example:

    -
    decrease :: Int -> Maybe Int
    -decrease i = if i>0 then Just (i-1) else Nothing
    -
    -small :: Int -> Maybe Int
    -small i = if i<10 then Just i else Nothing
    -
    -decreaseSmall :: Int -> Maybe Int
    --- do what decrease does, but fail if small fails
    -decreaseSmall i = decrease i <* small i
    -
    decreaseSmall 4   ==> Just 3
    -decreaseSmall 0   ==> Nothing
    -decreaseSmall 11  ==> Nothing
    +
    decrease :: Int -> Maybe Int
    +decrease i = if i>0 then Just (i-1) else Nothing
    +
    +small :: Int -> Maybe Int
    +small i = if i<10 then Just i else Nothing
    +
    +decreaseSmall :: Int -> Maybe Int
    +-- do what decrease does, but fail if small fails
    +decreaseSmall i = decrease i <* small i
    +
    decreaseSmall 4   ==> Just 3
    +decreaseSmall 0   ==> Nothing
    +decreaseSmall 11  ==> Nothing

    Now that we’ve seen all these operators, we can understand the full definition of Applicative. All the operators have definitions in terms of liftA2, so it’s enought to define liftA2 and pure when implementing an Applicative instance.

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

    15.4 The Validation Applicative

    Let’s look at an Applicative that’s a bit more interesting than Maybe or lists. Often in programming we need to validate some inputs from the user. In these cases it’s useful to gather together all the errors that the input might have. The file exercises/Examples/Validation.hs implements the Validation datatype:

    -
    data Validation a = Ok a | Errors [String]
    -  deriving (Show,Eq)
    +
    data Validation a = Ok a | Errors [String]
    +  deriving (Show,Eq)

    The Applicative instance for Validation works like this:

    -
    liftA2 (+) (Ok 1) (Ok 2)
    -  ==> Ok 3
    -liftA2 (+) (Errors ["oh no"]) (Errors ["boom"])
    -  ==> Errors ["oh no","boom"]
    +
    liftA2 (+) (Ok 1) (Ok 2)
    +  ==> Ok 3
    +liftA2 (+) (Errors ["oh no"]) (Errors ["boom"])
    +  ==> Errors ["oh no","boom"]

    Note how in contrast to the Maybe Applicative, we have many different kinds of failures.

    Here’s a worked example that introduces some helpers and then uses them to congratulate somebody on their birthday:

    -
    invalid :: String -> Validation a
    -invalid err = Errors [err]
    -
    -check :: Bool -> String -> a -> Validation a
    -check b err x
    -  | b = pure x
    -  | otherwise = invalid err
    -
    -birthday :: String -> Int -> Validation String
    -birthday name age = liftA2 congratulate checkedName checkedAge
    -  where checkedName = check (length name < 10) "Name too long" name
    -        checkedAge = check (age < 99) "Too old" age
    -        congratulate n a = "Happy "++show a++"th birthday "++n++"!"
    -
    birthday "Guy" 31
    -  ==> Ok "Happy 31th birthday Guy!"
    -birthday "Guybrush Threepwood" 31
    -  ==> Errors ["Name too long"]
    -birthday "Yog-sothoth" 10000
    -  ==> Errors ["Name too long","Too old"]
    +
    invalid :: String -> Validation a
    +invalid err = Errors [err]
    +
    +check :: Bool -> String -> a -> Validation a
    +check b err x
    +  | b = pure x
    +  | otherwise = invalid err
    +
    +birthday :: String -> Int -> Validation String
    +birthday name age = liftA2 congratulate checkedName checkedAge
    +  where checkedName = check (length name < 10) "Name too long" name
    +        checkedAge = check (age < 99) "Too old" age
    +        congratulate n a = "Happy "++show a++"th birthday "++n++"!"
    +
    birthday "Guy" 31
    +  ==> Ok "Happy 31th birthday Guy!"
    +birthday "Guybrush Threepwood" 31
    +  ==> Errors ["Name too long"]
    +birthday "Yog-sothoth" 10000
    +  ==> Errors ["Name too long","Too old"]

    Oh right, here are the Functor and Applicative instances for Validation:

    -
    instance Functor Validation where
    -  fmap f (Ok x) = Ok (f x)
    -  fmap _ (Errors e) = Errors e
    -
    -instance Applicative Validation where
    -  pure x = Ok x
    -  liftA2 f (Ok x)      (Ok y)      = Ok (f x y)
    -  liftA2 f (Errors e1) (Ok y)      = Errors e1
    -  liftA2 f (Ok x)      (Errors e2) = Errors e2
    -  liftA2 f (Errors e1) (Errors e2) = Errors (e1++e2)
    +
    instance Functor Validation where
    +  fmap f (Ok x) = Ok (f x)
    +  fmap _ (Errors e) = Errors e
    +
    +instance Applicative Validation where
    +  pure x = Ok x
    +  liftA2 f (Ok x)      (Ok y)      = Ok (f x y)
    +  liftA2 f (Errors e1) (Ok y)      = Errors e1
    +  liftA2 f (Ok x)      (Errors e2) = Errors e2
    +  liftA2 f (Errors e1) (Errors e2) = Errors (e1++e2)

    The definition of liftA2 for Validation shows that the errors are collected together left-to-right. This can be seen in the example above where the expression liftA2 congratulate checkedName checkedAge outputs the error ("Name too long") from checkedName first, and the error ("Too old") from checkedAge last.

    @@ -4148,42 +4149,42 @@ liftA2 sumMoney (parseMoney "123" "e") (parseMoney "xxx

    15.5 Validating Lists: traverse

    So far we’ve dealt with fixed-size things and Applicatives: we’ve applied a function of two or three arguments to some things. What if we have an arbitrary amount of inputs? What if we need to validate a list?

    Let’s look at some ways to implement a function like this:

    -
    allPositive [1,2,3]
    -  ==> Ok [1,2,3]
    -allPositive [1,2,3,-4]
    -  ==> Errors ["Not positive: -4"]
    -allPositive [1,-2,3,-4]
    -  ==> Errors ["Not positive: -2","Not positive: -4"]
    +
    allPositive [1,2,3]
    +  ==> Ok [1,2,3]
    +allPositive [1,2,3,-4]
    +  ==> Errors ["Not positive: -4"]
    +allPositive [1,-2,3,-4]
    +  ==> Errors ["Not positive: -2","Not positive: -4"]

    As always, when working with lists, pattern matching and recursion are usually the way to go. Here’s a recursive solution:

    -
    allPositive :: [Int] -> Validation [Int]
    -allPositive [] = Ok []
    -allPositive (x:xs) = liftA2 (:) checkThis checkRest
    -  where checkThis = check (x>=0) ("Not positive: "++show x) x
    -        checkRest = allPositive xs
    +
    allPositive :: [Int] -> Validation [Int]
    +allPositive [] = Ok []
    +allPositive (x:xs) = liftA2 (:) checkThis checkRest
    +  where checkThis = check (x>=0) ("Not positive: "++show x) x
    +        checkRest = allPositive xs

    It’s a bit of a chore to always spell out a recursion like this. If we were working in a Monad we could just use a helper like mapM:

    -
    mapM (\x -> if x>=0 then Just x else Nothing) [1,2,3]
    -  ==> Just [1,2,3]
    -mapM (\x -> if x>=0 then Just x else Nothing) [1,2,3,-4]
    -  ==> Nothing
    +
    mapM (\x -> if x>=0 then Just x else Nothing) [1,2,3]
    +  ==> Just [1,2,3]
    +mapM (\x -> if x>=0 then Just x else Nothing) [1,2,3,-4]
    +  ==> Nothing

    The equivalent of mapM for Applicative is called traverse. It’s a member of the type class Traversable:

    -
    traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
    +
    traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

    That’s one heck of a type signature, so let’s simplify it a bit. Lists are Traversable, so we can specialize this type into:

    -
    traverse :: Applicative f => (a -> f b) -> [a] -> f [b]
    +
    traverse :: Applicative f => (a -> f b) -> [a] -> f [b]

    That looks like what we need! For Applicatives that are also Monads, traverse is just another name for mapM:

    -
    traverse (\x -> if x>=0 then Just x else Nothing) [1,2,3]
    -  ==> Just [1,2,3]
    -traverse (\x -> if x>=0 then Just x else Nothing) [1,2,3,-4]
    -  ==> Nothing
    +
    traverse (\x -> if x>=0 then Just x else Nothing) [1,2,3]
    +  ==> Just [1,2,3]
    +traverse (\x -> if x>=0 then Just x else Nothing) [1,2,3,-4]
    +  ==> Nothing

    But for our Validation, which isn’t a Monad, traverse is exactly what we want:

    -
    allPositive :: [Int] -> Validation [Int]
    -allPositive xs = traverse checkNumber xs
    -  where checkNumber x = check (x>=0) ("Not positive: "++show x) x
    -
    allPositive [1,2,3]
    -  ==> Ok [1,2,3]
    -allPositive [1,2,3,-4]
    -  ==> Errors ["Not positive: -4"]
    -allPositive [1,-2,3,-4]
    -  ==> Errors ["Not positive: -2","Not positive: -4"]
    +
    allPositive :: [Int] -> Validation [Int]
    +allPositive xs = traverse checkNumber xs
    +  where checkNumber x = check (x>=0) ("Not positive: "++show x) x
    +
    allPositive [1,2,3]
    +  ==> Ok [1,2,3]
    +allPositive [1,2,3,-4]
    +  ==> Errors ["Not positive: -4"]
    +allPositive [1,-2,3,-4]
    +  ==> Errors ["Not positive: -2","Not positive: -4"]

    Note how traverse for Validation collects all the errors together, in the order they occur in the original list.

    PS. In fact, Validation is one of the few examples of an Applicative that can’t be a Monad. Can you figure out why?

    @@ -4191,144 +4192,144 @@ liftA2 sumMoney (parseMoney "123" "e") (parseMoney "xxx

    15.6 Sidenote: Traversable

    So what things are Traversable? Many familiar structures. Here are some examples:

    -
    decrease :: Int -> Maybe Int
    -decrease i = if i>0 then Just (i-1) else Nothing
    -
    -- Lists are Traversable
    -traverse decrease [1,2,3] ==> Just [0,1,2]
    -traverse decrease [1,0,3] ==> Nothing
    -
    --- Arrays are Traversable
    -traverse decrease (array (1,3) [(1,10),(2,11),(3,12)])
    -         ==> Just (array (1,3) [(1,9),(2,10),(3,11)])
    -
    --- Maps are Traversable
    -traverse decrease (M.fromList [("a",1),("b",2)])
    -         ==> Just (M.fromList [("a",0),("b",1)])
    -traverse decrease (M.fromList [("a",1),("b",0)])
    -         ==> Nothing
    -
    --- Either is Traversable
    -traverse decrease (Left "abc") ==> Just (Left "abc")
    -traverse decrease (Right 3)    ==> Just (Right 2)
    -traverse decrease (Right 0)    ==> Nothing
    +
    decrease :: Int -> Maybe Int
    +decrease i = if i>0 then Just (i-1) else Nothing
    +
    -- Lists are Traversable
    +traverse decrease [1,2,3] ==> Just [0,1,2]
    +traverse decrease [1,0,3] ==> Nothing
    +
    +-- Arrays are Traversable
    +traverse decrease (array (1,3) [(1,10),(2,11),(3,12)])
    +         ==> Just (array (1,3) [(1,9),(2,10),(3,11)])
    +
    +-- Maps are Traversable
    +traverse decrease (M.fromList [("a",1),("b",2)])
    +         ==> Just (M.fromList [("a",0),("b",1)])
    +traverse decrease (M.fromList [("a",1),("b",0)])
    +         ==> Nothing
    +
    +-- Either is Traversable
    +traverse decrease (Left "abc") ==> Just (Left "abc")
    +traverse decrease (Right 3)    ==> Just (Right 2)
    +traverse decrease (Right 0)    ==> Nothing

    So Traversable is a type class for all sorts of containers, kind of like Foldable. Indeed, if you look at the definition, Traversable is a subclass of Foldable. It also turns out that traverse and mapM are methods of the class!

    -
    class (Functor t, Foldable t) => Traversable t where
    -  traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
    -  mapM :: Monad m => (a -> m b) -> t a -> m (t b)
    +
    class (Functor t, Foldable t) => Traversable t where
    +  traverse :: Applicative f => (a -> f b) -> t a -> f (t b)
    +  mapM :: Monad m => (a -> m b) -> t a -> m (t b)

    It can be hard to keep the types straight here. Let’s go back to the type of traverse:

    -
    traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
    +
    traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)

    Here we have two functors: t and f. The t functor is also a Foldable and a Traversable and the f functor is also an Applicative. The traverse function lets us run f-operations inside a t-container.

    Don’t worry if that feels abstract. In practice, you pretty much always use traverse on lists.

    15.7 Dealing with Failure: Alternative

    If you play around with applicatives a bit you’ll start to notice some limits to their power. For example, when writing parsers like we did in the parseMoney example, it would be nice to be able to try some a couple of different parsers and take any non-failure result. This is easy enough to write for a concrete Applicative like Maybe, as can be seen below.

    -
    data Answer = Yes | No
    -  deriving (Show, Eq)
    -
    -parseYes :: String -> Maybe Answer
    -parseYes "y" = Just Yes
    -parseYes "yes" = Just Yes
    -parseYes "maybe" = Just Yes
    -parseYes _ = Nothing
    -
    -parseNo :: String -> Maybe Answer
    -parseNo "n" = Just No
    -parseNo "no" = Just No
    -parseNo "maybe" = Just No
    -parseNo _ = Nothing
    -
    -eitherOf :: Maybe x -> Maybe x -> Maybe x
    -eitherOf (Just x) _  = Just x
    -eitherOf Nothing  mx = mx
    -
    -parseAnswer :: String -> Maybe Answer
    --- prefer positive answers!
    -parseAnswer s = eitherOf (parseYes s) (parseNo s)
    -
    parseAnswer "yes"    ==> Just Yes
    -parseAnswer "y"      ==> Just Yes
    -parseAnswer "n"      ==> Just No
    -parseAnswer "maybe"  ==> Just Yes
    -parseAnswer "x"      ==> Nothing
    +
    data Answer = Yes | No
    +  deriving (Show, Eq)
    +
    +parseYes :: String -> Maybe Answer
    +parseYes "y" = Just Yes
    +parseYes "yes" = Just Yes
    +parseYes "maybe" = Just Yes
    +parseYes _ = Nothing
    +
    +parseNo :: String -> Maybe Answer
    +parseNo "n" = Just No
    +parseNo "no" = Just No
    +parseNo "maybe" = Just No
    +parseNo _ = Nothing
    +
    +eitherOf :: Maybe x -> Maybe x -> Maybe x
    +eitherOf (Just x) _  = Just x
    +eitherOf Nothing  mx = mx
    +
    +parseAnswer :: String -> Maybe Answer
    +-- prefer positive answers!
    +parseAnswer s = eitherOf (parseYes s) (parseNo s)
    +
    parseAnswer "yes"    ==> Just Yes
    +parseAnswer "y"      ==> Just Yes
    +parseAnswer "n"      ==> Just No
    +parseAnswer "maybe"  ==> Just Yes
    +parseAnswer "x"      ==> Nothing

    How could we generalize eitherOf? We can’t give it the type Applicative f => f x -> f x -> f x because then the implementation would need to be effectively something like eitherOf a b = liftA2 something a b, but then eitherOf Nothing (Just x) would be Nothing (since that’s how the Applicative instance works)!

    It turns out we need a new type class: Alternative. An Alternative adds two operations to Applicative: empty means no results, and <|> means combining results.

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

    Now we can rewrite our parsing code using general operations:

    -
    data Answer = Yes | No
    -  deriving (Show, Eq)
    -
    -parseYes :: Alternative f => String -> f Answer
    -parseYes "y" = pure Yes
    -parseYes "yes" = pure Yes
    -parseYes "maybe" = pure Yes
    -parseYes _ = empty
    -
    -parseNo :: Alternative f => String -> f Answer
    -parseNo "n" = pure No
    -parseNo "no" = pure No
    -parseNo "maybe" = pure No
    -parseNo _ = empty
    -
    -parseAnswer :: Alternative f => String -> f Answer
    -parseAnswer s = parseYes s <|> parseNo s
    +
    data Answer = Yes | No
    +  deriving (Show, Eq)
    +
    +parseYes :: Alternative f => String -> f Answer
    +parseYes "y" = pure Yes
    +parseYes "yes" = pure Yes
    +parseYes "maybe" = pure Yes
    +parseYes _ = empty
    +
    +parseNo :: Alternative f => String -> f Answer
    +parseNo "n" = pure No
    +parseNo "no" = pure No
    +parseNo "maybe" = pure No
    +parseNo _ = empty
    +
    +parseAnswer :: Alternative f => String -> f Answer
    +parseAnswer s = parseYes s <|> parseNo s

    We can also pick which Alternative we run our parser in to get different behaviours. Maybe gives us only one result, while [] gives us all possible results.

    -
    > parseAnswer "yes" :: Maybe Answer
    -Just Yes
    -> parseAnswer "maybe" :: Maybe Answer
    -Just Yes
    -> parseAnswer "yes" :: [Answer]
    -[Yes]
    -> parseAnswer "maybe" :: [Answer]
    -[Yes,No]
    +
    > parseAnswer "yes" :: Maybe Answer
    +Just Yes
    +> parseAnswer "maybe" :: Maybe Answer
    +Just Yes
    +> parseAnswer "yes" :: [Answer]
    +[Yes]
    +> parseAnswer "maybe" :: [Answer]
    +[Yes,No]

    The Alternative instances for [] and Maybe are unsurprising:

    -
    instance Alternative [] where
    -  empty = []
    -  (<|>) = (++)
    -
    -instance Alternative Maybe where
    -  empty = Nothing
    -  Just x  <|> _  = Just x
    -  Nothing <|> mx = mx
    +
    instance Alternative [] where
    +  empty = []
    +  (<|>) = (++)
    +
    +instance Alternative Maybe where
    +  empty = Nothing
    +  Just x  <|> _  = Just x
    +  Nothing <|> mx = mx

    The Validation type is also an Alternative. The instance collects together all error messages, just like the Applicative instance did.

    -
    instance Alternative Validation where
    -  empty = Errors []
    -  Ok x <|> _ = Ok x
    -  Errors e1 <|> Ok y = Ok y
    -  Errors e1 <|> Errors e2 = Errors (e1++e2)
    +
    instance Alternative Validation where
    +  empty = Errors []
    +  Ok x <|> _ = Ok x
    +  Errors e1 <|> Ok y = Ok y
    +  Errors e1 <|> Errors e2 = Errors (e1++e2)

    Here’s a final example: validating contact information, which is either a phone number or an email address.

    -
    data ContactInfo = Email String | Phone String
    -  deriving Show
    -
    -validateEmail :: String -> Validation ContactInfo
    -validateEmail s = check (elem '@' s) "Not an email: should contain a @" (Email s)
    -
    -checkLength :: String -> Validation ContactInfo
    -checkLength s = check (length s <= 10) "Not a phone number: should be at most 10 digits" (Phone s)
    -
    -checkDigits :: String -> Validation ContactInfo
    -checkDigits s = check (all isDigit s) "Not a phone number: should be all numbers" (Phone s)
    -
    -validatePhone :: String -> Validation ContactInfo
    -validatePhone s = checkDigits s *> checkLength s
    -
    -validateContactInfo :: String -> Validation ContactInfo
    -validateContactInfo s = validateEmail s <|> validatePhone s
    -
    validateContactInfo "user@example.com"
    -  ==> Ok (Email "user@example.com")
    -validateContactInfo "01234"
    -  ==> Ok (Phone "01234")
    -validateContactInfo "01234567890"
    -  ==> Errors ["Not an email: should contain a @","Not a phone number: should be at most 10 digits"]
    -validateContactInfo "01234567890x"
    -  ==> Errors ["Not an email: should contain a @",
    -              "Not a phone number: should be all numbers",
    -              "Not a phone number: should be at most 10 digits"]
    -validateContactInfo "x"
    -  ==> Errors ["Not an email: should contain a @",
    -              "Not a phone number: should be all numbers"]
    +
    data ContactInfo = Email String | Phone String
    +  deriving Show
    +
    +validateEmail :: String -> Validation ContactInfo
    +validateEmail s = check (elem '@' s) "Not an email: should contain a @" (Email s)
    +
    +checkLength :: String -> Validation ContactInfo
    +checkLength s = check (length s <= 10) "Not a phone number: should be at most 10 digits" (Phone s)
    +
    +checkDigits :: String -> Validation ContactInfo
    +checkDigits s = check (all isDigit s) "Not a phone number: should be all numbers" (Phone s)
    +
    +validatePhone :: String -> Validation ContactInfo
    +validatePhone s = checkDigits s *> checkLength s
    +
    +validateContactInfo :: String -> Validation ContactInfo
    +validateContactInfo s = validateEmail s <|> validatePhone s
    +
    validateContactInfo "user@example.com"
    +  ==> Ok (Email "user@example.com")
    +validateContactInfo "01234"
    +  ==> Ok (Phone "01234")
    +validateContactInfo "01234567890"
    +  ==> Errors ["Not an email: should contain a @","Not a phone number: should be at most 10 digits"]
    +validateContactInfo "01234567890x"
    +  ==> Errors ["Not an email: should contain a @",
    +              "Not a phone number: should be all numbers",
    +              "Not a phone number: should be at most 10 digits"]
    +validateContactInfo "x"
    +  ==> Errors ["Not an email: should contain a @",
    +              "Not a phone number: should be all numbers"]

    Note how here, just like in previous examples, the errors are collected left-to-right: the errors from validateEmail come before the errors from validatePhone. The error from checkDigits comes before the error from checkLength.

    @@ -4690,102 +4691,102 @@ TODO explain `some` and `many`

    Boo! There’s a ghost in the type system! Let’s have a look at what phantom types can do for you.

    Phantom types are types that don’t take any values. They are related to newtypes (see lecture 10) in that both are a way to add additional type checking without affecting the evaluation of the program at all.

    Let’s use phantom types to track which currency an amount of money is in. We define the phantom types EUR and USD (note how they don’t have any constructors!), and the parameterized type Money a that doesn’t use the type parameter a for anything. Then we can define two constants, one in euros and the other in dollars. You can find all of the code from this section in the file exercises/Examples/Phantom.hs.

    -
    data EUR
    -data USD
    -data Money currency = Money Double
    -  deriving Show
    -
    -dollar :: Money USD
    -dollar = Money 1
    -
    -twoEuros :: Money EUR
    -twoEuros = Money 2
    +
    data EUR
    +data USD
    +data Money currency = Money Double
    +  deriving Show
    +
    +dollar :: Money USD
    +dollar = Money 1
    +
    +twoEuros :: Money EUR
    +twoEuros = Money 2

    Note how the type signatures for dollar and twoEuros are doing all the work here. An expression like Money 1 has the polymorphic type Money currency if we don’t restrict it to a more specific type. We give dollar and twoEuros more limited types explicitly. This is analogous to defining something like one :: Int; one = 1, since the constant 1 has the polymorphic type Num p => p but we give it a more restricted type.

    -
    *Examples.Phantom> :t Money
    -Money :: Double -> Money currency
    -*Examples.Phantom> :t Money 1
    -Money 1 :: Money currency
    +
    *Examples.Phantom> :t Money
    +Money :: Double -> Money currency
    +*Examples.Phantom> :t Money 1
    +Money 1 :: Money currency

    Now that we have some constants, we can write functions that operate on them. Let’s start with a function scaleMoney that multiplies an amount of money by a number. The currency stays constant. Here too, the type signature is doing the work: without the type signature, Haskell would infer a type of Double -> Money a -> Money b.

    -
    scaleMoney :: Double -> Money currency -> Money currency
    -scaleMoney factor (Money a) = Money (factor * a)
    -
    *Examples.Phantom> :t scaleMoney 3 twoEuros
    -scaleMoney 3 twoEuros :: Money EUR
    -*Examples.Phantom> :t scaleMoney 3 dollar
    -scaleMoney 3 dollar :: Money USD
    +
    scaleMoney :: Double -> Money currency -> Money currency
    +scaleMoney factor (Money a) = Money (factor * a)
    +
    *Examples.Phantom> :t scaleMoney 3 twoEuros
    +scaleMoney 3 twoEuros :: Money EUR
    +*Examples.Phantom> :t scaleMoney 3 dollar
    +scaleMoney 3 dollar :: Money USD

    Next up: adding two amounts that are in the same currency. We get a nice type error if we try to add values in two different currencies.

    -
    addMoney :: Money currency -> Money currency -> Money currency
    -addMoney (Money a) (Money b) = Money (a+b)
    -
    *Examples.Phantom> :t addMoney dollar dollar
    -addMoney dollar dollar :: Money USD
    -*Examples.Phantom> :t addMoney twoEuros twoEuros
    -addMoney twoEuros twoEuros :: Money EUR
    -*Examples.Phantom> :t addMoney twoEuros dollar
    -error:
    -    • Couldn't match type ‘USD’ with ‘EUR’
    -      Expected type: Money EUR
    -        Actual type: Money USD
    -    • In the second argument of ‘addMoney’, namely ‘dollar’
    -      In the expression: addMoney twoEuros dollar
    +
    addMoney :: Money currency -> Money currency -> Money currency
    +addMoney (Money a) (Money b) = Money (a+b)
    +
    *Examples.Phantom> :t addMoney dollar dollar
    +addMoney dollar dollar :: Money USD
    +*Examples.Phantom> :t addMoney twoEuros twoEuros
    +addMoney twoEuros twoEuros :: Money EUR
    +*Examples.Phantom> :t addMoney twoEuros dollar
    +error:
    +    • Couldn't match type ‘USD’ with ‘EUR’
    +      Expected type: Money EUR
    +        Actual type: Money USD
    +    • In the second argument of ‘addMoney’, namely ‘dollar’
    +      In the expression: addMoney twoEuros dollar

    As before, the type signature is crucial. Here’s the same implementation with an unrestricted type. Now we can add anything to anything!

    -
    addMoneyUnsafe :: Money x -> Money y -> Money z
    -addMoneyUnsafe (Money a) (Money b) = Money (a+b)
    -
    *Examples.Phantom> addMoneyUnsafe twoEuros dollar
    -Money 3.0
    +
    addMoneyUnsafe :: Money x -> Money y -> Money z
    +addMoneyUnsafe (Money a) (Money b) = Money (a+b)
    +
    *Examples.Phantom> addMoneyUnsafe twoEuros dollar
    +Money 3.0

    We can keep going with this approach, and define currency conversions. We define the type Rate that uses phantom types to track the currencies it’s translating between. The types of convert and invert are restricted to have the properties we want. There’s also an unrestricted version of the convert function to let you compare the types.

    -
    data Rate from to = Rate Double
    -  deriving Show
    -
    -eurToUsd :: Rate EUR USD
    -eurToUsd = Rate 1.22
    -
    -convert :: Rate from to -> Money from -> Money to
    -convert (Rate r) (Money a) = Money (r*a)
    -
    -invert :: Rate from to -> Rate to from
    -invert (Rate r) = Rate (1/r)
    -
    -convertUnsafe :: Rate from to -> Money x -> Money y
    -convertUnsafe (Rate r) (Money a) = Money (r*a)
    -
    *Examples.Phantom> convert eurToUsd twoEuros
    -Money 2.44
    -*Examples.Phantom> convert eurToUsd dollar
    -error:
    -    • Couldn't match type ‘USD’ with ‘EUR’
    -      Expected type: Money EUR
    -        Actual type: Money USD
    -    • In the second argument of ‘convert’, namely ‘dollar’
    -      In the expression: convert eurToUsd dollar
    -      In an equation for ‘it’: it = convert eurToUsd dollar
    -*Examples.Phantom> convert (invert eurToUsd) dollar
    -Money 0.819672131147541
    -*Examples.Phantom> convertUnsafe eurToUsd dollar
    -Money 1.22
    +
    data Rate from to = Rate Double
    +  deriving Show
    +
    +eurToUsd :: Rate EUR USD
    +eurToUsd = Rate 1.22
    +
    +convert :: Rate from to -> Money from -> Money to
    +convert (Rate r) (Money a) = Money (r*a)
    +
    +invert :: Rate from to -> Rate to from
    +invert (Rate r) = Rate (1/r)
    +
    +convertUnsafe :: Rate from to -> Money x -> Money y
    +convertUnsafe (Rate r) (Money a) = Money (r*a)
    +
    *Examples.Phantom> convert eurToUsd twoEuros
    +Money 2.44
    +*Examples.Phantom> convert eurToUsd dollar
    +error:
    +    • Couldn't match type ‘USD’ with ‘EUR’
    +      Expected type: Money EUR
    +        Actual type: Money USD
    +    • In the second argument of ‘convert’, namely ‘dollar’
    +      In the expression: convert eurToUsd dollar
    +      In an equation for ‘it’: it = convert eurToUsd dollar
    +*Examples.Phantom> convert (invert eurToUsd) dollar
    +Money 0.819672131147541
    +*Examples.Phantom> convertUnsafe eurToUsd dollar
    +Money 1.22

    Note! The words currency, from, to and so forth in the previous examples are just type variables. There’s nothing special going on with them. We could’ve as well given invert a type like Rate a b -> Rate b a without any change in type safety.

    This approach that uses phantom types has clear benefits: giving us type errors for invalid code. Also, compared to defining lots of concrete types like data MoneyEur = MoneyEur Double, with phantom types we need to implement functions like scaleMoney and addMoney only once. Also, we’re able to define polymorphic and reusable concepts like Rate. You can contrast this approach with the Boxing section of Lecture 7.

    However, phantom types also have downsides. Without advanced tricks we can’t really handle currencies that are defined at runtime (for example: reading an amount from a user). It’s also easy to end up in a place where you start needing language extensions like Generalized Algebraic Datatypes, type families and other type-level programming constructs. Eventually you’re all the way in the world of dependent typing.

    So what are good applications for phantom types? When you need to track some simple, but crucial information, that is known at compile-time. An example that’s somewhat better than currencies is tracking whether inputs from the user have been sanitated to prevent attacks like SQL injection or cross-site scripting.

    We can use the types Input Safe and Input Unsafe to track whether strings are safe for passing into the database or not. If our module only exports the makeInput function, and not the Input constructor, the type system ensures that any inputs must pass through the escapeInput function at some point before going into a database function like addForumComment.

    -
    data Safe
    -data Unsafe
    -
    -data Input a = Input String
    -
    --- Public constructor function for Input, only allows constructing
    --- Unsafe Inputs from Strings.
    -makeInput :: String -> Input Unsafe
    -makeInput xs = Input xs
    -
    --- Adds comment to the database.
    -addForumComment :: Input Safe -> IO Result
    -addForumComment = ...
    -
    --- We can combine inputs, but that won't change their safety
    -concatInputs :: Input a -> Input a -> Input a
    -concatInputs (Input xs) (Input ys) = Input (xs++ys)
    -
    --- Strip bad characters to turn an unsafe input safe
    -escapeInput :: Input Unsafe -> Input Safe
    -escapeInput (Input xs) = Input (filter (\c -> isAlpha c || isSpace c) xs)
    +
    data Safe
    +data Unsafe
    +
    +data Input a = Input String
    +
    +-- Public constructor function for Input, only allows constructing
    +-- Unsafe Inputs from Strings.
    +makeInput :: String -> Input Unsafe
    +makeInput xs = Input xs
    +
    +-- Adds comment to the database.
    +addForumComment :: Input Safe -> IO Result
    +addForumComment = ...
    +
    +-- We can combine inputs, but that won't change their safety
    +concatInputs :: Input a -> Input a -> Input a
    +concatInputs (Input xs) (Input ys) = Input (xs++ys)
    +
    +-- Strip bad characters to turn an unsafe input safe
    +escapeInput :: Input Unsafe -> Input Safe
    +escapeInput (Input xs) = Input (filter (\c -> isAlpha c || isSpace c) xs)

    16.3 Simultaneity

    @@ -4794,62 +4795,62 @@ TODO explain `some` and `many`

    One of the great things about purity is that it makes parallelism, computing many things at the same time, very easy. Let’s have a look at how we can do this in Haskell. First off, we need to start a new GHCi that has parallel execution enabled. The simplest way is:

    $ stack ghci --ghci-options "+RTS -N"

    Next up, let’s define a very naive version of the Fibonacci function (remember Lecture 1?) , enable performance statistics with :set +s, and see how long it takes to compute five values of the function:

    -
    Prelude> fib 0 = 1; fib 1 = 1; fib n = fib (n-1) + fib (n-2)
    -Prelude> :set +s
    -Prelude> map fib [29,29,29,29,29]
    -[832040,832040,832040,832040,832040]
    -(7.54 secs, 2,440,860,632 bytes)
    +
    Prelude> fib 0 = 1; fib 1 = 1; fib n = fib (n-1) + fib (n-2)
    +Prelude> :set +s
    +Prelude> map fib [29,29,29,29,29]
    +[832040,832040,832040,832040,832040]
    +(7.54 secs, 2,440,860,632 bytes)

    Now let’s bring in the module Control.Parallel.Strategies that defines ways to evaluate values in parallel. We’ll use the parList rseq strategy to evaluate all the elements of the list to WHNF, in parallel.

    -
    Prelude> import Control.Parallel.Strategies
    -Prelude Control.Parallel.Strategies> withStrategy (parList rseq) (map fib [29,29,29,29,29])
    -[832040,832040,832040,832040,832040]
    -(4.80 secs, 488,531,384 bytes)
    +
    Prelude> import Control.Parallel.Strategies
    +Prelude Control.Parallel.Strategies> withStrategy (parList rseq) (map fib [29,29,29,29,29])
    +[832040,832040,832040,832040,832040]
    +(4.80 secs, 488,531,384 bytes)

    That’s almost 2 times as fast, on the 2-core machine that this example is being run on. Pretty nice. The coolest thing here is that we were able to define the computation (map fib ...) completely separately from the evaluation strategy (parList rseq), separating the what to compute from the how to compute.

    16.3.2 Concurrency

    Computer science makes the distinction between parallel and concurrent computations. Parallel computations are those that just run separate independent computations in parallel (in other words, parallelism is pure). Concurrent computations are those where there are multiple interacting threads of computation. Concurrency usually involves threads, locks, messages and deadlocks.

    In addition to great tooling for parallelism, Haskell also has good tooling for concurrency via threads. Since concurrency is all about side-effects, concurrent computations happen in the IO Monad. The classic example of threading is two threads, one printing a stream of As and the other a stream of Bs. Here it is in Haskell:

    -
    printA :: IO ()
    -printA = putStrLn (replicate 40 'A')
    -
    -printB :: IO ()
    -printB = putStrLn (replicate 40 'B')
    -
    -concurrency :: IO ()
    -concurrency = do
    -  forkIO printA
    -  forkIO printB
    -  return ()
    +
    printA :: IO ()
    +printA = putStrLn (replicate 40 'A')
    +
    +printB :: IO ()
    +printB = putStrLn (replicate 40 'B')
    +
    +concurrency :: IO ()
    +concurrency = do
    +  forkIO printA
    +  forkIO printB
    +  return ()
    Prelude Control.Concurrent> concurrency
     AABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABABB

    The operation forkIO :: IO () -> IO ThreadId takes an IO operation and starts running it in the backgroud. It produces a ThreadId that can be used to e.g. terminate the thread.

    If we want to add actual communication between threads, we can use abstractions like MVar (a mutable thread-safe variable) or Chan (a queue).

    Here’s a simple example where one thread writes a value to an MVar and another one waits for them and prints them. An MVar works like a mailbox: it is either empty or full. Calling takeMVar on an empty box waits for the box to get filled (with a putMVar). Symmetrically, trying to putMVar into a full box waits until the box is empty.

    -
    takeMVar :: MVar a -> IO a
    -putMVar :: MVar a -> a -> IO ()
    -newEmptyMVar :: IO (MVar a)
    -
    send :: [String] -> MVar String -> IO ()
    -send values var = mapM_ (putMVar var) values
    -
    -receive :: MVar String -> IO ()
    -receive var = do val <- takeMVar var
    -                 print val
    -                 -- loop unless at last value
    -                 when (val/="end") (receive var)
    -
    -concurrency2 :: IO ()
    -concurrency2 = do
    -  var <- newEmptyMVar
    -  forkIO (send ["hello","world","and","goodbye","end"] var)
    -  forkIO (receive var)
    -  return ()
    -
    Prelude Control.Concurrent Control.Monad> concurrency2
    -"hello"
    -"world"
    -"and"
    -"goodbye"
    -"end"
    +
    takeMVar :: MVar a -> IO a
    +putMVar :: MVar a -> a -> IO ()
    +newEmptyMVar :: IO (MVar a)
    +
    send :: [String] -> MVar String -> IO ()
    +send values var = mapM_ (putMVar var) values
    +
    +receive :: MVar String -> IO ()
    +receive var = do val <- takeMVar var
    +                 print val
    +                 -- loop unless at last value
    +                 when (val/="end") (receive var)
    +
    +concurrency2 :: IO ()
    +concurrency2 = do
    +  var <- newEmptyMVar
    +  forkIO (send ["hello","world","and","goodbye","end"] var)
    +  forkIO (receive var)
    +  return ()
    +
    Prelude Control.Concurrent Control.Monad> concurrency2
    +"hello"
    +"world"
    +"and"
    +"goodbye"
    +"end"