Look again at the type of a simple function with more than one argument:
We have been considering functions like this as taking two arguments and returning a result. In fact, the truth is a little different. The type int → int → int can also be written as int → (int → int). OCaml lets us omit the parentheses because → is a right-associative operator in the language of types. This gives us a clue.
In truth, the function
addis a function which, when you give it an integer, gives you a function which, when you give it an integer, gives the sum.
This would be of no particular interest to us, except for one thing: we can give a function with two arguments just one argument at a time, and it turns out to be rather useful. For example:
OCaml
# let add x y = x + y
val add : int -> int -> int = <fun>
# let f = add 6
val f : int -> int = <fun>
# f 5
- : int = 11
Here, we have defined a function f by applying
add to just one argument. This gives a function of type
int →
int which adds six to any number. We then apply this
function to 5, giving 11. When defining
f, we used partial application (we supplied only
some of the arguments). In fact, even when supplying all the arguments
at once, we could equally write (add) rather than
add. We can add six to every element in a list:
map (add 6) [10; 20; 30]
Here, add 6 has the type int →
int, which is an appropriate type to be the first
argument to map when mapping over a list of integers. We
can use partial application to simplify some examples from earlier in
the book. We mentioned that you can write, for example,
( * ) to produce a function from an operator. It has type
int →
int → int. We may
partially apply this function, so instead of writing
map (fun x -> x * 2) [10; 20; 30]
we may write
map (( * ) 2) [10; 20; 30]
Recall the function to map something over a list of lists from the questions to Chapter 6:
With partial application, we can write
Can you see why? The partially applied function map f is
of type α list → β
list, which is exactly the right type to pass to
map when mapping over lists of lists. In fact, we can go
even further and write:
Here, map (map f) has type α
list list → β list list so when
an f is supplied to mapl, a function is
returned requiring just the list. This is partial application at work
again.
You can see the real structure of multiple-argument functions, by
writing add using anonymous functions:
This makes it more obvious that our two-argument add
function is really just composed of one-argument functions, but
let add x y = x + y is much
clearer! We can supply one or more arguments at a time, but they must be
supplied in order. Everything in this chapter also works for functions
with more than two arguments.
The function f x y has type α
→ β →
γ which can
also be written α →
(β
→ γ). Thus, it takes an argument of
type α and returns a function
of type β → γ
which, when you give it an argument of type β returns something of type γ. And so, we can supply just one
argument to the function f (which is called partial
application), or supply both at once. When we write
let f x y = … this is just
shorthand for
let f = fun x -> fun y -> …
Rewrite the summary paragraph at the end of this chapter for the
three argument function g a b c.
Recall the function member x l which determines if
an element x is contained in a list l. What is
its type? What is the type of member x? Use partial
application to write a function member_all x ls which
determines if an element is a member of all the lists in the list of
lists ls.
Why can we not write a function to halve all the elements of a
list like this: map (( / ) 2) [10; 20; 30]? Write a
suitable division function which can be partially applied in the manner
we require.
Write a function mapll which maps a function over
lists of lists of lists. You must not use the
let rec construct. Is it possible to write
a function which works like map, mapl, or
mapll depending upon the list given to it?
Write a function truncate which takes an integer and
a list of lists, and returns a list of lists, each of which has been
truncated to the given length. If a list is shorter than the given
length, it is unchanged. Make use of partial application.
Write a function which takes a list of lists of integers and returns the list composed of all the first elements of the lists. If a list is empty, a given number should be used in place of its first element.