Functions in OCaml
Learn how to define and use functions in OCaml - currying, partial application, closures, higher-order functions, recursion, and labeled arguments with Docker-ready examples
In OCaml, functions are not just a way to organize code - they are the central building block of the entire language. As a functional language, OCaml treats functions as first-class values: you can store them in variables, pass them as arguments, return them from other functions, and build new functions by combining existing ones.
This changes how you think about functions. There is no return keyword and no statement/expression distinction - a function simply evaluates to the value of its body. Every function technically takes exactly one argument and returns one result, a design called currying that makes partial application natural and powerful.
In this tutorial you’ll learn how to define functions, write recursive functions, work with higher-order functions and closures, use the pipe operator to compose data transformations, and take advantage of OCaml’s labeled and optional arguments.
Defining Functions
Functions are defined with let. There is no return - the value of the last expression is the result. Recursive functions need the rec keyword, and mutually recursive functions are joined with and.
Create a file named functions.ml:
| |
Note how arguments are separated by spaces, not commas, and the call site uses the same syntax: add 3 4, not add(3, 4). The let ... in form inside hypotenuse introduces names that are only visible within the function body - this is how local scope works in OCaml.
Higher-Order Functions, Closures, and Currying
Because functions are values, a function can take a function as an argument or return a brand new function. A function that captures variables from its surrounding scope is a closure. And since every OCaml function is curried, supplying only some arguments produces a new function - partial application.
Create a file named higher_order.ml:
| |
The pipe operator |> is idiomatic OCaml: x |> f is just f x, but chaining reads top-to-bottom like a pipeline. Here we filter the odd numbers, multiply each by ten, then sum them. Note also (+) - wrapping an operator in parentheses turns it into an ordinary function value you can pass to List.fold_left.
Labeled and Optional Arguments
OCaml lets you name arguments with ~label, so callers can pass them in any order, and declare optional arguments with ? plus a default value. This makes function signatures self-documenting and avoids the “which argument was which?” problem.
Create a file named labeled_args.ml:
| |
Labeled arguments are especially valuable for functions like divide where swapping the two integers silently produces a wrong answer. The label makes the intent explicit at every call site.
Running with Docker
Run each example using the official OCaml image. The ocaml command interprets the file directly - no separate compile step needed.
| |
Expected Output
Running functions.ml:
add 3 4 = 7
multiply 6 7 = 42
square 9 = 81
hypotenuse 3 4 = 5.0
factorial 5 = 120
is_even 10 = true
is_odd 7 = true
Running higher_order.ml:
apply_twice (+3) to 10 = 16
add5 100 = 105
double 21 = 42
sum of squares = 55
odd numbers x10 summed = 90
Running labeled_args.ml:
5
20
Hello, World!
Bonjour, Monde!
Key Concepts
- No
returnkeyword - a function evaluates to the value of its last expression, since everything in OCaml is an expression. - Space-separated application - call functions with
add 3 4, notadd(3, 4); arguments are separated by spaces, not commas. - Recursion is explicit - use
let recfor recursive functions andlet rec ... and ...for mutually recursive ones. - Functions are first-class values - they can be stored, passed as arguments, and returned, enabling higher-order functions like
List.mapandList.fold_left. - Currying and partial application - every function takes one argument and returns a result; supplying fewer arguments yields a new specialized function.
- Closures capture variables from their defining scope, as in
make_adderreturning a function that remembersn. - The pipe operator
|>threads a value through a chain of transformations for readable, top-to-bottom data pipelines. - Labeled (
~name) and optional (?(name = default)) arguments make call sites clear and let you pass arguments in any order.
Comments
Loading comments...
Leave a Comment