- Courses
- 💻Programming Languages
- 🐫OCaml (WIP)
- Type Inference & Higher-Order Functions
- Currying & Partial Application
On this page
Currying and Partial Application in Real Use
Published on July 21, 2026
By Enrico Hochkoeppler
Web Developer & TutoriaLibre Founder
Currying and partial application are related but distinct ideas, and the distinction matters once they show up in real code rather than toy examples. Currying is the underlying fact that every OCaml function of several arguments is really a chain of one-argument functions. Partial application is what that fact makes possible in practice: supplying only some of a function's arguments up front to produce a new, more specific function, ready to be finished off with the remaining arguments later.
From Curried Definition to Specialized Function
The clearest everyday use of partial application is turning a general-purpose function into a specialized one, simply by fixing some of its arguments ahead of time:
let add x y = x + y
let add_five = add 5
# add_five 10;;
- : int = 15
add_five is a genuine function value, of type int -> int, produced by supplying only the first of add's two eventual arguments. Nothing special had to be written to make add_five possible — it falls directly out of add being a curried, one-argument-at-a-time function to begin with. This same pattern applies just as naturally to built-in operators, since they're ordinary curried functions too:
# let double = ( * ) 2;;
val double : int -> int = <fun>
# double 21;;
- : int = 42
Partial Application Inside Higher-Order Functions
Partial application shows up constantly as an argument to List.map, List.filter, and similar functions, precisely because it avoids writing out an anonymous function just to plug in one fixed value:
List.map (add 5) [1; 2; 3] (* [6; 7; 8] *)
List.filter (( < ) 3) [1; 2; 3; 4; 5] (* [4; 5], keeps x where 3 < x *)
add 5 and ( < ) 3 are both already-complete function values by the time they're passed in — there's no need to wrap either one in fun x -> ... first, since partial application already produced exactly the one-argument function each higher-order function expects.
Fitting Partial Application Into a Pipeline
Partial application becomes especially useful once a value is being threaded through a pipeline with |>, since the function on the right-hand side of |> only ever receives one argument: whatever was piped in. Any function that naturally takes its "main" argument somewhere other than last needs some adjustment to fit. List.nth, for instance, takes the list first and the index second — the reverse of what a pipeline handing over a list would need for extracting a specific position. Fun.flip, which swaps the order of a two-argument function's parameters, combines with partial application to solve exactly this:
let second_word sentence =
sentence
|> String.split_on_char ' '
|> Fun.flip List.nth 1
Fun.flip List.nth 1 partially applies the flipped version of List.nth to the index 1, producing a one-argument function — "get element 1 of" — that's exactly what the pipeline needs at that point, ready to receive the list piped in from the previous step.
Tuple-Style Functions Can't Be Partially Applied
Not every multi-argument function in OCaml is written in curried style. A function can also take its arguments bundled into a single tuple:
let add_tupled (x, y) = x + y
# add_tupled;;
- : int * int -> int = <fun>
add_tupled takes exactly one argument — a pair — rather than two arguments one at a time, and there's no way to "partially apply" a tuple, since the whole pair has to exist before the function can be called at all. This is the practical trade-off between the two styles: curried functions support partial application directly; tuple-style functions don't, but can sometimes avoid allocating an intermediate closure when a function is genuinely always called with every argument at once. In everyday OCaml code, the curried style is the default and by far the more common choice, specifically because partial application is valuable often enough to be worth keeping available.
A Performance Note
Calling a curried function with every one of its arguments at once carries no meaningful runtime penalty compared to a tupled equivalent — the compiler generates efficient code for the fully-applied case either way. Partial application does carry a small, real cost, since it involves allocating an intermediate function value (a closure) to hold onto the arguments supplied so far. In ordinary application code, this cost is negligible and essentially never worth avoiding for its own sake; it's worth being aware of mainly in code that's already been identified as performance-critical through profiling, rather than something to preemptively avoid everywhere else.
A Gotcha Worth Knowing: The Ignored Partial Application Warning
A curried function applied to fewer arguments than it needs doesn't fail — it just produces a function value, which is exactly the mechanism partial application relies on. That behavior becomes a real trap when it happens by accident. Consider a sequence of two expressions separated by ;, which requires the first one to have type unit:
let () =
print_int;
print_newline ()
print_int has type int -> unit, not unit — the argument was simply forgotten. Because a curried function missing an argument is a perfectly valid expression on its own, this doesn't fail to compile the way a genuine type error would; instead, the compiler emits a warning about an ignored partial application, flagging that a function is sitting somewhere a plain value was expected and pointing out that some arguments are probably missing. Treating this warning as worth fixing immediately, the same way a missing pattern-match case is worth fixing immediately, is important: it's very often the compiler catching a real, otherwise-silent bug — a function call that looks complete but never actually runs.
Key Takeaways
- Partial application means supplying fewer arguments than a curried function eventually needs, producing a new function that's ready for the rest; currying is the underlying mechanism that makes this possible.
- Partial application is genuinely useful for specializing a function (
add 5), for supplying arguments directly to higher-order functions likeList.map, and for reshaping functions (often withFun.flip) to fit a pipeline's single-argument expectation. - Tuple-style functions take one argument — a tuple — and cannot be partially applied; curried functions can, which is why curried style is the default in idiomatic OCaml.
- Fully applying a curried function has no meaningful performance cost; partial application has a small cost from allocating a closure, generally not worth worrying about outside of profiled, performance-critical code.
- A curried function missing an argument in a place where
unitwas expected triggers an ignored-partial-application warning, which very often indicates a genuinely forgotten argument rather than a false alarm.
Found this helpful? Share the knowledge...
... or support this project to keep it alive!
Previous
Function Composition & Pipe Operator
Next
Mutable State
- From Curried Definition to Specialized Function
- Partial Application Inside Higher-Order Functions
- Fitting Partial Application Into a Pipeline
- Tuple-Style Functions Can't Be Partially Applied
- A Performance Note
- A Gotcha Worth Knowing: The Ignored Partial Application Warning
- Key Takeaways
