Có một mô hình lập trình thành lập để đối phó với điều này?
Có, các hàm bao bọc tách tài nguyên khỏi xử lý ngoại lệ. Những gì tôi làm là sử dụng một wrapper chung, unwind
(một LISPism mà tôi chứ không phải sử dụng):
let unwind ~(protect:'a -> unit) f x =
try let y = f x in protect x; y
with e -> protect x; raise e
Đây là một wrapper đơn giản mà không chiếm một cách chính xác cho trường hợp ngoại lệ được nêu ra trong protect
; một wrapper kiểm tra đầy đủ đảm bảo rằng protect
được gọi là chỉ một lần ngay cả khi nó tự không thể được Yaron Minski's, hoặc cái này mà tôi nghĩ là rõ ràng hơn một chút:
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
Sau đó, tôi xác định các trường hợp cụ thể theo yêu cầu, ví dụ :
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)
Lý do tôi chuyển thông số cho các chức năng cụ thể cho chương trình bậc cao hơn; cụ thể, bằng cách xác định một nhà điều hành ứng dụng à la Haskell, tôi có thể viết:
let() = with_output_file "foo.txt" $ fun otch ->
output_string otch "hello, world";
(* ... *)
với cú pháp không quá nặng. Đối với một ví dụ hơi tham gia nhiều hơn, hãy xem xét những điều sau đây:
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)
mà có thể được sử dụng với một cuộc gọi như with_open_graph " 400x300" $ fun width height -> (*...*)
.
Rất đẹp! Một chút sang một bên: Tôi giả định '$' được định nghĩa là 'let ($) f x = f x' nhưng điều này sẽ làm cho nó trái kết hợp trong khi nó là kết hợp đúng trong Haskell. Do đó 'print_int $ (+) 3 $ 4' không hoạt động trong OCaml. Đối với một toán tử liên kết thích hợp, một toán tử có thể định nghĩa 'let (@@) f x = f x'. –
Nếu 'bảo vệ' đặt ra một ngoại lệ, nó có được thực thi hai lần không? Vì ngoại lệ bị bắt và 'bảo vệ' được thực thi lại. Xem thêm [Yaron Minsky thực hiện 'bảo vệ'] (http://caml.inria.fr/pub/ml-archives/caml-list/2003/07/5ff669a9d2be35ec585b536e2e0fc7ca.en.html). –
@ChristianLindig: Có, mã ban đầu của tôi không xử lý các ngoại lệ kép. Xem chỉnh sửa của tôi cho một phiên bản "chức năng" mà tôi nghĩ rằng cải thiện trên Yaron của. –