Prelude.ExnFunctions for working with exceptions.
fold ?exn f acc x: tail-recursively nest applications of (f x acc) until f raises an exception.
If ~exn is given, only exn cleanly terminates the recursion and any other exception is reraised; otherwise, any exception terminates cleanly.
(fold f acc x) = (f x (... (f x (f x (f x (f x acc)))))) Example: this function counts the number of lines on an input channel:
fold (fun chan n -> Stdlib.ignore (input_line chan); succ n) 0 default d f x: return (f x) unless an exception is raised, in which case return default value d.
(succeeds f x) evaluates (f x) and returns true iff the evaluation did NOT raise an exception.
(succeeds f x) = (catch f x |> Option.something)(reraise exn f x) is (f x), converting any exception to exn.
Specifically, (reraise exn f x) is (f x) unless an exception is raised, in which case we instead raise exn.
(reraise ~this exn f x) is (f x) unless some exception e is raised, in which case we raise:
exn if (e = this)e if (e <> this)finalize f g x: evaluate (f x), afterwards calling (g x) to finalize the resource x.
Evaluation of (g x) is guaranteed even if f raises an exception (which is re-raised).
Process the lines of a file, avoiding a file descriptor leak in the case that process raises an exception:
open_in "/etc/passwd" |> finalize process close_in (to_string exn) produces a "better" (IMHO) representation of an exception for end users.
Specifically, Sys_error and Unix.Unix_error exceptions, the ones most often encountered by end users, are rendered like so:
Printexc.to_string: (Sys_error "X: No such file or directory")
Exn.to_string: X: No such file or directory
Printexc.to_string: Unix.Unix_error(Unix.ENOENT, "open", "X")
Exn.to_string: X: No such file or directory
All other exceptions are just passed to Printexc.to_string.
Short-circuiting Execution
return and label taken from Batteries. See OCaml Batteries Included.
val label : ('u label -> 'u) -> 'u(label f) creates a label around the invocation of f, which is a computation that can be short-circuited (exited) via return.
Usage is: (label (fun l -> ... return l v ...; d)) where l is the label, v is the value to be returned, and d is the default value of the entire expression if return is never called.
Example: return immediately with (Some x), the first value < 10 in xs, presumably a long list of integers, or None if no such value is found: label (fun l -> iter (fun x -> if x < 10 then return l (Some x)) xs; None)
Compare this to:
foldl (fun r x -> if x < 10 && r = None then Some x else r) None xs which evaluates to the same value, but always examines every element of xs, where the label / return version stops at the first x < 10.
"label f creates a new label x and invokes f x. If, during the execution of f, return x v is invoked, the execution of f x stops immediately and label f returns v. Otherwise, if f x terminates normally and returns y, label f returns y.
Calling return x v from outside scope f is a run-time error and causes termination of the program." See Batteries.