Home > Enterprise >  Raising meaningful exceptions in Ocaml
Raising meaningful exceptions in Ocaml

Time:01-26

I would like to print a meaningful message when raising a defined exception in OCaml:

type t = A | B of int
exception Wrong of t

Say I have a function

let string_of_t = function
  | A -> "A"
  | B n -> ("B" ^ (string_of_int n))

Then I would like to have a function

val print_exception : ( 'a -> string ) -> a' exn -> string

so that I can define

let my_raise e =
  print_endline ("Error: I got the unexpected value" ^ (print_exception string_of_t e));
  raise e    [???]

Is there such a function print_exception ?

The problem is not very well-posed (for instance, there is no a' exn type), but I hope my intent is understandable. I've seen that one can use [@@deriving sexp] but this looks like some magic outside of the language, and there is probably something easier and within the language.

CodePudding user response:

There are two ways. The first one is using Printexc, the second one would be to match all the exceptions you want to print and print accordingly, something like:

exception Zero of int
exception B of string

let pp ppf = function
  | Zero d -> Format.fprintf ppf "Zero of %d" d
  | B s -> Format.fprintf ppf "B of %s" s
  | Not_found -> Format.fprintf ppf "Not found"
  | _ -> Format.fprintf ppf "Your exception is in another castle"

let f n d = if d = 0 then raise (Zero d) else n / d

let () =
  let n, d = (10, 0) in
  try Format.printf "%d/%d is %d@." n d (f n d)
  with e ->
    Format.eprintf "%a@." pp e;
    Format.eprintf "%s@." (Printexc.to_string e)

Will give

❯ ./exc
Zero of 0
Exc.Zero(0)

Combining the two seems to be the best solution to be able to customise some displays and let all the others be a default one:

let pp ppf = function
  | Zero d -> Format.fprintf ppf "Zero of %d" d
  | e -> Format.fprintf ppf "%s" (Printexc.to_string e)

In OCaml we don't have (yet) modular implicits so unless you use [@@derive ...] you need to use one of the two solutions.

As a side-note, exceptions can be caught in a pattern matching:

let () =
  let n, d = (10, 0) in
  match f n d with
  | r -> Format.printf "%d/%d is %d@." n d r
  | exception e ->
      Format.eprintf "%a@." pp e;
      Format.eprintf "%s@." (Printexc.to_string e)

It does semantically the same as what I wrote before but it's better for call stacks (if I'm not mistaken)

CodePudding user response:

If you want to log a meaningful message before raising, it seems to me that it might be simpler to combine the logging and exception raising in one function rather than trying to reconstruct an error message from a generic unknown exception after the fact. For instance, the following function

let log_and_raise exn fmt =
  Format.kfprintf
  (fun ppf -> Format.pp_print_newline ppf (); raise exn)
  Format.err_formatter fmt 

can be used like this

exception A of int
let test n = log_and_raise (A n) "Raising the exception (A %d)" n

and will raise the exception A n after printing the error message on stderr.

  •  Tags:  
  • Related