Thinking in Haskell

There are two aspects to the early stages of programming
effectively in Haskell. The first is coming to terms with the
shift in mindset from imperative programming to functional: we
have to step away from an entire toolbox of programming habits
from other languages. We do this not because these techniques
are bad, but because in a functional language they're either not
applicable or they turn out downright ungainly. At the same
time, we have to come up to speed with a replacement vocabulary,
so that we can hack productively.

Our second challenge is learning our way around the standard
Haskell libraries. As in any language, the libraries act as a
lever, enabling us to multiply our problem solving power.
However, Haskell libraries tend to be organised around a higher
level of abstraction than those in many other languages. We'll
probably have to put more effort into learning them, but in
exchange they offer a tantalisingly greater magnification of our
efforts.

In this chapter, we'll introduce a number of common
functional programming techniques. We'll draw upon examples
from imperative languages to highlight the shift in thinking
that we'll need to make. As we do so, we'll walk through some
of the fundamentals of Haskell's standard libraries. We'll also
intermittently cover a few more language features along the
way.

Warming up: portably splitting lines of text

Haskell provides a built-in function,
lines, that lets us split a text string on
line boundaries. It returns a list of strings with line
termination characters omitted.

While lines looks useful, it relies on
us reading a file in “text mode” in order to work
(yes, we'll be talking about opening files soon); it doesn't
deal well with Windows line ending conventions.

ghci> lines "a\r\nb"
["a\r","b"]
ghci> lines "a\r\nb"
["a\r","b"]

The function only splits on newline characters, leaving
carriage returns dangling at the ends of lines. Ugh. We can't
rely on opening a file in text mode to do the right thing on our
behalf. For example, if we're reading a Windows-generated text
file on a Linux or Unix box, we'll get trailing carriage returns
at the end of each line.

Years of comfortable hacking with Python's “universal
newline” support, which transparently handles Unix and
Windows line ending conventions for us, left us wanting
something similar in Haskell. Although Python conveniently
provides a built-in splitlines string
method, we'll rewrite it as a Python function, just to see what
a reasonable Python implementation might look like.

The key to understanding this code lies with Python's
partition string method: it searches for a
substring. If it finds it, it returns a triple of the substring
before the match, the match, and the substring after the match.
Otherwise, it returns a triple of the entire string and two
empty strings.

Because we're still early in our career of reading Haskell
code, we'll discuss our Haskell implementation in quite some
detail. This is almost the last instance where we'll walk
through “basic” Haskell code at this level.

Haskell provides a similar function named
break that we can use to the same end.
Unlike Python's partition, it operates over
lists of any type, instead of strings. It does this by taking a
function as its first parameter: this function takes an element
of the list, and returns a Bool to indicate whether
to break the list there or not. What break
returns is a two-tuple, of the sublist before the predicate
returns True (the prefix), and the rest of the list
(the suffix).

Our function first calls break on its
input string, to break it into the substring before a line
terminator, and the rest of the string (which will include the
line terminator, if any).

It then uses the pattern matching capabilities of
case to inspect the return value of
break. We know that
break always returns a two-tuple, so each
pattern on the left matches a two-tuple. The first element of
the tuple pattern doesn't inspect its value; it just binds the
variable pre to whatever value is in that
element of the tuple. The second element of the pattern
does inspect its value. The first pattern
matches any string containing at least two characters that
begins with a carriage return, followed by a newline, then binds
suf to the remainder of the string. The
other patterns should be self-explanatory.

The right-hand side of each of the first three branches of
the case expression constructs a new list, using
the prefix string pre as the head, and as the
remainder, the list of strings resulting from applying
splitLines to the suffix string
suf without the leading line separator
characters.

Following a prose description of the behaviour of a Haskell
function isn't easy. We can get a better understanding by
stepping into ghci, and watching the function at work in
different circumstances.

We'll start by looking at a string that doesn't contain any
line separators.

ghci> splitLines "foo"
["foo"]
ghci> splitLines "foo"
["foo"]

Here, our call to break never finds a
line separator, so we get an empty suffix.

Because the suffix begins with a carriage return, followed
by a newline, we match on the first branch of the
case expression. This gives us
pre bound to "foo", and
suf bound to "bar". We call
splitLines again, this time on
"bar" alone.

ghci> splitLines "bar"
["bar"]
ghci> splitLines "bar"
["bar"]

The result is that we construct a list whose head is
"foo" and whose tail is
["bar"].

Working with lists

As the bread and butter of functional programming, lists
deserve some serious attention. The standard prelude defines
dozens of functions for dealing with lists. Many of these will
be indispensable tools, so it's important that we learn them
early on.

The Data.List module is the “real”
logical home of all standard list functions. The prelude merely
re-exports a large subset of the functions exported by
Data.List. Several invaluable functions in
Data.List are not re-exported
by the standard prelude. As we walk through list functions in
the sections that follow, we'll explicitly mention those that
are in Data.List.

ghci> :module +Data.List
ghci> :module +Data.List

Because none of these functions is complex or takes more
than about three lines of Haskell to write, we'll be brief in
our descriptions of each. In fact, a quick and useful learning
exercise is to write a definition of each function after you've
read about it.

Basic list manipulation

The simplest function on a list is
null, which merely tells us whether or
not the list is empty.

Safely and sanely working with unsafe functions

When we want to use a function like
safe, where we know that it might blow up
on us if we pass in an empty list, the temptation might
initially be strong to check the length of the list before we
call safe. Let's construct a hideously
artificial example to illustrate our point.

If we're coming from a language like Perl or python, this
might seem like a perfectly natural way to write this test.
Behind the scenes, Python lists are arrays; and Perl arrays
are, well, arrays. So they necessarily know how long they
are, and calling len(foo) or $#foo+1
is a perfectly natural thing to do. But as with many other
things, it's not a good idea to blindly transplant such an
assumption into Haskell.

We've already seen the definition of the list algebraic
data type in the section called “A little more about lists”, and know
that a list doesn't encode its own length. Thus, the only way
that length can operate is to walk the
entire list.

Therefore, when we only care whether or not a list is
empty, calling length isn't a good
strategy. It can potentially do a lot more work than we want,
if the list we're working is finite. Worse, Haskell lets us
define infinitely long lists, on which an unsuspecting call to
length will never return!

A more appropriate function to call here instead is
null, which runs in constant time. Better
yet, using null makes our code indicate
what property of the list we really care about.

Partial and total functions

Functions that only have return values defined for a
subset of valid inputs are called partial
functions (calling error doesn't qualify
as returning a value!). We call functions that return valid
results over their entire input domains
total functions.

It's always a good idea to know whether a function you're
using is partial or total. Calling a partial function with an
input that it can't handle is probably the single biggest
source of straightforward, avoidable bugs in Haskell
programs.

Some Haskell programmers go so far as to give partial
functions names that begin with a prefix such as
unsafe, so that they can't shoot themselves in
the foot accidentally.

It's arguably a deficiciency of the standard prelude that
it defines quite a few “unsafe” partial
functions, like head, without also
providing “safe” total equivalents.

They have more useful cousins, all
and any, which operate on lists of any
type. Each one takes a predicate as its first argument;
all returns True if that
predicate succeeds on every element of the list, while
any returns True if the
predicate succeeds on any element of the list.

Working with sublists

The take function, which we already
met in the section called “Calling functions”, returns a
sublist consisting of the first several elements from a list.
Its converse, drop, drops several
elements from the head of the list.

The takeWhile and
dropWhile functions take predicates:
takeWhile constructs a list as long as
the predicate returns True, while
dropWhile drops elements from the list as
long as the predicate returns True.

In Data.List, three predicates,
isPrefixOf,
isInfixOf, and
isSuffixOf, let us test for the presence
of sublists within a bigger list. The easiest way to use them
is as infix functions, where they read quite naturally.

The isPrefixOf function tells us
whether its left argument matches the beginning of its right
argument.

Haskell's type system makes it an interesting challenge to
write functions that take variable numbers of arguments. So
if we want to zip three lists together, we call
zip3 or zipWith3,
and so on up to zip7 and
zipWith7.

How to think about loops

Unlike traditional languages, Haskell has neither a
for loop nor a while loop. If we've
got a lot of data to process, what do we use instead? There are
several possible answers to this question, so let's build up a
toolbox of answers.

Explicit recursion

A straightforward way to make the jump from a language
that has loops to one that doesn't is to run through a few
examples, looking at the differences. Here's a C function
that takes a string of decimal digits and turns them into an
integer.

The C code computes the result incrementally as it
traverses the string; the Haskell code can do the same.
However, in Haskell, we write the loop as a function, which
we'll call loop just to keep things nice
and explicit.

That first parameter to loop is the
accumulator variable we'll be using. Passing zero into it is
equivalent to initialising the acc variable
in C at the beginning of the loop.

Rather than leap into blazing code, let's think about the
data we have to work with. Our familiar String
is just a synonym for [Char], a list of
characters. The easiest way for us to get the traversal right
is to think about the structure of a list: it's either empty,
or a single element followed by the rest of the list.

We can express this structural thinking directly by
pattern matching on the list type's constructors. It's often
handy to think about the easy cases first: here, that means we
will consider the empty-list case.

loop acc [] = accloop acc [] = acc

An empty list doesn't just mean “the input string is
empty”; it's also the case we'll encounter when we
traverse all the way to the end of a non-empty list. So we
don't want to “error out” if we see an empty
list. Instead, we should do something sensible. Here, the
sensible thing is to return our accumulated value.

The other case we have to consider arises when the input
list is not empty. We need to do something with the current
element of the list, and something with the rest of the
list.

We compute a new value for the accumulator, and give it
the name acc'. We then call the
loop function again, passing it the
updated value acc' and the rest of the
input list; this is equivalent to the loop starting another
round in C.

Single quotes in variable names

Remember, a single quote is a legal character to use in
a Haskell variable name, and is pronounced
“prime”. There's a common idiom in Haskell
programs involving a variable, say foo,
and another variable, say foo'. We can
usually assume that foo' is somehow
related to foo. It's often a new value
for foo, as in our code above.

Sometimes we'll see this idiom extended, such as
foo''. Since keeping track of the number
of single quotes tacked onto the end of a name rapidly
becomes tedious, use of more than two in a row is thankfully
rare.

Each time the loop function calls
itself, it has a new value for the accumulator, and it
consumes one element of the input list. Eventually, it's
going to hit the end of the list, at which time the
[] pattern will match, and the recursive calls
will cease.

How well does this function work? For positive integers,
it's perfectly cromulent.

ghci> asInt "33"
33
ghci> asInt "33"
33

But because we were focusing on how to traverse lists, not
error handling, our poor function misbehaves if we try to feed
it nonsense.

Because the last thing that loop does
is simply call itself, it's an example of a tail recursive
function. There's another common idiom in this code, too.
Thinking about the structure of the list, and handling the
empty and non-empty cases separately, is a kind of approach
called structural recursion.

We call the non-recursive case (when the list is empty)
the base case. We'll see people refer to the case where the
function calls itself as the recursive case (surprise!), or
they might give a nod to mathematical induction and call it
the inductive case.

Structural induction isn't confined to lists; we can use
it on other algebraic data types, too. We'll have more to say
about it later.

Transforming every piece of input

Consider another C function, square,
which squares every element in an array.

Our square function consists of two
pattern matching equations. The first
“deconstructs” the beginning of a non-empty list,
to get its head and tail. It squares the first element, then
puts that on the front of a new list, which is constructed by
calling square on the remainder of the
empty list. The second equations ensures that
square halts when it reaches the end of
the input list.

The effect of square is to construct
a new list that's the same length as its input list, with
every element in the input list substituted with its square in
the output list.

Here's another such C loop, one that ensures that every
letter in a string is converted to uppercase.

Here, we're importing the toUpper
function from the standard Data.Char module,
which contains lots of useful functions for working with
Char data.

Our upperCase function follows a
similar pattern to our earlier square
function. It terminates with an empty list when the input
list is empty; and when the input isn't empty, it calls
toUpper on the first element, then
constructs a new list cell from that and the result of calling
itself on the rest of the input list.

These examples follow a common pattern for writing
recursive functions over lists in Haskell. The base
case handles the situation where our input list
is empty. The recursive case deals with
a non-empty list; it does something with the head of the list,
and calls itself recursively on the tail.

Mapping over a list

The square and
upperCase functions that we just defined
produce new lists that are the same lengths as their input
lists, and do only one piece of work per element. This is
such a common pattern that Haskell's prelude defines a
function, map, to make it easier.
map takes a function, and applies it to
every element of a list, returning a new list constructed from
the results of these applications.

The signature tells us that map takes
two arguments. The first is a function that takes a value of
one type, a, and returns a
value of another type, b. This
is the only unfamiliar piece of notation in the type; notice
the parentheses that surround the signature of the function
argument so we (and Haskell) won't misread it.

Since map takes a function as
argument, we refer to it as a
higher-order function. (In spite of the
name, there's nothing mysterious about higher-order functions;
it's just a term for functions that take other functions as
arguments, or return functions.)

Since map abstracts out the pattern
common to our square and
upperCase functions so that we can reuse
it with less boilerplate, we can look at what those functions
have in common and figure out how to implement it
ourselves.

This business of seeing that we're repeating an idiom,
then abstracting it so we can reuse (and write less!) code, is
a common aspect of Haskell programming.

Selecting pieces of input

Another common operation on a sequence of data is to comb
through it for elements that satisfy some criterion. Here's
an example in C++ of a function that walks a linked list of
numbers and returns those that are odd.

Our Haskell equivalent has a recursive case that's a bit
more complex than our earlier functions: it only puts a number
in the list it returns if the number is odd. Using a guard
expresses this nicely.

The filter function takes a predicate
(a function that tests an argument and returns a
Bool) and applies it to every element in its
input list, returning a list of only those for which the
predicate evaluates to True.

Our helper function is tail
recursive, and uses an accumulator parameter,
acc, to hold the current partial sum of the
list. As we already saw with asInt, this
is a “natural” way to represent a loop in a pure
functional language.

For something a little more complicated, let's take a look
at the Adler-32 checksum. Here's a Java implementation.

This isn't exactly easier to follow than the Java code,
but let's look at what's going on. Once again,
helper function is tail recursive. We've
turned the two variables we updated on every loop iteration in
Java into accumulator parameters. When our recursion
terminates on the end of the input list, we compute our
checksum and return it.

If we take a step back, we can restructure our Haskell
adler32 to more closely resemble our
earlier mySum function. Instead of two
accumulator parameters, we can use a single accumulator that's
a two-tuple.

Why would we want to make this seemingly meaningless
structural change? Because as we've already seen with
map and filter, we
can extract the common behaviour shared by
mySum and
adler32_try2 into a higher-order
function. We can describe this behaviour as “do
something to every element of a list, updating an
accumulator as we go, and returning the accumulator when
we're done”.

This kind of function is called a
fold, because it “folds up”
a list, and it has two variants, foldl
and foldr.

The foldl function takes a
“stepper” function, an initial value for its
accumulator, and a list. The “stepper” takes an
accumulator and an element from the list, and returns a new
accumulator value. All foldl does is call
the “stepper” on the current accumulator and an
element of the list, and passes the new accumulator value to
itself recursively to consume the rest of the list.

We refer to foldl as a “left
fold” because it consumes the list from left (the
head) to right.

Notice how much simpler this code is? We're no longer
using explicit recursion, because foldl
takes care of that for us. We've simplified our problem down
to two things: what the initial value of the accumulator
should be (the second parameter to
foldl), and how to update the accumulator
(the step function). As an added bonus,
our code is now shorter, too, which makes it easier to
understand.

We can rewrite adler32_try2 in a
similar way, using foldl to let us focus
on the details that are important.

Here, our accumulator is a two-tuple, so the result of
foldl will be, too. We pull the final
accumulator apart when foldl returns, and
bit-twiddle it into a “proper” checksum.

Why use folds, maps, and filters?

A quick glance reveals that
adler32_foldl isn't really any shorter
than adler32_try2. Why should we use a
fold in this case? The advantage here lies in the fact that
folds are extremely common in Haskell, and they have regular,
predictable behaviour.

This means that a reader with a little experience will
have an easier time understanding a function that uses a fold
than one that uses explicit recursion. Where a fold isn't
going to produce any surprises, the behaviour of a function
that recurses explicitly isn't immediately obvious. Explicit
recursion requires us to read closely to understand exactly
what's going on.

This line of reasoning applies to other higher-order
library functions, including those we've already seen,
map and filter.
Because they're library functions with well-defined behaviour,
we only need to learn what they do once, and we'll have an
advantage when we need to understand any code that uses
them.

Avoiding multiple traversals of a list

From looking at adler32_foldl, we
know that we can accumulate more than one value at a time when
we fold over a list. Here's another use for a fold:
optimising code by avoiding multiple traversals of a
list.

Let's consider the problem of finding the root mean square
of a list of numbers: compute the sum of the squares of every
element in the list, divide by its length, then coompute the
square root of that number. In an imperative language like C,
we wouldn't even think twice about writing code like
this.

This is a lovely, compact translation of the verbal
description. It even uses our new friend, the
map function, to make the code clearer by
avoiding explicit recursion, but it's not necessarily good
code. The calls to map and
length are each going to traverse the
input list once.

On a small list, the cost of traversing it twice obviously
won't matter, but on a big list, we're likely to notice. We
can use a fold to avoid this need to traverse the list
twice.

Clearly, this code isn't as readable as the earlier
version that used map and
length. Which version should we prefer?
It's often best to start out by writing the most readable
code, since we can make that correct most quickly, and put off
worrying about transforming it into something faster until
much later, when we have profiling data for our program. Only
if those numbers indicate a performance problem should we
worry about stepping back in and transforming our code. We'll
have much more to say about profiling, performance, and
optimisation later, in chapter XXX.

Folding from the right and primitive recursion

The counterpart to foldl is
foldr, which folds from the right of a
list.

At first glance, foldr might seem
less useful than foldl: what use is a
function that folds from the right? But consider the
Prelude's filter function, which we last
encountered in the section called “Selecting pieces of input”. If we write
filter using explicit recursion, it will
look something like this.

This is the sort of definition that could cause us a
headache, so let's examine it a little depth. Like
foldl, foldr takes a
function and a base case (what to do when the input list is
empty) as arguments. From reading the type of
filter, we know that our
myFilter function must return a list of
the same type as it consumes, so the base case should be a
list of this type, and the step helper
function must return a list.

Since we know that foldr calls
step on one element of the input list at
a time, with the accumulator as its second argument, what
step does must be quite simple. If the
predicate returns True, it pushes that
element onto the accumulated list; otherwise, it leaves the
list untouched.

The class of functions that we can express using
foldr is called primitive
recursive. A surprisingly large number of list
manipulation functions are primitive recursive. For example,
here's map written in terms of
foldr.

If you want to understand the definition of
foldl using foldr,
it's best to have the following tools at hand: some headache
pills, a glass of water, ghci (so you can find out what
the id function does), and a pencil and
paper.

While we can write foldl in terms of
foldr, we can't do the converse:
foldr is “more basic than”
foldl. This should make it clearer why
we call functions written with foldr
primitive recursive.

(By the way, don't feel like you have to go to special
lengths to remember the term “primitive
recursive”. It's just useful to remember that you
read about it somewhere, and that it has something to do with
foldr.)

Another useful way to think about the way
foldr works is that it
transforms its input list. Its first two
arguments are “what to do with each head/tail element of
the list”, and “what to substitute at the end
of the list”.

The “identity” transformation with
foldr thus replaces the empty list with
itself, and applies the list constructor to each head/tail
pair:

Now that we can think in terms of transforming a list, it
becomes easier for us to reason about functions like summing a
list. We replace the empty list with zero, which becomes our
first accumulator value. We then apply the
(+) function to each element of the list
and an accumulator value, to give a new accumulator
value.

ghci> foldr (+) 0 [1,2,3]
6
ghci> foldr (+) 0 [1,2,3]
6

Knowing that foldr transforms a list
from right to left, we can “unroll” the
application of it by hand to see the intermediate accumulators
that it produces.

As our extended treatment of folds should indicate, the
foldr function is nearly as important a
member of our list-programming toolbox as the more basic list
functions we saw in the section called “Working with lists”.

A final note about foldl

To keep our initial discussion simple, we used
foldl throughout most of this section.
However, any time you want to fold from the left in practice,
use foldl' from the
Data.List module instead, because it's more
efficient. You should take this on faith for now; we'll
explain why you should avoid plain foldl
in normal use in section XXX.

Use ghci to load the Data.List module
and figure out what groupBy does,
then write your own implementation using a fold.

6.

How many of the following standard prelude functions
can you rewrite using list folds?

Further reading

The article [Hutton99] is an excellent and
deep tutorial covering folds. It includes many examples of how
to use simple, systematic calculation techniques to turn
functions that use explicit recursion into folds.

Example: Run-Length Encoding

FIXME: Example: run-length encoding. Use to show how
looping can be done via tail recursion.

Type Inference

FIXME: Discuss type inference: what it is and how it can
save a lot of work.

Anonymous (lambda) functions

In many of the function definitions we've seen so far, we've
written short helper functions.

Haskell lets us write completely anonymous functions, which
we can use to avoid the need to give names to our helper
functions. Anonymous functions are often called
“lambda” functions, in a nod to their heritage in
the lambda calculus. We introduce an anonymous function with a
backslash character, \. This is followed by the
function's arguments (which can include patterns), then an arrow
-> to introduce the function's body.

Lambdas are most easily illustrated by example. Here's a
rewrite of isInAny using an anonymous
function.

We've wrapped the lambda in parentheses here so that Haskell
can tell where the function body ends.

Anonymous functions behave in every respect identically to
functions that have names, but Haskell places a few important on
how we can define them. Most importantly, whereas we can write
a normal function using multiple clauses containing different
patterns and guards, a lambda can only have a single clause in
its definition.

The limitation to a single clause restricts how we can use
patterns in the definition of a lambda. We'll usually write a
normal function with several clauses to cover different pattern
matching possibilities.

The definition typechecks, so it will compile, so the error
will occur at runtime. The moral of this story is to be careful
in how you use patterns when defining an anonymous function:
make sure your patterns can't fail!

Another thing to notice about the
isInAny and isInAny2
functions we showed above is that the first version, using a
helper function that has a name, is a little easier to read than
the version that plops an anonymous function into the middle.
The named helper function doesn't disrupt the
“flow” of the function in which it's used, and the
judiciously chosen name gives us a little bit of information
about what the function is expected to do.

In contrast, when we run across a lambda in the middle of a
function body, we have to switch gears and read its definition
fairly carefully to understand what it does. To help with
readability and maintainability, then, we tend to avoid lambdas
in many situations where we could use them to trim a few
characters from a function definition. Very often, we'll use
a partially applied function instead, resulting in clearer and
more readable code than either a lambda or an explicit
function. Don't know what a partially applied function is yet?
Read on!

Partial function application and currying

In Haskell, a single piece of syntax doesn't often get
pressed into use for multiple tasks. So why does the
-> arrow get used for what looks like two
purposes in the type signature of a function?

It looks like the -> is separating the
arguments to dropWhile from each other, but
also that it separates the arguments from the return type. But
in fact -> has only one meaning: it
denotes a function that takes an argument of the type on the
left, and returns a value of the type on the right.

The implication here is that in Haskell, all functions take
only one argument. While dropWhilelooks like a function that takes two
arguments, it only takes one. Here's a perfectly valid Haskell
expression.

ghci> :module +Data.Char
ghci> dropWhile isSpace
<interactive>:1:0:
No instance for (Show ([Char] -> [Char]))
arising from use of `print' at <interactive>:1:0-16
Possible fix:
add an instance declaration for (Show ([Char] -> [Char]))
In the expression: print it
In a 'do' expression: print it
ghci> :module +Data.Charghci> dropWhile isSpace
<interactive>:1:0:
No instance for (Show ([Char] -> [Char]))
arising from use of `print' at <interactive>:1:0-16
Possible fix:
add an instance declaration for (Show ([Char] -> [Char]))
In the expression: print it
In a 'do' expression: print it

Well, that looks useful. The value
dropWhile isSpace is a function that strips leading
white space from a string.

Every time we give an argument to a function, we can
“chop” an element off the front of its type
signature. Let's take zip3 as an example
to see what we mean; this is a function that zips three lists
into a list of three-tuples.

If we call zip3 with just one argument,
we get a function that accepts two arguments, where the first
argument is now fixed. No matter what
second or third arguments we pass, the first argument to this
new function will always be the fixed value we already
specified.

Partial function application lets us avoid writing tiresome
throwaway functions. It's generally a lot better for this
purpose than the anonymous functions we introduced in the section called “Anonymous (lambda) functions”. Looking back at the
isInAny function we defined there, here's
how we'd write it to use a partially applied function instead of
a named helper function or a lambda.

Here, the expression isInfixOf needle is the
partially applied function. We're taking the function
isInfixOf, and “fixing” its
first argument to be the needle variable from
our parameter list. This gives us a partially applied function
that has exactly the same type and behaviour as the helper and
lambda in our earlier definitions.

Sections

Haskell provides a handy notational shortcut to let us
write partially applied functions using infix operators. If
we enclose an operator in parentheses, we can supply its left
or right argument inside the parentheses to get a partially
applied function. This kind of partial application is called
a section.

As-patterns and function composition

Haskell's tails function, in the
Data.List module, generalises the
tail function we introduced earlier. It
successively applies tail to its input,
then calls itself on the result, until there's nothing
left.

Each of these strings is a suffix of
the initial string, so tails produces a
list of all suffixes, plus an extra empty list at the
end. In fact, it always produces that extra empty list, even
when its input list is empty.

ghci> tails []
[[]]
ghci> tails []
[[]]

What if we want a function that behaves like
tails, but which only
returns suffixes? One possibility would be for us to write our
own version by hand.

Where did that at-sign come from?

You may have noticed the funny-looking pattern
xs@(_:xs') in our definition of
suffixes. This is called an
as-pattern, and it means “bind the
variable xs to the expression in the
matched pattern (_:xs')”.

In this case, if the pattern after the “@”
matches, xs will be bound to the entire
list that matched, and xs' to the rest of
the list (we used the wildcard _ pattern to
indicate that we're not interested in the value of the head of
the list).

Let's look at a second definition of the
suffixes function, only this time without
using an as-pattern.

Here, the list that we've deconstructed in the pattern
match just gets put right back together in the body of the
function. The as-pattern makes the code more readable by
letting us avoid the need to repeat ourselves.

As-patterns are not just for readability

Not only can as-patterns reduce the clutter in our code;
they can help it to execute more efficiently, too. Since we
don't have much of a mental model for thinking about
evaluation and efficiency yet, we'll return to this topic
later, in XXX.

Code reuse

It seems a shame to introduce a new function,
suffixes, that does almost the same thing
as the existing tails function. Surely
we can do better?

We can create new functions at any time by writing chains
of composed functions, stitched together with
(.), so long (of course) as the result
type of the function on the right of each
(.) matches the type of parameter that
the function on the left can accept.

Here's an example drawn from a piece of code I wrote the
day before I started on this section. I wanted to get a list
of C preprocessor definitions from a header file shipped with
libpcap, a popular network packet filtering
library.

We take an entire file, split it up with
lines, then call foldr step
[] on the result. Since I know, based on the type of
lines, that I'm folding over a list of
strings, the step helper function must
thus operate on individual lines.

If we match a macro definition, we cons the name of the
macro onto the head of the list we're returning; otherwise, we
do nothing with the list on this invocation.

We can see heavy use of function composition in the body
of step. While all of these functions
are by now familiar to us, it can take a little practice to
glue together the sequence of types in a chain of compositions
like this. Let's walk through the procedure by hand.

In this case, we can reassure ourselves that we're safe
from a runtime failure in the call to
head. The pattern guard in the
definition of step ensures that after
we call words on any string that makes
it past the guard, we'll have a list of at least two
elements, "#define" and some macro beginning
with "DLT_". You can see this in some of the
ghci code snippets above.

This is an example of the kind of reasoning we ought to
do to convince ourselves that our code won't explode when we
call partial functions. Don't forget our earlier
admonition: calling unsafe functions like this requires
care, and can often make our code more fragile in subtle
ways.