-
Notifications
You must be signed in to change notification settings - Fork 2
Currying
Functions can be curried, For example:
fn map(f, l) {
if (l == []) {
[]
} else {
f(head(l)) @ map(f, tail(l))
}
}
fn mul(x, y) {
x * y
}
map(mul(2), [1, 2, 3, 4]); // [2, 4, 6, 8]
Note that mul is defined with two arguments but called with only one, resulting
an a closure that accepts the second argument and performs the computation.
There are better ways to write
map, see later.
Binary operators can also be curried. That means the previous example could be more succinctly written as
map(2 *, [1, 2, 3, 4]);
Internally this is achieved completely by the parser. On detecting an incomplete binary operator, it wraps it in a closure, with a generated argument symbol. So writing
map(3 * 2 *, [1, 2, 3, 4]);
is just a shorthand for:
map(fn (_x) { 3 * 2 * _x }, [1, 2, 3, 4]);
Because this is done at a syntactic level, there are some oddities. For example this works
map(2 * 3 + 1 +, [1, 2, 3, 4]); // => [8, 9, 10, 11]
That is because the curried expression parses as ((2 * 3) + 1) +. However
something like 2 + 2 * would parse as 2 + (2 *) and would fail to type check
because the curried (2 *) is a function int -> int and you can't add an int to a function
(see Type Notation for an explanation of int -> int.)
Similarly, this works:
map(1 @, [[2], [3], [4]]); // => [[1, 2], [1, 3], [1, 4]]
but this doesn't:
map(1 @ 2 @, [[3], [4], [5]]);
Because @ is right-associative the expression 1 @ 2 @ parses as 1 @ (2 @), i.e. an
attempt to cons 1 with a function list(int) -> list(int).
So curried binary operators are an occasional convenience, and you can always provide a wrapper function if they won't parse the way you want them to.
Up: Functions
PyScheme, AKA F-Natural