Please Buy now at Leanpub

Written and illustrated by GetContented.

Published on 2024-05-24.

Previous chapter: 16. Fridge, The Game

17. The People Book

... 17.1. Models of Data

... 17.2. More on Data Types

... 17.3. Making Our Types Show

... 17.4. Building Our First Value

... 17.5. Records

... 17.6. Finding a Person from the List

... 17.7. Filtering out People in a List

... 17.8. A Note About List Efficiency

... 17.9. Higher Order Functions: filter

... 17.10. Some Eta Reduction

... 17.11. Using filter

... 17.12. Higher Order Functions: map

... 17.13. Higher Order Functions: sortBy

... 17.14. Removing Parentheses With The ($) Function

... 17.15. Using minimumBy

... 17.16. Homework

Next chapter: 18. Times-Table Train of Terror

In this chapter, we’ll see some code for working with our own super-simple address book, and in the process introduce an extremely useful variety of functions called **higher order functions**. We’ll also dig a bit deeper into **sum** and **product** types (or **algebraic data types** as they're generally known as together), and introduce **records**, another way to work with data within such data types.

Imagine you were building up a list of your favourite people from the subject areas of maths and computing. (I know, what a silly suggestion!) So, what kind of information would you want to write down about them? Let’s just pick a couple of things about them to keep track of.

A data model is a set of data types or data that describes something. We’ve actually been doing data-modelling the whole book. When we say that we’re going to use an `Integer`

value to represent someone’s age, for example, we’re doing **data-modelling**. In our case right now, we’re about to be modelling people. So, what data would make up a person?

Well, because a person can have many pieces of information about them (or we could call them fields or attributes), we need a way to build a single type out of a combination of other types. To do this in Haskell, we use a **product type**, as we’ve briefly seen before. Here’s an example of one of these **product** types:

```
type Name = String
type Year = Int
data Person = Person Name Name Year
deriving (Show)
```

You’re probably wondering about some things here. What is “deriving”? Why is `data`

being used without the `|`

symbol, and why is `Name`

written twice? We’ll go through this now.

Firstly, we can see that we have a type alias for `Name`

as `String`

. We also have one for `Year`

as `Int`

. Great, there’s nothing new there. We know that just says these different type names can be used for those types, and Haskell will know what we mean. As you know, these are called type aliases or type synonyms.

What about `data Person`

though? Well, this is how we define `Person`

to be an **algebraic data type**, as we mentioned above. The `data`

keyword tells Haskell we’re creating our own fresh new type. We’ve seen these before, but let’s go through it in more detail to understand it better.

The part to the left of the `=`

symbol is the **type name**. Once our type is created, this name can be used in places where types can go, for example in type annotations. This name is `Person`

in the example above. If we’d written `data Muppet = HappyMuppet Name`

instead, then `Muppet`

would be the type name instead.

Next we’ll look to the right of the `=`

symbol. We see `Person`

again. This is a **value constructor**. When we create a type like this, Haskell creates us a function (or just a value if there are no type fields) for each of the values the type describes. In our case, we just have the one, which is `Person`

. Looking to the right of this, we can see the types that make up this `Person`

value, and the value constructor.

If we’d created the more complicated data type of `data SizedPerson = TallPerson Name | ShortPerson Name`

, then we would have **two** value constructors: `TallPerson :: String -> SizedPerson`

and `ShortPerson :: String -> SizedPerson`

, just to show you how it looks when there are sum **and** product types in the one algebraic data type.

Anyway, it’s called a product type because a single piece of this type of data is a **product** of more than one piece of data of these types. These pieces of data are often called **fields** of the data type. Again, if we’d written `data Muppet = HappyMuppet Name`

, then `HappyMuppet`

would be the **value constructor** (also sometimes called the **data constructor**).

So, after we’ve defined this type, Haskell will have defined a new value constructor function for us automatically `Person :: Name -> Name -> Year -> Person`

. Notice that the return type of this function is `Person`

as well as the function being named `Person`

. If we go back to our muppet example as a contrasting example, we can see that the type of the data constructor would be this instead: `HappyMuppet :: Name -> Muppet`

.

There is also this `deriving (Show)`

line after `Person`

. That is for us to tell Haskell that we’d like it to create an easy to print version of this data type for us, automatically creating an instance of the `Show`

typeclass for this data type. When we use `show`

on it, it will just print it out like it’s written in the code.

Back to `Person`

, though, those `Name`

fields... why **two** names? Is it first and last names? If so, which is which? Let’s see an entry for a person. Maybe that will clear it up:

```
-- the famous mathematician
-- Blaise Pascal
blaise :: Person
blaise = Person "Blaise" "Pascal" 1623
```

Ah, so the given name comes first, and the family name goes second. But, what is `Year`

supposed to represent here? Is it the year of birth? of death? of something else?

Don’t you wish there was some way we could see exactly what the types were supposed to mean **inside** of the **product type** itself? Well, it turns out there **is**. It’s what’s called **record syntax**, which is simply a way to let us name the fields as we specify a data type. Here’s what the `Person`

type looks like using **record syntax**:

```
type Name = String
type Year = Int
data Person = Person
{ personFirstName :: Name
, personLastName :: Name
, yearOfBirth :: Year }
deriving (Show)
```

Okay, there are names for the fields now, so it’s clearer what each means. Also, Haskell automatically makes what is called a **getter** function for the fields, and it also allows us to use something we’ll see called **record update syntax** for making new data based on existing data. So, `personFirstName`

is a function of type `Person -> Name`

, which means we pass it a `Person`

and it gives us back the first name, and so on for the other fields. How about constructing a `Person`

now? What’s that like? Well, it can work like this:

```
blaise :: Person
blaise =
Person { personFirstName = "Blaise"
, personLastName = "Pascal"
, yearOfBirth = 1623 }
```

However, we can still build a `Person`

the usual way, and all the old things work as before.

Something new, though, is that we can also easily build new records out of others by doing the following:

```
traise :: Person
traise = blaise { personFirstName = "Traise" }
```

This is called **record update syntax**. This one creates a person whose data looks like this: `Person {personFirstName = "Traise", personLastName = "Pascal", yearOfBirth = 1623}`

. Note that we can now “set” and “get” the data for fields in any order we like. We can set more than one field at once, too.

Let’s look at some more people:

```
people :: [Person]
people =
[ Person "Isaac" "Newton" 1643
, Person "Leonard" "Euler" 1707
, Person "Blaise" "Pascal" 1623
, Person "Ada" "Lovelace" 1815
, Person "Alan" "Turing" 1912
, Person "Haskell" "Curry" 1900
, Person "John" "von Neumann" 1903
, Person "Lipot" "Fejer" 1880
, Person "Grace" "Hopper" 1906
, Person "Anita" "Borg" 1949
, Person "Karen" "Sparck Jones" 1935
, Person "Henriette" "Avram" 1919 ]
```

Let’s see how we’d find a particular person, say, the first person whose birthday is after 1900 in the list. First, we need to make sure we’ve imported the `Data.List`

module, because we’ll be using the `find`

function from that module. At the top of our code file, we make sure the import is present:

```
import qualified Data.List as L
```

Then, we can find our person with this definition of an expression:

```
firstAfter1900 :: Maybe Person
firstAfter1900 =
L.find (\(Person _ _ year) -> year >= 1900) people
```

The `find`

function has the following type signature `L.find :: Foldable t => (a -> Bool) -> t a -> Maybe a`

. This means it takes two arguments: a function from some type of value called `a`

to a `Bool`

known as the predicate, and a `Foldable`

of that same `a`

type. Our `Foldable`

instance will be on list, because we have `people :: [Person]`

as our `Foldable t => t a`

value.

Essentially what the function does is apply the predicate to each of the items until one returns `True`

in which case it returns that particular item wrapped in `Just`

, otherwise it returns `Nothing`

. It’s not the most efficient way to find things because of the way lists are constructed, but it will be fine for our purposes here.

Also to note here is the way we're using the `Person`

constructor to pattern-match out the parts of the `Person`

as it gets fed into the `find`

function, with our predicate: `(Person _ _ year) -> year >= 1900)`

. The two underscores simply throw those particular fields away because we’re not interested in them, and we just match out the year as the variable `year`

which we then compare to `1900`

.

Instead of doing it like that, we could also have written it like this, which is a bit more flexible because it doesn't depend on the field ordering in the data type:

```
firstAfter1900' :: Maybe Person
firstAfter1900' =
L.find (\person -> yearOfBirth person >= 1900) people
```

Let’s see how we’d find the sub-list of people whose name begins with L, using recursion and the list above:

```
firstNameBeginsWithL :: Person -> Bool
firstNameBeginsWithL p =
case personFirstName p of
'L':_ -> True
_ -> False
makeNewListWithOnlyLPeople :: [Person] -> [Person]
makeNewListWithOnlyLPeople [] = []
makeNewListWithOnlyLPeople (x:xs)
| firstNameBeginsWithL x =
x : makeNewListWithOnlyLPeople xs
| otherwise =
makeNewListWithOnlyLPeople xs
peopleThatBeginWithL =
makeNewListWithOnlyLPeople people
```

The `firstNameBeginsWithL`

function takes a `Person`

as the variable `p`

, gets the first name with the `personFirstName`

getter function, then we have a `case`

expression on that.

If the first name is a `String`

beginning with the letter `L`

, it will match `'L':_`

because `(:)`

is, as we know, a value constructor that matches any list and pattern-match splits it into its head and tail. This returns `True`

, otherwise the “`_`

” pattern will pattern-match on anything else, which means we return `False`

.

Next we’ll look at the `makeNewListWithOnlyLPeople`

function, which is a reasonably simple recursive function using **guard patterns**. Remember that **guard patterns** work by Haskell matching the first expression that evaluates to `True`

, then returning the expression on the right of the corresponding `=`

symbol. We pull the `Person`

list into head and tail as `x`

and `xs`

respectively using pattern-matching again. If the `Person`

in `x`

has a first name that begins with `L`

, we add it to the return list by using `(:)`

to prepend it to the tail of the list (`xs`

) with the function applied to it. If it doesn’t begin with `L`

, we simply apply the function to the tail of the list.

You might notice that `makeNewListWithOnlyLPeople`

is **using** the `firstNameBeginsWithL`

function as a kind of testing function. This type of function is called a **predicate** function in programming. It checks if something is true or not. What if we wanted to be able to swap out that function, and make a whole lot of different lists with people whose names started with letters other than `L`

? Well, next we’ll look at a general way to do just this.

You might be thinking that this way of finding something is very inefficient. You’d be correct if you were thinking this! For small amounts of data, the list type is very handy and useful, and more than efficient enough. However, for much larger amounts of data, we would want to use different functions and types if we wanted things to be fast and efficient. We’ll see these in later volumes. As with everything, the context gives meaning to the content, so as the content changes (you get a bigger set of data), we must choose different ways of working with it (choose a different context of functions and data types).

Lists are very good for certain things, such as for representing data that is added to at the front. They are also quite easy to write code for, so they’re good for beginners to look at first, such as yourself. As you learn programming more you’ll start to get an appreciation and understanding of the different types for storing data, and when it’s good to use each.

What we just saw is a very common pattern in Haskell: we take a testing function that returns a `Bool`

value (otherwise known as a **predicate**) and a list of the same type of items that the **predicate** takes, and we return a new list of items that resulted in `True`

when “tested” against the predicate. It’s so common that there’s already a built-in higher-order function for it in Haskell called `filter`

:

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

A **higher-order function** is a function that takes one or more functions as argument(s). This term also applies to functions that return functions, but because of the way functions work in Haskell, that’s so common and easy that we usually don’t count it.

Let’s see how rewriting our “only L people” function using `filter`

simplifies it:

```
makeNewListWithOnlyLPeople' :: [Person] -> [Person]
makeNewListWithOnlyLPeople' xs =
filter firstNameBeginsWithL xs
```

Here, `xs`

is matched to the list of type `Person`

, and we pass it to `filter`

, along with our predicate (`firstNameBeginsWithL`

). This version does exactly what our previous function does, but with a lot less code to read and write. Using these built in common function like `filter`

and the others we’ll see later is really useful because it saves us having to reinvent the wheel each time we want such common functionality. It also gives us a common language to talk about these things with other Haskell programmers.

We can further simplify the definition by removing the `xs`

from both sides of the equals sign!

```
makeNewListWithOnlyLPeople'' :: [Person] -> [Person]
makeNewListWithOnlyLPeople'' =
filter firstNameBeginsWithL
```

That is, `filter`

usually takes two arguments: a function of type `(a -> Bool)`

(any type at all to `Bool`

), and a value of type `[a]`

(a list of that same type), then returns a value of type `[a]`

(another list of that same type). If we were to supply it with both arguments, it would return us value of type `[a]`

, but if we only supply the first one (the predicate from `a -> Bool`

), then we’ll end up with a function from `[a]`

to `[a]`

!

The technical name for this process of getting rid of these variables that are repeated on the right hand side of the inside and outside is called **eta reduction**.

Here’s another example of it:

```
plus num1 num2 = num1 + num2
-- the second argument "num2" gets "erased":
plus' num1 = (num1 +)
-- this is the same as:
plus'NonSectioned = \num1 -> (+) num1
plus'' = (+)
```

Note that `(num1 +)`

is technically actually what's called a **section**: that is, a partially applied operator. However, because we're showing this eta reduction with the `(+)`

operator, `plus' num1 = (num1 +)`

is effectively the same function as `plus' num1 = (+) num1`

.

All three of these functions work in the same way. The `(+)`

function already takes two arguments, which as we know in Haskell means it is actually two nested functions. Let’s look at yet another way to do the same thing, this time with lambdas:

```
add = \x -> (\y -> x + y)
-- the second argument, "y" gets "erased":
add' = \x -> (x +)
-- this is the same as:
add'NonSectioned = \x -> (+) x
add'' = (+)
```

In each step, we’re simply removing one of the unnecessary variables from our function definition, because `(+)`

is **already** a function itself, so by the end, all we’re doing is effectively saying that `add''`

is simply the `(+)`

function.

Getting back to our original functionality, let’s look at a different way to write the whole function:

```
-- don't get confused, c is not the letter c here
-- it's a variable name, holding the Char value
-- we're matching on
firstLetterIs :: Char -> String -> Bool
firstLetterIs c "" = False
firstLetterIs c (x:_) = c == x
firstNameBeginsWith :: Char -> Person -> Bool
firstNameBeginsWith c p =
firstLetterIs c firstName
where firstName = personFirstName p
peopleThatBeginWithL :: [Person]
peopleThatBeginWithL =
filter (firstNameBeginsWith 'L') people
```

We have `firstLetterIs`

, a more general function that takes a `Char`

and a `String`

and returns `True`

if the first letter of the `String`

is the passed in `Char`

value. The beauty of this function is if we decide we want to use a different letter, we just have to change the one spot in the code.

Then there’s the `firstNameBeginsWith`

function that gets the first name of the passed in `Person`

and matches its first letter against a passed in `Char`

value by using the `firstLetterIs`

function.

Finally, we use `filter`

along with the partially applied function `firstNameBeginsWith 'L'`

and the people list to create a `Person`

list value defined as `peopleThatBeginWithL`

.

It might be pretty clear to you now how we can easily build a list by filtering on the first name beginning with any character we like, and it should be reasonably easy to see how you could create a list of people whose last names start with a different letter. (For example, `filter (firstNameBeginsWith 'H') people`

).

Now we’re going to take a look at something we’ll need later on. We may want to get the last name of a `Person`

. We know how to do this for one `Person`

just fine. Let’s say the person is `blaise`

, then we’d write `personLastName blaise`

. That’s pretty straightforward.

Well, what if we actually wanted to get a whole **list** of first names from a whole list of people? What would we use? Well, given what we know about recursion, we’d probably write it something like this:

```
peopleToLastNames :: [Person] -> [String]
peopleToLastNames [] = []
peopleToLastNames (x:xs) =
personLastName x : peopleToLastNames xs
```

Study this little function well! What we’re seeing is a function with two definitions, as usual. It’s looking like some standard recursion. The first definition simply says if the `[Person]`

passed in is the empty list of `Person`

, return the empty list of `String`

.

The second definition is where most of the work takes place. This first pattern matches the head of the list into `x`

and the tail into `xs`

. So, `x`

will be a `Person`

, and `xs`

will be a `[Person]`

. It then returns applying `personLastName`

to `x`

which gives us a `String`

, then prepends this using `(:)`

to the result of calling the whole function again on the tail of the list (recursively).

Next we can imagine what it’d be like if we wanted a kind of general function so we weren’t locked in to **only** using the `personLastName`

function. Let’s see what an equivalent first name function would be like, first:

```
peopleToFirstNames :: [Person] -> [String]
peopleToFirstNames [] = []
peopleToFirstNames (x:xs) =
personFirstName x : peopleToFirstNames xs
```

Not much changes, does it? We’ve really only changed the function that gets called on each `Person`

value to turn it into a `String`

value. What if we made a general function and let the programmer using the function pass in their own function of type `Person -> String`

, that way we could make this quite general:

```
mapPeople :: (Person -> String) -> [Person] -> [String]
mapPeople f [] = []
mapPeople f (x:xs) =
f x : mapPeople f xs
```

Ok, notice we’ve added another function argument at the front — it’s `f`

, which is the function of type `Person -> String`

we’re passing in — and all our function definitions now have an added `f`

parameter and variable. Also notice we’re using it by appling it to `x`

before using `(:)`

with our recursive function application of `mapPeople`

at the end of the last line which also has to have the `f`

argument, as it’s now a required argument to our function.

This is now quite general. We can use this with first names or last names. Let’s see the redefined version of these functions now:

```
peopleToLastNames :: [Person] -> [String]
peopleToLastNames people =
mapPeople personLastName people
peopleToFirstNames :: [Person] -> [String]
peopleToFirstNames people =
mapPeople personFirstName people
```

That’s starting to look **very** nice and compact. We now know, though, that we can **eta reduce** these functions by getting rid of the people argument, as follows:

```
peopleToLastNames :: [Person] -> [String]
peopleToLastNames = mapPeople personLastName
peopleToFirstNames :: [Person] -> [String]
peopleToFirstNames = mapPeople personFirstName
```

Nice. However, it’s time to let you in on a secret. The `mapPeople`

function already exists in Haskell, as an even **more** general function called `map`

. This one works on lists of **any** type of value at all.

Let’s see its type signature:

```
map :: (a -> b) -> [a] -> [b]
```

This takes two arguments: a function from anything `a`

to anything `b`

, a list of those `a`

values, and returns a list of those `b`

values. For us, this means we'd want these `a`

and `b`

values to be `Person`

and `String`

(we call this **specialisation**). Pay careful attention and note that where Haskell has written `a`

and `b`

in type signatures, that doesn’t mean that those types **have** to be different, only that they **can** be different, if you’d like.

To illusrated this, if you want to `map`

from `String`

to `String`

, or the same type to the same type, there’s nothing stopping you. For example, here’s a function that maps from a list of strings to their reverse string counterparts, using the `reverse`

function: `reverseMap = map reverse :: [String] -> [String]`

.

So, anyway, we could have just written our `mapPeople`

function like this:

```
mapPeople :: (Person -> String) -> [Person] -> [String]
mapPeople = map
```

Or, we could just have used `map`

instead of `mapPeople`

.

So, given we have a list of people called `people`

, we’d create a list of their last names like this:

```
lastNames :: [String]
lastNames = map personLastName people
```

Very nice and compact.

So, the higher order function `map`

takes a function and a list. It gives a new list with the function applied to each element of the list. Let’s see how `map`

could be implemented:

```
map' :: (a -> b) -> [a] -> [b]
map' f [] = []
map' f (x:xs) = f x : map' f xs
```

As you can see, it’s very similar to the function we had for getting the last names of people.

Here’s a picture that might help you understand what map does visually:

You get a whole new list with new items that have been created by applying the function to the original elements. The original list and items are still there, unchanged.

This is important: Haskell values are almost never modified, they’re always created afresh, or referred to if they’re identical.

You might be tempted to think of it a bit like the function is “doing something” to each element, especially with these higher order functions, but it’s not, it’s looking at the original element and using the passed-in mapping function to make an entirely new element for the new list, leaving the original untouched. This is very good, because if two functions are referring to one value, the last thing you want is for that value to be changed by one of the functions without the other one realising it. Luckily, this can’t happen in Haskell. This is because Haskell has **purity**, which means functions cannot work on things other than their arguments, and also because it has **immutability** which is a fancy word meaning data can’t be changed! You might worry that this would make it inefficient, but that’s not the case. In many cases it actually makes it **more** efficient.

Ok, so now we have our last names in our `lastNames`

variable, perhaps we might want to sort them. First, we need to make sure we’ve imported the `Data.List`

module, because the sorting functions are in that module. At the top of our code file, we make sure the following is present:

```
import qualified Data.List as L
```

Before we get to these functions for sorting, we need to know a little about the `Ord`

**typeclass**. This is for types that can be ordered. `Ord`

provides the functions `compare`

, `(<)`

, `(<=)`

, `(>)`

, `(>=)`

, `min`

and `max`

. These functions allow comparison between two values in various ways. You should investigate their types using **hoogle**. If you use a web search for “haskell hoogle” it will find it. It’s a very handy tool for looking at functions. There is also a **sum type** called `Ordering`

that this typeclass uses that has the values `LT`

, `GT`

, and `EQ`

, which represent less-than, greater-than and equal-to respectively. The `compare`

function returns this type, and sorting functions use the `compare`

function and the `Ordering`

type to do their work.

Right, so now we can have a definition for our sorted last names using the `sort`

function from the `Data.List`

module, whose type signature is `Ord a => [a] -> [a]`

. It takes a list whose element type must be instances of `Ord`

(for comparing and ordering, remember), and returns the sorted version of that list.

```
sortedLastNames :: [String]
sortedLastNames = L.sort lastNames
```

This will be sorted alphabetically. What if we wanted it in reverse? Well, there is a `reverse`

function in Haskell whose type is `[a] -> [a]`

that will reverse any list at all. Instead of using this, though, we’re going to see how to use a function called `sortBy`

that takes a function like `compare`

, instead.

Firstly, `sortBy`

has a type of `(a -> a -> Ordering) -> [a] -> [a]`

and `compare`

has type `Ord a => a -> a -> Ordering`

, so you’ll notice that the `sort`

function is effectively the same thing as `sortBy compare`

. To reverse the order, we can simply provide a function to `sortBy`

that returns the opposite `Ordering`

that compare would return, and we can do that by swapping the arguments to `compare`

:

```
reverseSortedLastNames :: [String]
reverseSortedLastNames =
L.sortBy (\x y -> compare y x) lastNames
```

This definition is more efficient than doing `reverse (sortBy compare lastNames)`

, because it only has to go through the list once. For our small data set, this is not going to be a problem. It would matter with a very large list, though. In that case, though, a list would probably not actually be the best data structure to use.

We can see there that we’ve used a lambda of two arguments to **flip** the order of `compare`

’s arguments. This has the intended result of a reverse-sorted list of the lastNames.

There’s an arguably better way to do this than use a lambda, though. Haskell has a commonly-used function called `flip`

that works with any function of two or more arguments. Pass it any 2-argument function, and it’ll return you a function that works exactly the same, but has its arguments swapped. So here’s an alternate way to write `reverseSortedLastNames`

:

```
reverseSortedLastNames' :: [String]
reverseSortedLastNames' =
L.sortBy reverseCompare lastNames
where reverseCompare = flip compare
```

At this point, how we get the list of `firstNames`

should appear as no surprise.

```
firstNames :: [String]
firstNames =
map personFirstName people
```

Ok, but what if we wanted to sort the people list itself by some field of each person? Well, `Data.List`

has a `sortOn`

function whose type is `Ord b => (a -> b) -> [a] -> [a]`

. It orders the new list by the result of some function `a -> b`

applied to each element.

Let’s say for our purposes we want to create a list of people sorted by their first names. The function `personFirstName`

fits the `a -> b`

type perfectly for our purposes, as its type is `Person -> String`

and we want to sort on the first name `String`

.

```
sortedPeopleByFirstName :: [Person]
sortedPeopleByFirstName =
L.sortOn personFirstName people
```

Now let’s see a function that takes a `year`

, and a `person`

, and works out how many years ago from that year that person was born.

```
yearsSinceBirthAtYear :: Year -> Person -> Int
yearsSinceBirthAtYear y p = y - yearOfBirth p
```

We map `y`

to the comparison year, and `p`

to the passed in person, then apply the `yearOfBirth`

function to the person and subtract that from the comparison year. If we wanted to get this across all the people, we could map it as a part-applied function, say for `2012`

:

```
allYearsSinceBirthAt2012 :: [Int]
allYearsSinceBirthAt2012 =
map (yearsSinceBirthAtYear 2012) people
```

The type of `yearsSinceBirthAtYear :: Year -> Person -> Int`

means if we apply one argument to it (`2012`

in this case), we’ll end up with a function `(Person -> Int)`

that is locked to compare with `2012`

, takes a single argument (a `Person`

) and replies with the number of years difference between `2012`

and that person’s birth year.

And now a function that shows the earliest year of birth for the people on our list. This uses the `minimum`

function which will work on lists containing instances of `Ord`

. Actually it’s a very general function, because it will work not only on list, but any instance of `Foldable`

. There’s also a `maximum`

function that gets the highest ordered value, too. Let’s see their type signatures before we proceed:

```
minimum :: (Ord a, Foldable t) => t a -> a
maximum :: (Ord a, Foldable t) => t a -> a
```

These functions have **two** typeclass constraints on them. This says that `t`

must be an instance of the `Foldable`

typeclass, but also that `a`

must be an instance of the `Ord`

typeclass. Note that you cannot pass an empty list into these functions. You must only pass a list that has at least one item in them.

Luckily for us, `Int`

has an instance for `Ord`

, and list has an instance for `Foldable`

, so `minimum`

will work perfectly for us:

```
earliestYearOfBirth :: [Person] -> Year
earliestYearOfBirth people =
minimum (L.map yearOfBirth people)
```

In Haskell, we prefer not to use so many parentheses. There is a higher order function called `($)`

that will take a function on the left of it, and some expression on the right, and apply the function to the expression. It has an extremely low precedence, which means it will pretty much be applied last of all. This is basically the same effect as having parentheses wrapped around the expression on the right. Let’s see how the function above can be written with the `($)`

function.

```
earliestYearOfBirth' :: [Person] -> Year
earliestYearOfBirth' people =
minimum $ L.map yearOfBirth people
```

Note that we can’t get rid of the `people`

variable, though, because the `minimum`

function is wrapping the whole expression `L.map yearOfBirth people`

.

Note also that the `($)`

function is only for the cases where the parentheses would go right to the very end of the expression on the right.

Last of all, we want to find out which `Person`

was born first out of our list of people.

```
bornFirst :: [Person] -> Person
bornFirst people =
L.minimumBy compareBirthYears people
where compareBirthYears x y =
compare (yearOfBirth x) (yearOfBirth y)
```

Lots to explain here!

`Data.List`

’s `minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a`

function is another higher-order function: that is, it takes a function which is a comparing function `(a -> a -> Ordering)`

. It also takes a `Foldable`

instance wrapping some “`a`

” values of any type, and gives us the minimum one by using the comparing function on successive pairs of items.

Notice that the “`a`

” type doesn’t have to be an instance of `Ord`

here. So long as the comparing function returns an `Ordering`

and its two arguments are the same type, `minimumBy`

will compile with no problem.

Here, we’re using a `where`

clause to locally **scope** the comparing function `compareBirthYears`

, which simply takes two people, matches them into `x`

and `y`

respectively, and returns the application of the `compare`

function on their `yearOfBirth`

fields.

Because `compare`

has the type `Ord a => a -> a -> Ordering`

, and the `yearOfBirth`

fields are `Integer`

and they are instance of `Ord`

, compare can do the comparison, and this means `compareBirthYears x y`

returns an `Ordering`

, which means it’s the correct type that `minimumBy`

requires.

These higher-order functions such as `map`

, `filter`

, `fold`

and now `minimumBy`

and `maximumBy`

might seem complicated at first, but with lots of practice in thinking through all the types of the functions concerned, they will become second nature to read, and then later, to write.

Your homework is to adjust the program by adding middle name (`middleName`

) as a field to a `Person`

, and adjusting all the functions and usage of functions as you go. Make up some middle names for these people. See if you notice that by using records, we’ve made it much easier to change our program. See if you can imagine how difficult it would be changing our program if all the functions were tied to the **shape** of our data type!

If you’ve enjoyed reading this, please consider purchasing a copy at Leanpub today

Please follow us, and check out our videos Follow @HappyLearnTutes

Also, *Volume 2* is now in beta and being written! Show your support and register your interest at its Leanpub site.

Main Table of Contents

Previous chapter: 16. Fridge, The Game

17. The People Book

... 17.1. Models of Data

... 17.2. More on Data Types

... 17.3. Making Our Types Show

... 17.4. Building Our First Value

... 17.5. Records

... 17.6. Finding a Person from the List

... 17.7. Filtering out People in a List

... 17.8. A Note About List Efficiency

... 17.9. Higher Order Functions: filter

... 17.10. Some Eta Reduction

... 17.11. Using filter

... 17.12. Higher Order Functions: map

... 17.13. Higher Order Functions: sortBy

... 17.14. Removing Parentheses With The ($) Function

... 17.15. Using minimumBy

... 17.16. Homework

Next chapter: 18. Times-Table Train of Terror