The Objective Caml system
release 3.09
Documentation and user's manual
Xavier Leroy
(with Damien Doligez, Jacques Garrigue, Didier Rémy and Jérôme Vouillon)
Copyright © 2005 Institut National de
Recherche en Informatique et en Automatique
This manual is also available in
PDF.
Postscript,
DVI,
plain text,
as a
bundle of HTML files,
and as a
bundle of Emacs Info files.
Contents
Foreword
This manual documents the release 3.09 of the Objective Caml
system. It is organized as follows.
-
Part I, “An introduction to Objective Caml”,
gives an overview of the language.
- Part II, “The Objective Caml language”, is the
reference description of the language.
- Part III, “The Objective Caml tools”, documents
the compilers, toplevel system, and programming utilities.
- Part IV, “The Objective Caml library”, describes the
modules provided in the standard library.
Conventions
Objective Caml runs on several operating systems. The parts of
this manual that are specific to one operating system are presented as
shown below:
Unix:
This is material specific to the Unix family of operating
systems, including Linux and MacOS X.
Windows:
This is material specific to Microsoft Windows (95, 98, ME,
NT, 2000, XP).
License
The Objective Caml system is copyright © 1996, 1997, 1998,
1999, 2000, 2001, 2002, 2003, 2004, 2005
Institut National de Recherche en Informatique et en
Automatique (INRIA).
INRIA holds all ownership rights to the Objective Caml system.
The Objective Caml system is open source and can be freely
redistributed. See the file LICENSE in the distribution for
licensing information.
The present documentation is copyright © 2005
Institut National de Recherche en Informatique et en
Automatique (INRIA). The Objective Caml documentation and user's
manual may be reproduced and distributed in whole or
in part, subject to the following conditions:
-
The copyright notice above and this permission notice must be
preserved complete on all complete or partial copies.
- Any translation or derivative work of the Objective Caml
documentation and user's manual must be approved by the authors in
writing before distribution.
- If you distribute the Objective Caml
documentation and user's manual in part, instructions for obtaining
the complete version of this manual must be included, and a
means for obtaining a complete version provided.
- Small portions may be reproduced as illustrations for reviews or
quotes in other works without this permission notice if proper
citation is given.
Availability
The complete Objective Caml distribution can be accessed via the
http://caml.inria.fr/Caml Web site.
The http://caml.inria.fr/Caml Web site
contains a lot of additional information on Objective Caml.
Part I
An introduction to Objective Caml |
This part of the manual is a tutorial introduction to the Objective
Caml language. A good familiarity with programming in a conventional
languages (say, Pascal or C) is assumed, but no prior exposure to
functional languages is required. The present chapter introduces the
core language. Chapter 3 deals with the
object-oriented features, and chapter 2 with the
module system.
1.1 Basics
For this overview of Caml, we use the interactive system, which
is started by running ocaml from the Unix shell, or by launching the
OCamlwin.exe application under Windows. This tutorial is presented
as the transcript of a session with the interactive system:
lines starting with # represent user input; the system responses are
printed below, without a leading #.
Under the interactive system, the user types Caml phrases, terminated
by ;;, in response to the # prompt, and the system compiles them
on the fly, executes them, and prints the outcome of evaluation.
Phrases are either simple expressions, or let definitions of
identifiers (either values or functions).
#1+2*3;;
- : int = 7
#let pi = 4.0 *. atan 1.0;;
val pi : float = 3.14159265358979312
#let square x = x *. x;;
val square : float -> float = <fun>
#square(sin pi) +. square(cos pi);;
- : float = 1.
The Caml system computes both the value and the type for
each phrase. Even function parameters need no explicit type declaration:
the system infers their types from their usage in the
function. Notice also that integers and floating-point numbers are
distinct types, with distinct operators: + and * operate on
integers, but +. and *. operate on floats.
#1.0 * 2;;
This expression has type float but is here used with type int
Recursive functions are defined with the let rec binding:
#let rec fib n =
if n < 2 then 1 else fib(n-1) + fib(n-2);;
val fib : int -> int = <fun>
#fib 10;;
- : int = 89
1.2 Data types
In addition to integers and floating-point numbers, Caml offers the
usual basic data types: booleans, characters, and character strings.
#(1 < 2) = false;;
- : bool = false
#'a';;
- : char = 'a'
#"Hello world";;
- : string = "Hello world"
Predefined data structures include tuples, arrays, and lists. General
mechanisms for defining your own data structures are also provided.
They will be covered in more details later; for now, we concentrate on lists.
Lists are either given in extension as a bracketed list of
semicolon-separated elements, or built from the empty list []
(pronounce “nil”) by adding elements in front using the ::
(“cons”) operator.
#let l = ["is"; "a"; "tale"; "told"; "etc."];;
val l : string list = ["is"; "a"; "tale"; "told"; "etc."]
#"Life" :: l;;
- : string list = ["Life"; "is"; "a"; "tale"; "told"; "etc."]
As with all other Caml data structures, lists do not need to be
explicitly allocated and deallocated from memory: all memory
management is entirely automatic in Caml. Similarly, there is no
explicit handling of pointers: the Caml compiler silently introduces
pointers where necessary.
As with most Caml data structures, inspecting and destructuring lists
is performed by pattern-matching. List patterns have the exact same
shape as list expressions, with identifier representing unspecified
parts of the list. As an example, here is insertion sort on a list:
#let rec sort lst =
match lst with
[] -> []
| head :: tail -> insert head (sort tail)
and insert elt lst =
match lst with
[] -> [elt]
| head :: tail -> if elt <= head then elt :: lst else head :: insert elt tail
;;
val sort : 'a list -> 'a list = <fun>
val insert : 'a -> 'a list -> 'a list = <fun>
#sort l;;
- : string list = ["a"; "etc."; "is"; "tale"; "told"]
The type inferred for sort, 'a list -> 'a list, means that sort
can actually apply to lists of any type, and returns a list of the
same type. The type 'a is a type variable, and stands for any
given type. The reason why sort can apply to lists of any type is
that the comparisons (=, <=, etc.) are polymorphic in Caml:
they operate between any two values of the same type. This makes
sort itself polymorphic over all list types.
#sort [6;2;5;3];;
- : int list = [2; 3; 5; 6]
#sort [3.14; 2.718];;
- : float list = [2.718; 3.14]
The sort function above does not modify its input list: it builds
and returns a new list containing the same elements as the input list,
in ascending order. There is actually no way in Caml to modify
in-place a list once it is built: we say that lists are immutable
data structures. Most Caml data structures are immutable, but a few
(most notably arrays) are mutable, meaning that they can be
modified in-place at any time.
1.3 Functions as values
Caml is a functional language: functions in the full mathematical
sense are supported and can be passed around freely just as any other
piece of data. For instance, here is a deriv function that takes any
float function as argument and returns an approximation of its
derivative function:
#let deriv f dx = function x -> (f(x +. dx) -. f(x)) /. dx;;
val deriv : (float -> float) -> float -> float -> float = <fun>
#let sin' = deriv sin 1e-6;;
val sin' : float -> float = <fun>
#sin' pi;;
- : float = -1.00000000013961143
Even function composition is definable:
#let compose f g = function x -> f(g(x));;
val compose : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b = <fun>
#let cos2 = compose square cos;;
val cos2 : float -> float = <fun>
Functions that take other functions as arguments are called
“functionals”, or “higher-order functions”. Functionals are
especially useful to provide iterators or similar generic operations
over a data structure. For instance, the standard Caml library
provides a List.map functional that applies a given function to each
element of a list, and returns the list of the results:
#List.map (function n -> n * 2 + 1) [0;1;2;3;4];;
- : int list = [1; 3; 5; 7; 9]
This functional, along with a number of other list and array
functionals, is predefined because it is often useful, but there is
nothing magic with it: it can easily be defined as follows.
#let rec map f l =
match l with
[] -> []
| hd :: tl -> f hd :: map f tl;;
val map : ('a -> 'b) -> 'a list -> 'b list = <fun>
1.4 Records and variants
User-defined data structures include records and variants. Both are
defined with the type declaration. Here, we declare a record type to
represent rational numbers.
#type ratio = {num: int; denum: int};;
type ratio = { num : int; denum : int; }
#let add_ratio r1 r2 =
{num = r1.num * r2.denum + r2.num * r1.denum;
denum = r1.denum * r2.denum};;
val add_ratio : ratio -> ratio -> ratio = <fun>
#add_ratio {num=1; denum=3} {num=2; denum=5};;
- : ratio = {num = 11; denum = 15}
The declaration of a variant type lists all possible shapes for values
of that type. Each case is identified by a name, called a constructor,
which serves both for constructing values of the variant type and
inspecting them by pattern-matching. Constructor names are capitalized
to distinguish them from variable names (which must start with a
lowercase letter). For instance, here is a variant
type for doing mixed arithmetic (integers and floats):
#type number = Int of int | Float of float | Error;;
type number = Int of int | Float of float | Error
This declaration expresses that a value of type number is either an
integer, a floating-point number, or the constant Error representing
the result of an invalid operation (e.g. a division by zero).
Enumerated types are a special case of variant types, where all
alternatives are constants:
#type sign = Positive | Negative;;
type sign = Positive | Negative
#let sign_int n = if n >= 0 then Positive else Negative;;
val sign_int : int -> sign = <fun>
To define arithmetic operations for the number type, we use
pattern-matching on the two numbers involved:
#let add_num n1 n2 =
match (n1, n2) with
(Int i1, Int i2) ->
(* Check for overflow of integer addition *)
if sign_int i1 = sign_int i2 && sign_int(i1 + i2) <> sign_int i1
then Float(float i1 +. float i2)
else Int(i1 + i2)
| (Int i1, Float f2) -> Float(float i1 +. f2)
| (Float f1, Int i2) -> Float(f1 +. float i2)
| (Float f1, Float f2) -> Float(f1 +. f2)
| (Error, _) -> Error
| (_, Error) -> Error;;
val add_num : number -> number -> number = <fun>
#add_num (Int 123) (Float 3.14159);;
- : number = Float 126.14159
The most common usage of variant types is to describe recursive data
structures. Consider for example the type of binary trees:
#type 'a btree = Empty | Node of 'a * 'a btree * 'a btree;;
type 'a btree = Empty | Node of 'a * 'a btree * 'a btree
This definition reads as follow: a binary tree containing values of
type 'a (an arbitrary type) is either empty, or is a node containing
one value of type 'a and two subtrees containing also values of type
'a, that is, two 'a btree.
Operations on binary trees are naturally expressed as recursive functions
following the same structure as the type definition itself. For
instance, here are functions performing lookup and insertion in
ordered binary trees (elements increase from left to right):
#let rec member x btree =
match btree with
Empty -> false
| Node(y, left, right) ->
if x = y then true else
if x < y then member x left else member x right;;
val member : 'a -> 'a btree -> bool = <fun>
#let rec insert x btree =
match btree with
Empty -> Node(x, Empty, Empty)
| Node(y, left, right) ->
if x <= y then Node(y, insert x left, right)
else Node(y, left, insert x right);;
val insert : 'a -> 'a btree -> 'a btree = <fun>
1.5 Imperative features
Though all examples so far were written in purely applicative style,
Caml is also equipped with full imperative features. This includes the
usual while and for loops, as well as mutable data structures such
as arrays. Arrays are either given in extension between [| and |]
brackets, or allocated and initialized with the Array.create
function, then filled up later by assignments. For instance, the
function below sums two vectors (represented as float arrays) componentwise.
#let add_vect v1 v2 =
let len = min (Array.length v1) (Array.length v2) in
let res = Array.create len 0.0 in
for i = 0 to len - 1 do
res.(i) <- v1.(i) +. v2.(i)
done;
res;;
val add_vect : float array -> float array -> float array = <fun>
#add_vect [| 1.0; 2.0 |] [| 3.0; 4.0 |];;
- : float array = [|4.; 6.|]
Record fields can also be modified by assignment, provided they are
declared mutable in the definition of the record type:
#type mutable_point = { mutable x: float; mutable y: float };;
type mutable_point = { mutable x : float; mutable y : float; }
#let translate p dx dy =
p.x <- p.x +. dx; p.y <- p.y +. dy;;
val translate : mutable_point -> float -> float -> unit = <fun>
#let mypoint = { x = 0.0; y = 0.0 };;
val mypoint : mutable_point = {x = 0.; y = 0.}
#translate mypoint 1.0 2.0;;
- : unit = ()
#mypoint;;
- : mutable_point = {x = 1.; y = 2.}
Caml has no built-in notion of variable – identifiers whose current
value can be changed by assignment. (The let binding is not an
assignment, it introduces a new identifier with a new scope.)
However, the standard library provides references, which are mutable
indirection cells (or one-element arrays), with operators ! to fetch
the current contents of the reference and := to assign the contents.
Variables can then be emulated by let-binding a reference. For
instance, here is an in-place insertion sort over arrays:
#let insertion_sort a =
for i = 1 to Array.length a - 1 do
let val_i = a.(i) in
let j = ref i in
while !j > 0 && val_i < a.(!j - 1) do
a.(!j) <- a.(!j - 1);
j := !j - 1
done;
a.(!j) <- val_i
done;;
val insertion_sort : 'a array -> unit = <fun>
References are also useful to write functions that maintain a current
state between two calls to the function. For instance, the following
pseudo-random number generator keeps the last returned number in a
reference:
#let current_rand = ref 0;;
val current_rand : int ref = {contents = 0}
#let random () =
current_rand := !current_rand * 25713 + 1345;
!current_rand;;
val random : unit -> int = <fun>
Again, there is nothing magic with references: they are implemented as
a one-field mutable record, as follows.
#type 'a ref = { mutable contents: 'a };;
type 'a ref = { mutable contents : 'a; }
#let (!) r = r.contents;;
val ( ! ) : 'a ref -> 'a = <fun>
#let (:=) r newval = r.contents <- newval;;
val ( := ) : 'a ref -> 'a -> unit = <fun>
In some special cases, you may need to store a polymorphic function in
a data structure, keeping its polymorphism. Without user-provided
type annotations, this is not allowed, as polymorphism is only
introduced on a global level. However, you can give explicitly
polymorphic types to record fields.
#type idref = { mutable id: 'a. 'a -> 'a };;
type idref = { mutable id : 'a. 'a -> 'a; }
#let r = {id = fun x -> x};;
val r : idref = {id = <fun>}
#let g s = (s.id 1, s.id true);;
val g : idref -> int * bool = <fun>
#r.id <- (fun x -> print_string "called id\n"; x);;
- : unit = ()
#g r;;
called id
called id
- : int * bool = (1,