10

OCamlはJavaのような句try .. withを提供していません。finallyただし、特に副作用を処理する場合は便利です。たとえば、ファイルを開き、開いたファイルを関数に渡して、それを閉じるのが好きです。関数が例外を発生させた場合、ファイルを閉じる機会を得るためにそれをキャッチする必要があります。複数のファイルを開くと、これはますます複雑になり、それ自体を開くことも失敗する可能性があります。これに対処するための確立されたプログラミングパターンはありますか?

以下は、問題を説明する簡単な関数です。aが提供されている場合、または提供されていない場合、関数fはファイルに属するチャネルに適用されます。finally節がないため、2回出現します。pathstdinclose_in io

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
4

7 に答える 7

8

これに対処するための確立されたプログラミングパターンはありますか?

はい、リソースのクリーンアップを例外処理から切り離すラッパー関数です。私がしていることは、一般的なラッパー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 -> (*...*)

于 2012-06-30T22:31:57.197 に答える
4

OCaml 4.08以降、Fun.protectこの機能を標準ライブラリに提供する関数があります。

これを使用すると、例は次のようになります。

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
于 2021-05-06T22:24:46.340 に答える
3

Generalitiesの章のXavierLeroyとDidierRémyによるOCamlでのUnixシステムプログラミングの本から

「組み込みのfinalize構文はありません…OCaml言語でfinalizeしますが、簡単に定義できます:」

let try_finalize f x finally y =
  let res = try f x with exn -> finally y; raise exn in
  finally y;
  res
于 2013-04-06T21:45:16.347 に答える
2

私の知る限り、OCamlには組み込まれていませんが、そのようなパターンをエンコードできるライブラリを作成することはできます。ライブラリの例は、可能であればキャッチミーです。これはエラーのモナディックエンコーディングです。これは、メタプログラミングを使用して構成を追加するためのチュートリアルですfinallyおそらく他のアプローチもあります。

于 2012-06-30T19:31:43.023 に答える
1

ここにオプションがあります。path(コードを最小限の例にまとめるために、一致するものを削除しました。)

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 -> ()
于 2012-06-30T20:15:51.630 に答える
1

OCaml Batteriesライブラリコレクションは、最終的に使用できる2つの関数を提供します

http://ocaml-batteries-team.github.io/batteries-included/hdoc/BatPervasives.html

val finally : (unit -> unit) -> ('a -> 'b) -> 'a -> 'b

finally fend f x呼び出しf x、そして例外が発生したfend()場合でも。f x

val with_dispose : dispose:('a -> unit) -> ('a -> 'b) -> 'a -> 'b

with_dispose dispose f xfonxを呼び出し、dispose x終了時に呼び出しfます(戻り値または例外のいずれかを使用)。

于 2013-11-05T12:23:16.267 に答える
1

関数とfinallyブロックの両方で例外が発生した場合、最初の例外を確認したいので、次のようなものを使用します。

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 *)
于 2013-11-05T15:44:12.220 に答える