Emulate try-with-finally in OCaml

OCaml try .. withdoes not offer a suggestion finallysuch as Java. It would be helpful, however, especially when considering side effects. For example, I like to open a file, pass an open file to a function, and close it. If a function throws an exception, I must catch it in order to be able to close the file. This becomes more complicated when multiple files are opened, and the opening itself may fail. Is there an established programming scheme to solve this problem?

The following is a simple function illustrating the problem. The function fis applied to the channel, which belongs to the file, if available pathand stdinotherwise. Since there is no final sentence, close_in ioappears twice.

let process f  = function 
    | Some path -> 
        let io = open_in path in 
            ( (try f io with exn -> close_in io; raise exn)
            ; close_in io
            )
    | None -> f stdin
+5
source share
6 answers

Is there a predefined programming pattern to handle this?

Yes, wrapper functions that separate resource cleanup from exception handling. I use a common shell unwind(LISPism, which I rather use):

let unwind ~(protect:'a -> unit) f x =
  try let y = f x in protect x; y
  with e -> protect x; raise e

, , protect; , , protect , , Yaron Minski's, , :

let unwind ~protect f x =
  let module E = struct type 'a t = Left of 'a | Right of exn end in
  let res = try E.Left (f x) with e -> E.Right e in
  let ()  = protect x in
  match res with
  | E.Left  y -> y
  | E.Right e -> raise e

, :

let with_input_channel inch f =
  unwind ~protect:close_in f inch

let with_output_channel otch f =
  unwind ~protect:close_out f otch

let with_input_file fname =
  with_input_channel (open_in fname)

let with_output_file fname =
  with_output_channel (open_out fname)

, with_, , ; , à la Haskell, :

let () = with_output_file "foo.txt" $ fun otch ->
  output_string otch "hello, world";
  (* ... *)

. :

let with_open_graph spec (proc : int -> int -> unit) =
  unwind ~protect:Graphics.close_graph (fun () ->
    proc (Graphics.size_x ()) (Graphics.size_y ());
    ignore (Graphics.wait_next_event [Graphics.Button_down]);
    ignore (Graphics.wait_next_event [Graphics.Button_up]))
    (Graphics.open_graph spec)

with_open_graph " 400x300" $ fun width height -> (*...*).

+6

Unix OCaml Xavier Leroy Didier Rémy Generalities

" finalize try... OCaml, :"

let try_finalize f x finally y =
  let res = try f x with exn -> finally y; raise exn in
  finally y;
  res
+3

OCaml, , , . , , . - finally . , .

+2

. ( path, .)

let process f path =
  let exn = ref None in
  let io = open_in path in
  (try f io with e -> exn := Some e);
  close_in io;
  match !exn with Some e -> raise e | None -> ()
+1

The OCaml Batteries Library Collection offers two features that can be used for the ultimate

http://ocaml-batteries-team.imtqy.com/batteries-included/hdoc/BatPervasives.html

val finally : (unit -> unit) -> ('a -> 'b) -> 'a -> 'b

finally fend f xcauses f x, and then fend(), even if an f xexception is thrown.

val with_dispose : dispose:('a -> unit) -> ('a -> 'b) -> 'a -> 'b

with_dispose dispose f xcalls fon x, calling dispose xwhen fcompleted (either with a return value or with an exception).

+1
source

If both the function and finally block throw an exception, I prefer to see the initial exception, so I use something like this:

type 'a result = OK of 'a | Exn of exn
let result_of f x = try OK (f x) with e -> Exn e

(** invokes [f x], and always releases [x] by invoking [release x] *)
let do_with x release f =
  let result = result_of f x in
  let closed = result_of release x in
  match result, closed with
  | Exn e, _ -> raise e (* [f x] raised exception *)
  | _, Exn e -> raise e (* [release x] raised exception *)
  | OK r, OK () -> r (* all OK *)
+1
source

All Articles