Haskell/Higher-order functions

At the heart of functional programming is the idea that functions are just like any other value. The power of functional style comes from handling functions themselves as regular values, i.e. by passing functions to other functions and returning them from functions. A function that takes another function (or several functions) as an argument is called a higher-order function. They can be found pretty much anywhere in a Haskell program, and indeed we have already met some of them, such as  and the various folds. We saw commonplace examples of higher-order functions when discussing  in Lists II. Now, we are going to explore some common ways of writing code that manipulates functions.

A sorting algorithm
For a concrete example, we will consider the task of sorting a list. Quicksort is a well-known recursive sorting algorithm. To apply its sorting strategy to a list, we first choose one element and then divide the rest of the list into (A) those elements that should go before the chosen element, (B) those elements equal to the chosen one, and (C) those that should go after. Then, we apply the same algorithm to the unsorted (A) and (C) lists. After enough recursive sorting, we concatenate everything back together and have a final sorted list. That strategy can be translated into a Haskell implementation in a very simple way.

It should be pointed out that our  is rather naïve. A more efficient implementation would avoid the three passes through  at each recursive step and not use   to build the sorted list. Furthermore, unlike our implementation, the original quicksort algorithm does the sorting in-place using mutability. We will ignore such concerns for now, as we are more interested in the usage patterns of sorting functions, rather than in exact implementation.

The class
Almost all the basic data types in Haskell are members of the  class, which is for ordering tests what   is for equality tests. The  class defines which ordering is the "natural" one for a given type. It provides a function called, with type:

takes two values and compares them, returning an  value, which is   if the first value is less than the second,   if it is equal and   if it is greater than. For an  type, ,   from   and   can be seen as shortcuts to   that check for one of the three possibilities and return a   to indicate whether the specified ordering is true according to the   specification for that type. Note that each of the tests we use with  in the definition of   corresponds to one of the possible results of , and so we might have written, for instance,   as.

Choosing how to compare
With, sorting any list with elements in the   class is easy. Suppose we have a list of  and we want to sort them; we just apply   to the list. For the rest of this chapter, we will use a pseudo-dictionary of just a few words (but dictionaries with thousands of words would work just as well):

returns:

As you can see, capitalization is considered for sorting by default. Haskell s are lists of Unicode characters. Unicode (and almost all other encodings of characters) specifies that the character code for capital letters are less than the lower case letters. So "Z" is less than "a".

To get a proper dictionary-like sorting, we need a case insensitive. To achieve that, we can take a hint from the discussion of  just above. The recursive case of  can be rewritten as:

While this version is less tidy than the original one, it makes it obvious that the ordering of the elements hinges entirely on the  function. That means we only need to replace   with an  function of our choice. Therefore, our updated  is a higher-order function which takes a comparison function along with the list to sort.

We can reuse our  function to serve many different purposes.

If we wanted a descending order, we could just reverse our original sorted list with. Yet to actually do the initial sort descending, we could supply  with a comparison function that returns the opposite of the usual.

Higher-Order Functions and Types
The concept of currying (the generating of intermediate functions on the way toward a final result) was first introduced in the earlier chapter "Lists II". This is a good place to revisit how currying works.

Our  has type.

Most of the time, the type of a higher-order function provides a guideline about how to use it. A straightforward way of reading the type signature would be " takes, as its first argument, a function that gives an ordering of two  s. Its second argument is a list of  s. Finally, it returns a new list of  s". This is enough to correctly guess that it uses the given ordering function to sort the list.

Note that the parentheses surrounding  are mandatory. They specify that  forms a single argument that happens to be a function.

Without the parentheses, we would get  which accepts four arguments (none of which are themselves functions) instead of the desired two, and that wouldn't work as desired.

Remember that the  operator is right-associative. Thus, our erroneous type signature  means the same thing as.

Given that  is right-associative, the explicitly grouped version of the correct   signature is actually. This makes perfect sense. Our original  lacking the adjustable comparison function argument was of type. It took a list and sorted it. Our new  is simply a function that generates   style functions! If we plug in  for the   part, then we just return our original simple   function. If we use a different comparison function for the argument, we generate a different variety of a  function.

Of course, if we not only give a comparison function as an argument but also feed in an actual list to sort, then the final result is not the new -style function; instead, it continues on and passes the list to the new function and returns the sorted list as our final result.

Function manipulation
We will close the chapter by discussing a few examples of common and useful general-purpose higher-order functions. Familiarity with these will greatly enhance your skill at both writing and reading Haskell code.

Flipping arguments
is a handy little Prelude function. It takes a function of two arguments and returns a version of the same function with the arguments swapped.

in use:

We could have used flip to write a point-free version of the  comparing function from the quickSort example:

is particularly useful when we want to pass a function with two arguments of different types to another function and the arguments are in the wrong order with respect to the signature of the higher-order function.

Composition
The  composition operator is another higher-order function. It has the signature:

takes two functions as arguments and returns a new function which applies the second function to the argument and then the first. (Writing the type of  as   can make that easier to see.)

Composition and higher-order functions provide a range of powerful tricks. For a tiny sample, first consider the function, defined in the module. Quoting the documentation, it "returns all initial segments of the argument, shortest first", so that:

We can provide a one-line implementation for  (written point-free for extra dramatic effect) using only the following higher-order functions from Prelude: ,  ,   and  :

Swallowing a definition so condensed may look daunting at first, so analyze it slowly, bit by bit, recalling what each function does and using the type signatures as a guide.

The definition of  is super concise and clean with use of parentheses kept to a bare minimum. Naturally, if one goes overboard with composition by writing mile-long  chains, things will get confusing; but, when deployed reasonably, these point-free styles shine. Furthermore, the implementation is quite "high level": we do not deal explicitly with details like pattern matching or recursion; the functions we deployed — both the higher-order ones and their functional arguments — take care of such plumbing.

Application
is a curious higher-order operator. Its type is:

It takes a function as its first argument, and all it does is to apply the function to the second argument, so that, for instance,.

You might think that  is completely useless! However, there are two interesting points about it. First,  has very low precedence, unlike regular function application which has the highest precedence. In effect, that means we can avoid confusing nesting of parentheses by breaking precedence with. We write a non-point-free version of  without adding new parentheses:

Furthermore, as  is just a function which happens to apply functions, and functions are just values, we can write intriguing expressions such as:

(Yes, that is a list of functions, and it is perfectly legal.)

and
As the name suggests,  is a function that undoes currying; that is, it converts a function of two arguments into a function that takes a pair as its only argument.

One interesting use of  occasionally seen in the wild is in combination with , so that the first element of a pair is applied to the second.

There is also, which is the opposite of.

Because most Haskell functions are already curried,  is nowhere near as common as.

and
Finally, we should mention two functions which, while not higher-order functions themselves, are most often used as arguments to higher-order functions. , the identity function, is a function with type that returns its argument unchanged.

Similar in spirit to,   is an  function that works like this:

takes two arguments, discards the second and returns the first. Seen as a function of one argument,, it returns a constant function, which always returns the same value no matter what argument it is given.

and  might appear worthless at first. However, when dealing with higher-order functions it is sometimes necessary to pass a dummy function, be it one that does nothing with its argument or one that always returns the same value. and  give us convenient dummy functions for such cases.