oCamlCase

Anonymous functions

In most languages, creating a function requires giving it a name. OCaml lets you write a function as an expression anywhere a value is expected. The syntax is fun <arg> -> <expr>, and the result has a function type.

Other languages call these lambdas or anonymous functions. In OCaml, the formal term is abstraction, because the expression abstracts over the argument: the argument is left open, and the body is the computation to run once it is supplied.

The type of fun x -> x * x is int -> int. It takes an integer and returns an integer. No name is needed, and the function can be used immediately. You can also apply it on the spot by wrapping it in parentheses and passing an argument.

Key point

An abstraction produces a value of a function type. It is not a statement or a declaration. It is an expression that evaluates to a function.

anon.ml
(* A function with no name *)
let _ = fun x -> x * x
(* type: int -> int *)

(* Define it and apply it immediately *)
let _ = (fun x -> x * x) 7
(* = 49 *)

(* Bind it to a name if you want to reuse it *)
let square = fun x -> x * x

Tuple arguments

An abstraction takes exactly one argument, but that argument can be a tuple. Tuple destructuring happens right in the argument position, so the function body can use the components directly by name.

This looks like multiple arguments, but it is not. The function takes one pair and unpacks it. The distinction matters for currying: a function taking a tuple cannot be partially applied in the same way as a curried function.

Contrast fun x y -> x * y (two curried arguments, type int -> int -> int) with fun (x, y) -> x * y (one tuple argument, type int * int -> int). The difference is visible in the types.

tuple_arg.ml
(* Tuple argument *)
let _ = fun (x, y) -> x * y
(* type: int * int -> int *)

(* Apply with a tuple *)
let _ = (fun (x, y) -> x * y) (6, 7)
(* = 42 *)

(* Curried version, two separate args *)
let _ = fun x y -> x * y
(* type: int -> int -> int *)

The connection to let bindings

When you write let square x = x * x, OCaml treats it as exactly the same thing as let square = fun x -> x * x. The multi-argument let form is syntactic sugar. Under the hood, a named function is just an abstraction bound to a name.

This means there is nothing special about named functions. They are ordinary values of function types, stored in the environment like any other binding. The fun form makes this explicit.

let_vs_fun.ml
(* These two definitions are completely equivalent *)
let square x = x * x
let square' = fun x -> x * x

(* The let form is sugar. The fun form is the underlying representation.
   OCaml sees both as binding a function value to a name. *)

(* Same for multiple arguments *)
let add x y = x + y
let add' = fun x -> fun y -> x + y

Why first-class functions matter

Because abstractions are ordinary values, you can pass a function as an argument to another function, return a function from a function, and store functions in data structures. This is what it means for functions to be first-class.

Abstractions are the building block that makes all of this natural. You can produce a function on the fly, right at the call site, without first giving it a name and a top-level binding. Higher-order functions like List.map and List.filter rely entirely on this ability.

first_class.ml
(* Pass a function as an argument, defined inline *)
List.map (fun x -> x * 2) [1; 2; 3]
(* [2; 4; 6] *)

List.filter (fun x -> x > 2) [1; 2; 3; 4]
(* [3; 4] *)

(* Return a function from a function *)
let multiplier factor = fun x -> x * factor
let double = multiplier 2
(* double : int -> int *)
Good practice

Use inline abstractions for short, single-use transformations. Give a function a name with let only when you need to reuse it or when the logic is complex enough that a name improves readability.