OCaml Tutorial: Function

By Xah Lee. Date: . Last updated: .

Function Expression

Function in ocaml is a expression. That means, a function is a value that represents the function. This is also called anonymous function, lambda.

(* syntax for a function expression *)
fun n -> n + 1;;

(* applying a function to a value *)
(fun n -> n + 1) 2;;            (* ⇒ 3 *)

Parenthesis is used for grouping, not function call.

Function with Multiple Parameters

syntax is

fun parameter_1 parameter_2 parameter_3 … -> expression

(* syntax of function with 2 parameters *)
fun x y -> x +. y ;;

(* apply it to 2 arguments *)
(fun x y -> x +. y) 3. 4. ;;    (* ⇒ 7. *)

Assign Function to Variable

A function can be assigned to a variable, thus giving it a name.

(* function can be assigned to variable *)
let f = fun n -> n + 1;;
f 2;;                           (* ⇒ 3 *)

Define Named Function

Here's a syntax shortcut for defining named function.

The form

let f = fun p1 p2 … -> expr

is equivalent to

let f p1 p2 … = expr

Example

(* define named function *)
let f x = x + 1;;
f 3;;                           (* ⇒ 4 *)

Notice how this is similar to defining constants.

(* named function of 2 parameters *)
let f x y = x + y;;
f 3 4;;                                 (* ⇒ 7 *)

Recursive Function

Functions that refer to itself must be declared with rec.

(* example of factorial function *)
let rec f n = if n == 1 then 1 else n * f (n-1);;
f 3                                     (* ⇒ 6 *)

(* example of fibonacci sequence. *)
(* nth term is sum of previous 2 terms. Starting with 0 and 1. *)
let rec f n = match n with 0 -> 0 | 1 -> 1 | x -> f (n-1) + f (n-2);;
f 9;;                                  (* ⇒ 34 *)

(* Note: above are for example only. Not efficient *)

Operator Precedence of Function Syntax

The operator for function definition -> is more sticky to the right. This means, when you have expr1 -> expr2 -> expr3, ocaml will interpret it as expr1 -> ( expr2 -> expr3 ). This means when you define a function that returns a function, you don't have to use parenthesis.

However, function application in ocaml favors the left side. If you write f g h x;;, ocaml will interpret it as ((f g) h) x;;. This is particular important to know when you use functions of multiple arguments. Here's some examples:

let f s = s ^ "1";;                     (* append 1 to a string *)
let g s = s ^ "2";;                     (* append 2 to a string *)
let h s = s ^ "3";;                     (* append 3 to a string *)

h (g (f "x"));;                         (* ⇒ "x123" *)

In the above, if you use f g h "x";;, the compiler will give a error:

# Characters 0-1:
  f g h "x";;
  ^
Error: This function is applied to too many arguments,
maybe you forgot a `;'

Because it starts by computing f(g). This results in a error right away, because by your definition, f has one parameter of type string, and returns a string, but you are trying to apply the result to h "x".

If you call ((f g) h) "x", then the error is more clear:

((f g) h) "x";;
      ^
Error: This expression has type string -> string but is here used with type
         string

The error says “but is here used with type string” because your function g is defined to be of type “string → string”, and you are feeding g to f, which expect a string.

Using Operator as Function

Operators, such as “+ *” etc, can be considered as functions of 2 parameters. They can be used as a function in ocaml. A function call has the form “f arg1 arg2”, while operator such as “+” is used in the form “arg1 + arg2”. Sometimes it is useful to call operators in the function form, so that the power of function and its syntax fully apply to all operators.

To use operator in the form of a function, the operator must be bracketed with paren. The form is:

(operator_symbol) arg1 arg2

Example:

( + ) 3 4;;                             (* ⇒ 7 *)
( *. ) 23. 3.;;                         (* ⇒ 69. *)
( + ) ((+) 3 4) 5;;                       (* ⇒ 12 *)
( * ) ((+) 3 4) 5;;                       (* ⇒ 35 *)

Note that extra paren for grouping is necessary.

Defining Your Own Operator

You can define your own operator. The form is:

let ( symbol_chars ) left_arg right_arg = expression

Example:

(* define operator for vector addition of 2 dimensional vectors *)
(* using 2-tuple as vectors *)
let ( @+ ) vec1 vec2 = ( (fst vec1)+(fst vec2), (snd vec1)+(snd vec2) ) ;;
(3,4) @+ (5,6);;                        (* ⇒ (8,10) *)

Note: the char sequence used for operator is limited to symbol chars, such as “! @ # $ % ^ & *”. They cannot be letters, numbers, nor Unicode char.


Function Composition Example

In the following, you define a function compo that returns the function composition of 2 other functions. “compo” takes 2 arguments, f and g, each one is a function. It returns a function. When the result function is applied to a argument x, it is equivalent to f(g(x)).

(* example of defining your own function composition function *)

let compo f g = (fun x -> f (g x) );;

(* applying compo to 2 arguments, then apply the result to "a" *)
compo (fun x -> x ^ "c") (fun x -> x ^ "b") "a";;

In the above, the compo is given 2 arguments. Each one is a function, that takes a string input and output with another char appended. When compo is given these two input, and the output is applied to the argument "a", the result is "abc".

In the following example, we define a function named “apply”. “apply” takes 2 arguments, f and x. When called, it computes “f(x)”.

(* example of defining your own “apply” function *)
let apply = fun a -> ( fun b -> a b );;

(* define a function f *)
let f = fun x -> x+1;;

(* apply f to the value 3 *)
print_int (apply f 3);;                 (* prints 4 *)

(* define another function g *)
let g = fun x -> x+1;;

(* apply one of f or g, to the value 0 *)
print_int ( apply (if true then f else g) 0 );;  (* ⇒ 1 *)

In the above, we defined the variable “apply” using the “let apply =” form. On the right hand side, we have fun a -> (…), which means a function with one parameter, and this parameter is named “a”.

On the right hand side, is the body of the function fun b -> a b. This body is another function, with one argument “b”. The body of this function is “a b”. The syntax “symbol1 symbol2” means function symbol1 evaluated at symbol2. So, all of the above means, that “apply” is now a function expression with one parameter, and this function expression will return another function expression with one parameter, and returns “a evaluated at b”.

The whole result of this is that “apply” is a function with 2 parameters, say “a” and “b”, and return “a” evaluated at “b”.

Automatic Decomposition of Functions

Functions of more than 1 parameter can always be applied to just 1 argument. It returns a function expression, such that when this expression is used as a function by giving it more arguments, it returns the same result as original function with full arguments. (function decomposition in computer languages is also called “currying”.) Example:

let f n m = n + m;;
let g = f 3;;           (* g is now equivalent to: “fun x -> 3 + x” *)
g 4;;                   (* ⇒ 7 *)

(f 3) 4;;               (* don't need to define g first *)

Function with More Than One Parameters

To define a function with 2 parameters, you do it by defining a function with 1 parameter, that returns another function of 1 parameter. The form is this:

function parameter -> function parameter -> expression

The following is a example:

(* defining a function “f(x,y)” that computes x + y *)
function x -> ( function y -> x + y );;

(* applying the function to values *)
(function x -> ( function y -> x + y) ) 3 4;; (* ⇒ 7 *)

In the above example, the line function x -> ( function y -> x + y ), means a function with parameter x, which returns function y -> x + y, which is a function with parameter y, and computes x + y.

The number of parameters of a function is called its “arity”. So, a function with 2 parameters is a function of arity 2.

To define a function with 3 or more parameters, the same pattern is used.

function parameter_1 -> function parameter_2 -> function parameter_3 -> … -> expression

At the language core, all multi-parameter functions are decomposed into a sequence of functions of 1 parameter.