Reputation: 1246
OCaml's try .. with
does not offer a finally
clause like Java. It would be useful, though, especially when dealing with side effects. For example, I like to open a file, pass the open file to a function, and close it. In case the function raises an exception I have to catch it in order to have a chance to close the file. This gets increasingly complicated when multiple files are opened and opening itself might fail as well. Is there an established programming pattern to deal with this?
Below is a simple function illustrating the problem. Function f
is applied to a channel which belongs to a file if a path
is provided and stdin
otherwise. Because there is no finally clause, close_in io
appears 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
Upvotes: 10
Views: 5479
Reputation: 2118
From the book Unix system programming in OCaml by Xavier Leroy and Didier Rémy in the chapter Generalities
"There is no built-in finalize construct try …finalize in the OCaml language, but it can be easily defined: "
let try_finalize f x finally y =
let res = try f x with exn -> finally y; raise exn in
finally y;
res
Upvotes: 3
Reputation:
Is there an established programming pattern to deal with this?
Yes, wrapper functions that decouple resource clean-up from exception handling. What I do is to use a generic wrapper, unwind
(a LISPism with which I am rather used):
let unwind ~(protect:'a -> unit) f x =
try let y = f x in protect x; y
with e -> protect x; raise e
This is a simple wrapper that doesn't correctly account for exceptions raised in protect
; a fully checked wrapper that ensures that protect
is called only once even if it itself fails could be Yaron Minski's, or this one which I think is a bit clearer:
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
Then, I define specific instances as required, for instance:
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)
The reason I switch the parameters for the specific with_
functions is that I find it more convenient for higher-order programming; in particular, by defining an application operator à la Haskell, I can write:
let () = with_output_file "foo.txt" $ fun otch ->
output_string otch "hello, world";
(* ... *)
with a not very heavy syntax. For a somewhat more involved example, consider the following:
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)
which can be used with a call like with_open_graph " 400x300" $ fun width height -> (*...*)
.
Upvotes: 9
Reputation: 1216
As of OCaml 4.08, there is a Fun.protect
function which provides this functionality in the standard library.
Using it, your example would look like:
let process f = function
| Some path ->
let io = open_in path in
Fun.protect (fun () -> f io)
~finally:(fun () -> close_in io)
| None -> f stdin
Upvotes: 7
Reputation:
If both the function and the finally block raises 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 *)
Upvotes: 1
Reputation: 368
The OCaml Batteries library collection offers two functions that can be used for finally
http://ocaml-batteries-team.github.io/batteries-included/hdoc/BatPervasives.html
val finally : (unit -> unit) -> ('a -> 'b) -> 'a -> 'b
finally fend f x
calls f x
and then fend()
even if f x
raised an exception.
val with_dispose : dispose:('a -> unit) -> ('a -> 'b) -> 'a -> 'b
with_dispose dispose f x
invokes f
on x
, calling dispose x
when f
terminates (either with a return value or an exception).
Upvotes: 1
Reputation: 4415
Here is an option.
(I have removed the matching on path
to boil the code down to a minimal example.)
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 -> ()
Upvotes: 1
Reputation: 8523
It is not built-in to OCaml as far as I know, but you can write a library that lets you encode such patterns. An example library is Catch me if you can, which is a monadic encoding of errors. Here is a tutorial for adding a finally
construct using metaprogramming. There are probably others approaches as well.
Upvotes: 2