Functions

6.1 Defining functions

Calling — no parentheses or commas:

let square x = x * x
(* val square : int -> int *)

let add x y = x + y
(* val add : int -> int -> int *)

let greet name = "Hello, " ^ name ^ "!"
square 5          (* 25 *)
add 2 3           (* 5 *)
add 2 (square 3)  (* 11 — parentheses group arguments *)
square (-3)       (* parenthesise negative literals! square -3 means square - 3 *)

6.2 Anonymous functions (lambdas)

fun x -> x * 2
(fun x y -> x + y) 3 4      (* 7 *)
let square = fun x -> x * x (* same as let square x = x * x *)

6.3 Currying & partial application

Every multi-argument function is a chain of single-argument functions: int -> int -> int means int -> (int -> int).

let add x y = x + y
let add5 = add 5          (* int -> int *)
add5 10                   (* 15 *)

let increment_all = List.map (add 1)
increment_all [1;2;3]     (* [2;3;4] *)

6.4 Tupled vs curried

Prefer curried style — it enables partial application.

let add_tupled (x, y) = x + y   (* (int * int) -> int — one argument, a pair *)
let add_curried x y = x + y     (* int -> int -> int *)

6.5 Operators are functions

(+) 2 3                   (* 5 *)
List.fold_left (+) 0 [1;2;3]
( * ) 3 4                 (* note spaces: (* would start a comment *)

6.6 Defining custom operators

Allowed characters: ! $ % & * + - . / : < = > ? @ ^ | ~.

let ( |>> ) x f = f (f x)
let ( +/ ) a b = (a + b) / 2
let ( let* ) = Option.bind   (* binding operators: see chapter 33 *)

5 |>> (fun x -> x * 2)   (* 20 *)

6.7 Pipeline & application operators

let ( |> ) x f = f x          (* built-in *)
let ( @@ ) f x = f x          (* built-in, right-assoc, low precedence *)

[1;2;3;4]
|> List.filter (fun x -> x mod 2 = 0)
|> List.map (fun x -> x * x)
|> List.fold_left (+) 0        (* 20 *)

print_endline @@ string_of_int @@ 42

6.8 Labeled arguments

let divide ~num ~den = num / den
divide ~num:10 ~den:2     (* 5 *)
divide ~den:2 ~num:10     (* order doesn't matter *)

let num = 10 and den = 2 in
divide ~num ~den           (* label punning *)

let f ~x:renamed = renamed + 1   (* rename inside the body *)

6.9 Optional arguments

Rule: An optional argument must be followed by at least one positional argument, otherwise it can never be "erased" (warning 16).

let greet ?(greeting = "Hello") name = greeting ^ ", " ^ name
greet "Ann"                       (* "Hello, Ann" *)
greet ~greeting:"Hi" "Ann"        (* "Hi, Ann" *)

let log ?prefix msg =             (* prefix : string option inside *)
  match prefix with
  | None -> print_endline msg
  | Some p -> print_endline (p ^ msg)

let opt = Some "[x] " in log ?prefix:opt "hello"   (* pass an option directly *)

6.10 Function composition

let compose f g x = f (g x)
let ( % ) f g x = f (g x)
let inc_then_double = (fun x -> x * 2) % (fun x -> x + 1)
inc_then_double 3  (* 8 *)
(* Stdlib 5.2+: Fun.compose *)

6.11 function keyword (match on the last argument)

let describe = function
  | 0 -> "zero"
  | n when n < 0 -> "negative"
  | _ -> "positive"

6.12 The Fun module

Fun.id 5               (* 5 *)
Fun.const 1 "ignored"  (* 1 *)
Fun.flip (-) 1 10      (* 9 *)
Fun.negate (fun x -> x > 0) 5   (* false *)
Fun.protect ~finally:(fun () -> print_endline "cleanup") (fun () -> 42)

6.13 Closures

let make_multiplier n = fun x -> x * n
let triple = make_multiplier 3
triple 7   (* 21 — n is captured in the closure *)