This document gives you a brief tour of OCaml. It covers a rather small selection of features; the selection has been based on what features I personally think represent OCaml the best.

OCaml Briefly

by Mads Hartmann - 13 Nov 2014

This document does very little to explain use-cases for the selected features but rather focuses on syntax. For a more in-depth coverage of all of these features I recommend reading the OCaml Document and User’s Manual and Real World OCaml.

For each feature there is a small explanation of the syntax, some examples, and links for further reading. As such this document is ideal for someone who wants to get a taste of the features of OCaml or who want to learn more about a specific feature.

If you have any comments please reach out to me at mads379@gmail.com or @mads_hartmann on twitter.

If you want to play around with the code I recommend using this online REPL.

Note: This is still a work in progress. I haven’t yet covered the following: pattern matching, modules, functors, Abstract Data Types, and much more.

Table of Contents

Lists, Arrays, and Tuples

A List is constructed using square brackets where the elements are separated by semi-colons:

[ 1 ; 2; 3 ];;

An Array is constructed using square brackets with bars where the elements are separated by semi-colons:

[| 1 ; 2; 3 |];;

Tuples are constructed using parens and the elements are separated using commas:

( "foo", "bar", "foobar" )
- : string * string * string = ("foo", "bar", "foobar")

Let-bindings

A let-binding associates an identifier with a value. It is introduced using the following syntax let <identifier> = <expression> in <expression>.

let hi = "hi " in
let there = "there!" in
hi ^ there;;
hi there!

The ^ is a function that concatenates two strings. The ^ function is used as an infix operator in the example above but it could just as easily have been invoked in a prefix manner as shown below.

(^) "Hi " "there";;
Hi there

Let-bindings are also used to declare functions. More on that in the next section.

Functions

As was shown in the previous section you invoke a function by adding the arguments after the function name separated by whitespace.

min 42 10;;
10

It might take some time to get used to separating function arguments with whitespace rather than commas as you do in other programming languages. In another language the above example might look like this:

min(10,24);;
- : int * int -> int * int = <fun>

However, in OCaml this will partially apply the function min with one argument, the tuple (10, 24). Partial application is another nice feature of OCaml that allows you to supply N arguments to a function of arity X and get a function of arity X-N in return. This is possible as all functions in OCaml are curried.

let at_least_42 = max 42 in
at_least_42 22;;
42

Defining functions

A function is defined using the following syntax let <name> <arg1> <arg2> = <expression> in <expression>, that is, the function name followed by it’s arguments separated by whitespace.

The following is a function that takes one argument x (which is inferred to be an integer) and returns the square value of that integer.

let square x = x * x;;
val square : int -> int = <fun>

You can use the fun keyword to introduce a lambda, it has the following syntax fun <arg1> <arg2> -> <expression>. So the example above is equivalent to this:

let square = fun x -> x * x;;
val square : int -> int = <fun>

As mentioned earlier some functions can be used as infix operators. A function can be used as in infix operator if one of the following names are used ! $ % & * + - . / : < = > ? @ ^ | ~. Read more about infix and prefix functions in this section of Real World OCaml.

If your function only takes one argument and you want to pattern match on it you can use the function keyword (please ignore this horribly inefficient implementation I’m about to show you):

let rec sum = function
  | x :: xs -> x + (sum xs)
  | [] -> 0
in sum [1;2;3;4;5;1;2];;
18

More on pattern matching later. The previous example also shows that if you want to define a recursive function in OCaml you have to use the rec keyword.

Labeled arguments

By prefixing an argument with ~ you can give it a label which makes the code easier to read and makes the order of the arguments irrelevant.

let welcome ~greeting ~name = Printf.printf "%s %s\n" greeting name in
welcome ~name:"reader" ~greeting:"Hi"
Hi reader
- : unit = ()

Shayne Fletcher has written an excellent blog post that goes into both labeled and optional arguments (described below) in more detail.

Optional arguments

By prefixing an argument with ? you can make it optional. The value of optional arguments are represented using the Option type.

let welcome ?greeting_opt name =
  let greeting = match greeting_opt with
    | Some greeting -> greeting
    | None -> "Hi"
  in
  Printf.printf "%s %s\n" greeting name
in
welcome ~greeting_opt:"Hey" "reader" ;
welcome ?greeting_opt:None "reader"
Hey reader
Hi reader
- : unit = ()

Default arguments

For optional arguments you can provide a default value. Thus the previous example could also have been written as such:

let welcome ?(greeting="Hi") name =
  Printf.printf "%s %s\n" greeting name
in
welcome ~greeting:"Hey" "reader" ;
welcome "reader"
Hey reader
Hi reader
- : unit = ()

Records

Records are used to store a collection of values together as a single value. The example below defines a record named person with two components.

type person = {
  name: string;
  age: int;
} ;;
let p = { name = "Mads" ; age = 25 } in
Printf.printf "%s is %d years old" p.name p.age
Mads is 25 years old- : unit = ()

Records can be parameterized using a polymorphic type.

type 'a ranked = {
  item: 'a;
  rank: int;
};;
let item = { item = Some 42 ; rank = 1 }
val item : int option ranked = {item = Some 42; rank = 1}

There is a lot more to be said of records. See this this section of Real World OCaml and this section of the OCaml Users Guide.

Variants

Variants, also known as algebraic data types, are commonly used to define recursive data structures. Just like records they can be parameterized using a polymorphic type as shown in the example below where a variant is used to represent a binary tree.

type 'a tree =
  | Leaf of 'a
  | Node of 'a tree * 'a * 'a tree;;
Node ((Leaf "foo"), "bar", (Leaf "foobar"));;
- : string tree = Node (Leaf "foo", "bar", Leaf "foobar")

The type tree has two constructors: Leaf and Node. The example below shows one way to compute the height of such a tree:

let rec depth = function
  | Leaf _ -> 1
  | Node (left, _ ,right) -> 1 + max (depth left) (depth right)
in
let tree =
  Node ((Leaf 1), 2, (Node ((Leaf 3), 4, (Node ((Leaf 5), 6, (Leaf 6))))))
in
depth tree;;
4

The example above uses the function keyword to define a function that takes a single argument that is pattern matched on.

Variants are one of the most useful features of OCaml so it’s well worth spending some more time studying them. See this section of Real World OCaml for information on use-cases and best-practices.

Polymorphic Variants

OCaml also has another type of variants that they call polymorphic variants. When using polymorphic variants you do not need to define the constructors prior to using them - you can think of them as an ad-hoc version of the regular variants.

let length_of_title book_title =
  match String.length book_title with
  | length when length <= 5  -> `Short
  | length when length <= 10 -> `Medium
  | _  -> `Long
in
length_of_title "The Hitchhiker's Guide to the Galaxy"
- : [> `Long | `Medium | `Short ] = `Long

Once again this feature is thoroughly covered in this section of Real World OCaml.

Extensible Variants

As the name suggests extensible variants are variants that can be extended with new constructors.

This is a new feature that was introduced in OCaml 4.02 and as such I haven’t yet used these in my own code so I will stick to the examples shown in the OCaml Users Manual.

type attr = .. ;;
type attr += Str of string ;;
type attr +=
  | Int of int
  | Float of float ;;
type attr += Int of int | Float of float

For more information read this section of the OCaml Users Manual. This features was released after Real World Ocaml and as such it isn’t covered in the book unfortunately. I look forward to then next revision.

Exceptions

OCaml gives you the posibility of using exceptions for signaling and handling exceptional conditions in your programs.

Exceptions are not checked, meaning that the OCaml compiler does not enforce that you catch exceptions.

You can define your own exceptions in a way similar to variants.

exception BadRequest of int * string

You signal an exception using the raise keyword.

raise (BadRequest (500, "Oh noes, the service broke"))

You catch exceptions using the following syntax

try int_of_string "foo" with
| Failure msg -> -1
-1

Since OCaml 4.02 you can catch exceptions in pattern matches.

match int_of_string "foo" with
| n -> Printf.printf "Got an integer %d" n
| exception Failure msg -> Printf.printf "Caught exception with message %s" msg
Caught exception with message int_of_string- : unit = ()

As always, for more information read the section in the OCaml Users Manual.

Generalized Algebraic Datatypes

Generalized Algebraic Datatypes (GADTs) are an extension of the normal variants (algebraic datatypes). They can be used to allow the OCaml compiler to perform more sophisticated type checking or to control the memory representation of your data. I wrote a blog post about the former and Janestreet has written a great blog post on the latter.

The following is an example from my blog post. I include it here as an example of the syntax you use to define GADTs.

type _ value' =
  | GBool : bool -> bool value'
  | GInt : int -> int value'

type _ expr' =
  | GValue : 'a value' -> 'a expr'
  | GIf : bool expr' * 'a expr' * 'a expr' -> 'a expr'
  | GEq : 'a expr' * 'a expr' -> bool expr'
  | GLt : int expr' * int expr' -> bool expr'

GADTs were introduced in OCaml 4.00. The following section in the OCaml Users Manual describe the syntax and contains a few examples.