これに対処するための確立されたプログラミングパターンはありますか?
はい、リソースのクリーンアップを例外処理から切り離すラッパー関数です。私がしていることは、一般的なラッパーunwind
(私がむしろ使用しているLISPism)を使用することです:
let unwind ~(protect:'a -> unit) f x =
try let y = f x in protect x; y
with e -> protect x; raise e
これは、 ;で発生した例外を正しく考慮しない単純なラッパーです。それ自体が失敗した場合でも一度だけ呼び出されるprotect
ことを保証する完全にチェックされたラッパーは、Yaron Minskiのものである可能性があります。または、これは少し明確だと思います。protect
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_
高次のプログラミングに便利だと思うからです。特に、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 -> (*...*)
。