

What is a higher-order function?
It is just a function that takes or returns another function.
A first-order function is one which operates on individuals (anything that is not a function). A second-order function operates on first-order functions, and so on. A higher-order function means anything that is not first-order, or, more generally, a function that will operate on functions of any order.
Curried functions are higher-order in the sense that they return functions as a result of their partial application. Here are a couple of examples of functions that take functions as parameters.
fun double f a = f (f a); fun quadruple f a = (f (f (f (f a))));
The first three H.O. functions I want to talk about to manipulate lists, the most popular data structure in functional programming. The functions are:
map
filter
fold (AKA reduce, or accumulate.)
Map takes a function (of type
'a -> 'b) and a list of items (of type
'a) and returns the list (of elements of type
'b) that results from replacing each element in the argument list
by the result of applying the argument function to that element.
Now, map is built in, but it is simple and instructive to write
ourselves.
fun map | map
So, suppose we want to add one to every element of a list, we could use map as follows:
map [3,7,9];If we want a function that will add one to every element of a given list we just write:
fun incList l =or, more succinctly, using partial application:
val incList =
The function filter takes a predicate (a function from
'a to bool) and a list of elements of type
'a and returns the list of those elements for which the
predicate returned true. Filter is written as:
fun filter | filter
So, if we want to remove all the 0s from a list we would write:
filter [2,0,3,4,0,5,6,7,0,9];and, as above, we could store this capability off in a function as in:
val removeZeroes =
The remove_vowels function from the first assignment can be coded as
a call to filter. Think about how you would do it.
The last of these functions, fold is the most
complicated. In fold the idea is to successively combine all the
elements of the list together to get a single answer. For instance,
you could use fold to add up the elements of a list. Let's look at
how you would use it, then we should be able to figure out how you'd
write it.
fun sumList l = fold (op +) 0 l;So, the first parameter is what you want to do to the successive values of the list; the second parameter is the value you want to use to get the ball rolling, and the third value is the list. To write it we say:
fun fold | foldIt is instructive to reason out its type:
To use it to multiply the elements of a list, you would say:
val multList =As it turns out,
fold is built in to SML
as foldr. A version that associates to the right is
called foldl.
Just to show some of the other things you can do, consider that you
could write map as a partial application of
fold. As in:
fun map f l =
As you start to program using higher-order techniques you quickly find that three more simple higher-order functions can help quite a bit. Those functions are
curry
uncurry
compose

curry converts a pairs function to take arguments
individually.
For instance, we have written the curried addition function as:
fun add x y = x + y;But this is just a case of a more general process. We can define curry as a function that will work on any pair-based function:
fun curry f =
Now with curry we can just say:
val add =In other words, we are just assigning the name
add to the
curried version of the integer plus operator.
Similarly, uncurry converts a curried function to a pairs
function:
fun uncurry f =Now, we can define:
val plus = uncurry add;
It should be easy to show that for any curried function f
curry (uncurry f) = fand for any pairs function f,
uncurry (curry f) = f

The function compose takes two functions and returns another function that is equivalent to the two of them combined.
For example, suppose you have a function
squares that will square all the items in a list, defined as:
val squares = mapand another function
sum that adds up the elements of a list, as in:
val sum =If we want to define a function
sum_squares you can't just say
val sumSquares = sum squares;because it's ill-formed in terms of the types (why?). So, we need to write:
fun sumSquares l = sum (squares l);With
compose, however, we can just write:
val sumSquares = compose sum squares;That is, in general:
(compose f g) x == f (g x)
Compose itself is just defined as:
fun compose f g = ;
You should make sure you understand the types of all these functions.
While curry and uncurry are not built in,
compose is built in as the infix operator o. So you would
really write:
val sumSquares = sum o squares;
The next thing I want to talk about is the question of how names are scoped in ML. At present we have only seen two kinds of names: global ones and local formal parameter names.
Suppose we define the following names:
val x = 3; fun addx y = x + y; addx 5;Now, what happens if we "redefine"
x?
val x = 7; addx 5;While this may seem strange at first, it is actually a much more consistent behavior than in imperative languages. After all, if you write:
x = 3; y = x + 2; x = 4; cout << y;in C++, you don't expect the system to print 6 do you?
Note that this rule applies to all names. If I define a function, then another function which uses that function, and then I decide to redefine the original function, the second function will still continue using the original definition of the first function, even though the new definition is the one available at the top level:
fun add1 x = x + 1; fun add2 x = add1 (add1 x); add1 4; add2 4; fun add1 x = x + 3; add1 4; add2 4;

Often we want to define a name so that it is local to some scope. I.e., to name some intermediate result in a function, or to name an auxilliary function.
There are two main ways to restrict the scope of a name in ML: The
let construct, and the local construct. The
syntax for them is as follows:
The let construct is
written:
Thelet definition1 definition2 ... in expression end
local construct is written:
and it is used anywhere you are making a definiton, rather than computing a value.local definition1 definition2 ... in definition> end
The local function is most often used to restrict access to some auxillary function. For example:
local fun factAux result i n = if (i > n) then result else factAux (i * result) (i+1) n; in fun fact n = factAux 1 1 n; end;
This way the user has access to fact but has no idea
fact_aux exists. Notice the compiler keeps its mouth
shut concerning fact_aux.
(Note: The functionality of the local declaration is largely
subsumed by the functionality of the module system, and thus it is not
much used in actual practice.)
The let construct can be used either to provide temporary
names, or to hide a function definition. For example, before we
defined filter as:
fun filter p nil = nil | filter p (h::t) = if p h then h::(filter p t) else filter p t;We could save a little typing, and perhaps make things clearer by writing:
fun filter p nil = nil | filter p (h::t) = let val filter_t = filter p t; in if p h then h::filter_t else filter_t end;As an example of hiding a function, we can use
let in
much the same way as local:
fun fact n = let fun factAux result i n = if (i > n) then result else factAux (i * result) (i+1) n in factAux 1 1 n end;
Because the sub-function is in the scope of the parameters of the outer
function we do not actually need to pass n around
since its value does not change:
fun fact n = let fun factAux result i = if (i > n) then result else factAux (i * result) (i+1) in factAux 1 1 end;
While the let and local constructs enable us to
restrict the scope of names in one way, another issue in programming-in-the-large
is being able to divide up the global name space so that names used in different
global contexts do not conflict. This is the purpose of adding a module system to
a language.
The SML module system provides a clean mechanism for gathering related pieces of code together and for controlling which parts of the code are accessible from the outside. At the same time, all the internal code can be easily tested at the top-level with little or no extra work required for final packaging.
The SML module system is built on three pieces: structures, signatures, and functors. Here we will describe just the most basic part of the system, structures, which correspond to the basic notion of module provided in most other languages.
A structure is simply a collection of type and value definitions gathered together so that they may be loaded together, and accessed through one name.
A structure declaration has the form:
structure structureName = struct definition1 definition2 ... endFor example, we might gather together the last three useful higher-order functions into a single structure name
ho
structure ho = struct fun compose f g = fn x => f (g x) fun curry f = fn x => fn y => f (x,y) fun uncurry f = fn (x,y) => f x y end |
When the structure is loaded into SML, the system echos back the name of the structure, and the names of all the defined objects and their types.
These values are not available at the top-level. Rather, the items defined in the structure must
be accessed through their fully qualified names. which are formed by prepending the name of the
structure and a dot, as in ho.curry.

|
|
This page copyright ©2000 by Joshua S. Hodas. It was built on a Macintosh. Last rebuilt on Monday, January 31, 2000. |
http://www.cs.hmc.edu/~hodas/courses/cs131/lectures/lecture04s.html | |