I would like to release a resource when any exception is raised during the usage of the resource.
In C++ this task is easy: I put the release into the destructor, which gets called automatically, whatever happens. In Java one uses the 'finally' clause. What is the practice for this same task in Standard ML?
I tried to catch all exception with a variable pattern 'e' and re-raise it:
datatype FileReadResult = FileReadOkay of string | FileReadError
fun read_file (file_path_string : string) : FileReadResult =
let
val istream = TextIO.openIn file_path_string
(* this file is my resource *)
in
TextIO.closeIn istream;
FileReadOkay "" (* the content of the file will go here *)
handle e => (TextIO.closeIn istream; raise e)
end
handle Io => FileReadError
My compiler (MLton) accepts it, but because I am new in ML, I ask here for some assurance that this is really the right thing | best practice to do.
As this is a common design pattern, I created the below utility function to express it:
(* Uses the given resource in the given way while releasing it if any exception occurs. *)
fun use_resource (resource : 'Resource) (releaser : 'Resource -> unit) (usage : unit -> 'Result) : 'Result =
let
val r = usage ()
in
releaser resource;
r
end
handle e => (releaser resource; raise e)
This function plays the same role as the 'using' feature in C#.