Named after the mathematician Haskell Curry, "currying" is a way of defining functions of multiple arguments in terms of higher-order functions, each of which takes a single argument and returns a higher order function.
The key insight of currying: Any function of type
a * b -> c
can also be phrased as a function of type
a -> b -> c
Consider normal addition:
val add = fn (x, y) => x + y; val foo = add(3, 4);
This can also be viewed as:
val handCurriedAdd = fn (x) => fn(y) => x + y; val addFive = handCurriedAdd 5; (* adds 5 to any int *) val eleven = addFive 6; (* applying addFive *) val seventeen = (handCurriedAdd 8) 9; (* applying both at once *) val twenty = handCurriedAdd 10 10; (* application is left-associative *)
Note that handCurriedAdd is identical to makeAddX from the previous page. Since manually currying functions is tedious, ML provides a convenient syntax for currying:
fun curriedAdd x y = x + y; val thirty = curriedAdd 15 15;
Of course, these examples are pretty trivial. But consider what we can do if we curry qsort:
(* Curried definition: notice no comma between params *) fun qsort (greaterThan:('a * 'a) -> bool) (elems:'a list) = ... (* Not taking advantage of currying. Not so cool. *) fun sortComplexByReal elems = qsort (sortBy(RealPart)) elems); (* Here, we really take advantage of curried syntax *) val sortComplexByImag = qsort (sortBy(RealPart));
Incidentally, here we I am showing you currying as a matter of convenience in syntax. However, it's a profound idea that a function of multiple arguments can be conceptually partially evaluated to produce a function that does "the rest of the computation".
The ML standard libraries use currying extensively. Here are a couple of function type signatures from the List structure:
val filter : ('a -> bool) -> 'a list -> 'a list val exists : ('a -> bool) -> 'a list -> bool