是否有既定的编程模式来处理这个问题?
是的,包装函数将资源清理与异常处理分离。我所做的是使用通用包装器,unwind
(我比较习惯的一种 LISPism):
let unwind ~(protect:'a -> unit) f x =
try let y = f x in protect x; y
with e -> protect x; raise e
这是一个简单的包装器doesn't正确解释中提出的异常protect
;一个经过全面检查的包装器,确保protect
即使它本身失败也只被调用一次亚龙·明斯基的 https://caml.inria.fr/pub/ml-archives/caml-list/2003/07/5ff669a9d2be35ec585b536e2e0fc7ca.xml,或者我认为这个更清楚一点:
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 -> (*...*)
.