--- breaks: false tags: public-tech --- # 「先に定義した関数から後に定義した関数への呼び出し」を Object を使って実現する この記事は [OCaml Tips Advent Calendar 2022](https://adventar.org/calendars/8396) の12日目です。 OCaml の O は Object(ive) の O なので、OCaml にはオブジェクト(クラス)の ネイティブサポートがあります。Java や C++ のバックグラウンドがある人にとっては かなり素直にオブジェクト指向プログラミングを実現できます。 ```ocaml= class foo = object (self) (* インスタンス変数 x *) val mutable x = 0 (* メソッド f *) method f y = x + y (* 引数なしのメソッドも定義できる *) method get_x = x (* プライベートメソッド *) method private g y = (* メソッド f を呼び出す *) self#f y end class virtual bar = object (self) (* 仮想メソッド *) method virtual f : int -> int (* 普通のメソッド *) method g n = n + 1 (* f と g を呼び出す普通のメソッド *) method h n m = self#f n * self#g m end class baz = object (* bar を継承 *) inherit bar (* 仮想メソッドを実装 *) method f n = n * 2 (* 普通のメソッドをオーバーライド *) method! g n = n + 2 end let () = assert ((new baz)#h 3 4 = 36) ``` とはいえ、Java などと違って、OCaml ではクラス(オブジェクト)の使用は必須ではありません。 実際、実現したい多くのコードはレコードやファンクタを組み合わせることで実現できる 印象があり、オブジェクトはあまり使われない機能だと感じています[^ocaml-object-usage]。 実際私も、最近自分で使うまでは [RADWIMPS](https://github.com/approvers/RADWIMPS/blob/af2873ce81ba00d14085f1f59dcfddfebc8f4a43/RADWIMPS.ml)レポジトリで 見かけたことがあるくらいでした。 [^ocaml-object-usage]: 印象だけで特に統計データはありませんが、例えば「object はできるだけ使うな」と言っている StackOverflow の回答が[あります](https://stackoverflow.com/a/10780681)。この回答では逆にいつ使うべきかも議論していて、その中に(後述する)open recursion が含まれています。 ただし OCaml で「前に定義した関数から、後で定義した関数を呼び出したい」場合[^config]には object が便利です。というのも OCaml は lexical scope を 採用しているため、このような関数呼び出しを簡単には行えません。 そこで、あとから差し替えたい関数を使用する関数をメソッドとして定義したオブジェクトを 用意し、これを継承して、継承先で差し替える関数をオーバーライドすることで 「前に定義した関数から、後で定義した関数を呼び出し」ます。 ちなみに、こういう object の使われ方を open recursion と呼ぶそうです[^open-recursion-another-approach]。 [^config]: 個人的には、設定ファイルの実装を行うときに便利でした。デフォルトとして使われる設定ファイルをオブジェクトとして定義しておき、そこからの変更分を継承先でオーバーライドして実装しました。 ```ocaml= (* Object を使わない場合 *) let f () = "Hello" let g () = (* ここで呼び出される f はいつでも 1 つ目の f *) f () ^ " World" let f () = "Hey" let () = assert (g () = "Hello World") (* Object を使う場合 *) class base = object (self) method f = "Hello" method g = self#f ^ " World" (* 呼び出される f はオブジェクトによって異なる *) end class deriv = object inherit base method! f = "Hey" (* オーバーライド *) end let () = assert ((new deriv)#g = "Hey World") ``` [^open-recursion-another-approach]: ちなみに OCaml では object を使わなくても open recursion を実現できる[そうです](https://zehnpaard.hatenablog.com/entry/2021/03/09/120902)(が、object を使ったほうが楽っぽいです)。