OCaml Tutorial: Function
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
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.
let f = fun p1 p2 … -> expr
is equivalent to
let f p1 p2 … = expr
(* define named function *) let f x = x + 1;; f 3;; (* ⇒ 4 *)
(* named function of 2 parameters *) let f x y = x + y;; f 3 4;; (* ⇒ 7 *)
Note: the syntax is the same as defining variable.
Functions that refer to itself must be declared with
(* 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.
(aka right associative)
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 string *) let g s = s ^ "2";; (* append 2 to string *) let h s = s ^ "3";; (* append 3 to 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
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 with function named
f, 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
( + ) 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
(* 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 one of following chars:
! $ % & * + - . / : ? @ ^ < > = | ~
They cannot be letters, numbers, nor Unicode char. (there are few exceptions, such as buildin operator
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
(* 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
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 ->
At the language core, all multi-parameter functions are decomposed into a sequence of functions of 1 parameter.
「fun」 vs 「function」
function allows the use of pattern matching (i.e.
|), but consequently it can be passed only one argument.
function p_1 -> exp_1 | … | p_n -> exp_n
is equivalent to
fun exp -> match exp with p_1 -> exp_1 | … | p_n -> exp_n
fun does not allow pattern matching, but can be passed multiple arguments, e.g.
fun x y -> x + y