--- breaks: false tags: public-tech --- # バリアントのコンストラクタの引数を括弧で囲むと便利 この記事は [OCaml Tips Advent Calendar 2022](https://adventar.org/calendars/8396) の10日目です。 OCaml のバリアントで、複数の引数をとるコンストラクタを定義する際に、 括弧で囲む場合と囲まない場合がありえます。 ```ocaml= type foo = | A of int * int (* 括弧で囲まない *) | B of (int * int) (* 括弧で囲む *) ``` 大抵の場合どちらも期待通りに動作しますが、 コンストラクタの引数にタプルを渡そうとした場合に挙動が変わります。 ```ocaml= # let t = (1, 2);; val t : int * int = (1, 2) # A t;; (* => コンパイルエラー! *) Error: The constructor A expects 2 argument(s), but is applied here to 1 argument(s) # B t;; (* => コンパイル可能 *) - : foo = B (1, 2) ``` `A` は 2 つの引数を受け取るコンストラクタ、`B` は 1 つの(タプルの)引数を受け取る コンストラクタとして定義されているためだと考えられます。が、詳細はよく分かっていません。 とりあえず、特別な理由がなければ、括弧でくくって定義しておくとよさそうです。 **追記(2022/12/10)**:メモリ上のエンコーディングが違うことに起因すると教えてもらいました([1](https://twitter.com/zehnpaard/status/1601416494373818368), [2](https://twitter.com/blackenedgold/status/1601412242251321344))。ありがとうございます。OCaml では、多くのデータ構造を、固定サイズのブロックが並んだものとしてメモリ上で表現することになって[います](https://dev.realworldocaml.org/runtime-memory-layout.html)。2 引数コンストラクタでは 2 つのブロックがつながったものとして値が表現される一方、タプルを受け取る 1 引数コンストラクタでは、引数のタプルへのポインタが格納されたブロックを使って値が表現されます。そうすると、後者ではタプルの内容を参照するために一回余計に間接参照が必要になるので、その分オーバーヘッドがかかってしまうということでした。