OCaml has an exception mechanism similar to many other programming languages. A new type of OCaml exception is defined with this syntax:
exception E of t
E is a constructor name and
t is a type. The
of t is optional.
Notice how this is similar to defining a constructor of a variant type.
To create an exception value, use the same syntax you would for creating
a variant value. Here, for example, is an exception value whose constructor
Failure, which carries a
Failure "something went wrong"
This constructor is pre-defined in the standard library (scroll down to "predefined exceptions") and is one of the more common exceptions that OCaml programmers use.
To raise an exception value
e, simply write
There is a convenient function
failwith : string -> 'a in the standard library
Failure. That is,
failwith s is equivalent to
raise (Failure s).
(Often we use this function in the assignment release code we ship to you.)
To catch an exception, use this syntax:
try e with | p1 -> e1 | ... | pn -> en
e is what might raise an exception. If it does not, the entire
try expression evaluates to whatever
e does. If
e does raise an exception value
v, that value
v is that matched against the provide patterns, exactly like
Exceptions are Extensible Variants
All exception values have type
exn, which is a variant
defined in the core. It's an unusual
kind of variant, though, called an extensible variant, which allows
new constructors of the variant to be defined after the variant type
itself is defined. See the OCaml manual for more information about
extensible variants if you're interested.
Since they are just variants, the syntax and semantics of exceptions is already covered by the syntax and semantics of variants—with one exception (pun intended), which is the dynamic semantics of how exceptions are raised and handled.
Dynamic semantics. As we originally said, every OCaml expression either
evaluates to a value
raises an exception
or fails to terminate (i.e., an "infinite loop").
So far we've only presented the part of the dynamic semantics that handles
the first of those three cases. What happens when we add exceptions?
Now, evaluation of an expression either produces a value or produces an
exception packet. Packets are not normal OCaml values; the only pieces
of the language that recognizes them are
try. The exception value
produced by (e.g.)
Failure "oops" is part of the exception packet produced
raise (Failure "oops"), but the packet contains more than just the exception value;
there can also be a stack trace, for example.
For any expression
e other than
try, if evaluation of a subexpression of
produces an exception packet
P, then evaluation of
e produces packet
But now we run into a problem for the first time: what order are subexpressions
evaluated in? Sometimes the answer to that question is provided by the semantics
we have already developed. For example, with let expressions, we know that the
binding expression must be evaluated before the body expression. So the following
exception A exception B let x = raise A in raise B
And with functions, the argument must be evaluated before the function. So
the following code also raises
(raise B) (raise A)
It makes sense that both those pieces of code would raise the same exception,
given that we know
let x = e1 in e2 is syntactic sugar for
(fun x -> e2) e1.
But what does the following code raise as an exception?
(raise A, raise B)
The answer is nuanced. The language specification does not stipulate what order the
components of pairs should be evaluated in. Nor did our semantics exactly determine
the order. (Though you would be forgiven if you thought it was left to right.)
So programmers actually cannot rely on that order. The current implementation of OCaml,
as it turns out, evaluates right to left. So the code above actually raises
If you really want to force the evaluation order, you need to use let expressions:
let a = raise A in let b = raise B in (a,b)
That code will raise
One interesting corner case is what happens when a raise expression itself has a subexpression that raises:
exception C of string exception D of string raise (C (raise D "oops"))
That code ends up raising
D, because the first thing that has to happen is
C (raise D "oops") to a value. Doing that requires evaluating
raise D "oops" to a value. Doing that causes a packet containing
to be produced, and that packet then propagates and becomes the result of
C (raise D "oops"), hence the result of evaluating
raise (C (raise D "oops")).
Once evaluation of an expression produces an exception packet
P, that packet
propagates until it reaches a
try e with | p1 -> e1 | ... | pn -> en
The exception value inside
P is matched against the provided patterns using the
usual evaluation rules for pattern matching—with one exception
(again, pun intended). If none of the patterns matches, then instead of producing
Match_failure inside a new exception packet, the original exception packet
continues propagating until the next
try expression is reached.
There is a pattern form for exceptions. Here's an example of its usage:
match List.hd  with |  -> "empty" | h::t -> "nonempty" | exception (Failure s) -> s
Note that the code is above is just a standard
match expression, not a
It matches the value of
List.hd  against the three provided patterns. As we know,
List.hd  will raise an exception containing the value
The exception pattern
exception (Failure s) matches that value. So the above
code will evaluate to
In general, exception patterns are a kind of syntactic sugar. Consider this code:
match e with | p1 -> e1 | ... | pn -> en
Some of the patterns
p1..pn could be exception patterns of the form
q1..qn be that subsequence of patterns (without the
r1..rm be the subsequence of non-exception patterns. Then we can rewrite the
match try e with | q1 -> e1 | ... | qn -> en with | r1 -> e1 | ... | rm -> em
Which is to say: try evaluating
e. If it produces an exception packet, use the
exception patterns from the original match expression to handle that packet.
If it doesn't produce an exception packet but instead produces a normal value,
use the non-exception patterns from the original match expression to match that value.
Exceptions and OUnit
If it is part of a function's specification that it raises an exception, you might want to write OUnit tests that check whether the function correctly does so. Here's how to do that:
open OUnit2 let tests = "suite" >::: [ "empty" >:: (fun _ -> assert_raises (Failure "hd") (fun () -> List.hd )); "nonempty" >:: (fun _ -> assert_equal 1 (List.hd )); ] let _ = run_test_tt_main tests
assert_raises exc (fun () -> e) checks to see whether expression
exc. If so, the OUnit test case succeeds, otherwise it fails.
Note: a common error is to forget the
(fun () -> ...) around
e. If you do,
the OUnit test case will fail, and you will likely be confused as to why. The reason
is that, without the extra anonymous function, the exception is raised before
assert_raises ever gets a chance to handle it.