oCamlCase

What is currying?

When you write let add x y = x + y, OCaml sees it as fun x -> fun y -> x + y. The multi-argument syntax is sugar. This style of representing multi-argument functions as nested single-argument functions is called currying, after mathematician Haskell Curry.

The practical consequence: you can call a function with fewer arguments than it expects, and you'll get back a new function waiting for the rest.

Key insight

A curried function of n arguments is a chain of n single-argument functions, each closing over the previous argument.

The type signature makes this visible. add has type int -> int -> int, which associates right: int -> (int -> int). It takes an int and returns a function from int to int.

add.ml
(* These two definitions are identical *)
let add x y = x + y
let add = fun x -> fun y -> x + y

(* Type: int -> int -> int
         i.e. int -> (int -> int) *)

let result   = add 3 4     (* 7 *)
let add_five = add 5       (* int -> int *)
let result   = add_five 10  (* 15 *)

Partial application

Supplying fewer arguments than a function expects returns a new, more specific function. This is partial application, and it is the main reason currying is useful in practice.

In most languages, creating a specialized version of a function requires a wrapper or a factory. In OCaml, you just call the function with some of its arguments.

Here, multiply_by is a general multiplier. Partially applying it with 2 gives us double, a real named function we can reuse anywhere. We can then pass it directly to List.map without any wrapping.

Good practice

When writing higher-order functions, design the argument order so the most-varied argument comes last. This makes partial application natural.

partial.ml
let multiply_by factor x = factor * x
(* multiply_by : int -> int -> int *)

let double    = multiply_by 2
let times_ten = multiply_by 10

double 7      (* 14 *)
times_ten 3  (* 30 *)

(* No wrapper needed, pass directly *)
List.map double [1; 2; 3; 4; 5]
(* [2; 4; 6; 8; 10] *)

Labeled arguments

OCaml supports labeled arguments with ~label: syntax. Labels make the argument order explicit at the call site, and allow arguments to be supplied in any order, which changes how partial application works.

With labels, you can fix any argument regardless of position. Here we fix ~sep first to create specialized join functions, but we could equally fix the list argument and vary the separator.

Watch out

Labeled partial application is order-independent only if the remaining arguments have distinct labels (or the last argument is unlabeled). Mixing labeled and unlabeled can surprise you.

labeled.ml
let join ~sep words =
  String.concat sep words

(* Fix sep, vary the list *)
let csv      = join ~sep:","
let sentence = join ~sep:" "

csv      ["alice"; "bob"; "carol"]
(* "alice,bob,carol" *)

sentence ["the"; "quick"; "fox"]
(* "the quick fox" *)

The pipe operator and composition

Currying works naturally with the |> pipe operator. The pipe passes a value left-to-right through a sequence of functions, most of which are partially applied. The result reads like a data pipeline: each step is one transformation.

You can also define a compose operator % for when you want to build a reusable pipeline without an initial value.

compose.ml
(* Right-to-left compose *)
let (%) f g x = f (g x)

let trim_upper =
  String.uppercase_ascii % String.trim

trim_upper "  hello  "   (* "HELLO" *)

(* Left-to-right with |> *)
"  hello world  "
|> String.trim
|> String.uppercase_ascii
|> String.split_on_char ' '
(* ["HELLO"; "WORLD"] *)