Dive C1.03.2
Currying
Every function takes one argument; the type int -> int -> int tells you exactly how.
A function with two parameters is, in F#, a function of one argument that
returns a function of one argument. This is currying, named for Haskell Curry. It is why the
inferred type of add x y = x + y is int -> int -> int
and not a pair-taking signature: the arrow associates to the right, so the type reads
int -> (int -> int). Application, by contrast, associates to the
left, so add 3 4 means (add 3) 4.
One argument, then another
The function add is written with two parameters, but it is built from nested
one-argument functions. The definition below and the equivalent lambda form are the same value;
add takes x and returns the function
fun y -> x + y.
let add x y = x + y // val add : int -> int -> int // the same value, written out as nested one-argument functions let add = fun x -> (fun y -> x + y)
Supply one argument and a function comes back. Binding the result of add 3 to a
name shows its type: a function from int to int. The F#
Interactive prints the signature of a partially applied value with parentheses around the function type.
let add3 = add 3 // val add3 : (int -> int) add3 4 // 7 add 3 4 // 7 == (add 3) 4
- currying
- a function of many parameters is nested one-argument functions, each returning the next.
- arrow (->)
- associates to the right:
int -> int -> intisint -> (int -> int). - application
- associates to the left:
add 3 4is(add 3) 4. - (int -> int)
- the F# Interactive prints a partially applied function's type in parentheses.
Reading the type
The signature int -> int -> int has two arrows. Because the arrow associates
to the right, the second arrow binds first: the type is one int mapping to the
function (int -> int). The diagram makes the right-association bracket explicit.
int -> int -> int · the arrow associates right
Two arrows, one reading: add takes an int and
hands back a function. The signature was never a two-argument type; it was always one argument at a time.
Unfolding add 3 4
Step through the call. Left-associated application means add 3 4 is
(add 3) 4: apply add to 3 first,
producing a function, then apply that to 4. Each step highlights the part about to
reduce.
add 3 4 · one argument at a time
- add 3 4 application is left-associated: (add 3) 4
- (add 3) 4 apply add to 3 · add 3 : (int -> int)
- (fun y -> 3 + y) 4 the function returned by add 3
- 3 + 4 apply it to 4 · substitute y := 4
- 7 the value
A two-argument call is two one-argument calls in a row. The first produces a function; the second runs it. Currying is what lets the first stand on its own — the next dive keeps it.
Currying
A function of many parameters is one-argument functions nested, each returning the next.
F# today
add 3 is a value of type (int -> int) — a usable function on its own.
Where this leads
Currying is the mechanism; partial application is the use. Because add 3
is already a complete value, a program can supply some arguments now and the rest later — naming the
intermediate function, or passing it straight to List.map. Dive C1.03.3 turns the
unfolding into a tool.
References
Sources
- Microsoft — F# documentation — currying and the function-application syntax.
- The F# Language Specification — function types, the right-associative arrow, and left-associative application.
- The F# Software Foundation — the language's home and learning resources.
Related in this course
- C1.03.3 — Partial application — using the function
add 3returns. - C1.03 — Functions — the module hub and its three dives.
- Elixir — Currying with closures — building the same shape by hand on the BEAM.