CSCI 2041

ADVANCED PROGRAMMING PRINCIPLES

Exceptions

Errors

It is sometimes possible that an input does not lead to a sensible result, eg:

let rec assoc key lst = match lst with
|(k,v)::t -> if key=k then v else (assoc key t)
|[] -> (* ? *)

We’ve used 'a option before: None | Some v

What if there’s more than one kind of “bad input”?

What if there’s more than kind of bad input?

let rec parser token_list = match token_list with
  | (Const _)::t | (Var _)::t -> ...
  | OP::NOT::t -> let (bx,t1) = parser t in
    (match t1 with RP::t2 -> (bx,t2)
    | _ -> (* missing close paren *))
  | AND::tl
  | OR::tl -> parse_two ...
  | _ -> (* what *)
and parse_bool_exp tl =
  let (bx, t) = parser t in match t with
  | [] -> bx
  | _ -> (* extra tokens after closed expression... *)
type 'a error = Result of 'a | Err of string
let rec parser token_list = match token_list with
  | (Const _)::t | (Var _)::t -> ...
  | OP::NOT::t -> let (bx,t1) = parser t in
    (match t1 with RP::t2 -> (bx,t2)
    | _ -> Err "missing close paren")
  | OP::AND::tl
  | OP::OR::tl -> parse_two ...
  | _ -> Err "unexpected token"
and parse_bool_exp tl =
  let (bx, t) = parser t in match t with
  | [] -> Result bx
  | _ -> Err "Extra tokens in input"

What do we do with recursive results?

let rec parser token_list = match token_list with
  | (Const _)::t | (Var _)::t -> ...
  | OP::NOT::t -> let (bx,t1) = parser t in
    (match (bx,t1) with (Result b,RP::t2) -> (bx,t2)
    | (Err s, _) -> (Err s,t1)
    | _ -> Err "missing close paren")
  | OP::AND::tl
  | OP::OR::tl -> parse_two ...
  | _ -> Err "unexpected token"
and parse_bool_exp tl =
  let (bx, t) = parser t in match t with
  | [] -> Result bx
  | _ -> Err "Extra tokens in input"

Exceptions

Exceptions provide non-local handling of exceptional conditions.

To define a new exception Constructor we declare an exception:

exception <Name> of <type-expr>

Examples already defined in Ocaml:

exception Invalid_argument of string
exception Failure of string
exception Match_failure of string*int*int
exception Not_found
exception Stack_overflow

Exception constructors are values of a special union type exn:

let s = Invalid_argument "42"
val s : exn = Invalid_argument "42"

To raise an exception, we use the raise keyword

raise <ConstructorName> <args>

raise : exn -> 'a must be called with a valid exn constructor

The arguments to the constructor must match the type of the constructor

Exceptions are handled using the try/with expression:

try <expr> with
 | <exc-pattern1> -> <body1>
 | <exc-pattern2> -> <body2>

Tries to evaluate <expr>:

  • If no exception is raised, the result is the value of <expr>:

    try 2 with _ -> 02

  • If an exception is raised the first matching pattern’s body is returned:

    try raise Not_found with Not_found -> 3 | _ -> 43

  • If no matching pattern, the exception is propagated.

Type rules for try/with:


  • <expr> and each <body> must have matching types


  • each <pattern> must match an exn constructor


Exceptions provide non-local handling of exceptional conditions


When handled correctly, exceptions separate functionality from error recovery.

let menagerie = [("bowtruckle","Pickett"); ("phoenix", "Fawkes");
  ("hippogriff","Buckbeak")]
in try List.assoc "blast-ended skrewt" menagerie
  with Not_found -> "unfriendly"

However, unhandled exceptions cause program evaluation to terminate without producing a value:

exception TheInquisition
let unexpected s =
  let x = raise TheInquisition in s+17

let v = unexpected 42
exception Unclosed
exception Unexpected of token
exception Extra of token list
let rec parser token_list = match token_list with
  | (Const _)::t | (Var _)::t -> ...
  | OP::NOT::t -> let (bx,t1) = parser t in
    (match t1 with RP::t2 -> (bx,t2)
    | _ -> raise Unclosed)
  | OP::AND::tl
  | OP::OR::tl -> parse_two ...
  | h::tl -> raise (Unexpected h)
and parse_bool_exp tl =
  let (bx, t) = parser t in match t with
  | [] -> bx
  | _ -> raise (Extra t)

More about exceptions in Hickey, ch. 9. Some tricky bits…

try f x with p1 -> ...: f x is not in tail position

exception Empty
let split = function h::t -> (h,t) | [] -> raise Empty
let rec mapc f l =
  try let (h,t) = split l in (f h)::(mapc f t)
  with _ -> []
let woah = mapc (fun n -> 100 / n) [1;0;3]

type of raise: exn -> 'a

Type of Not_found and (Invalid_argument "blah") : exn

cs2041.org

// reveal.js plugins