※この記事は 2018年01月12日 に Qiita に投稿したものです。
※Reason 向けの内容ですが、 OCaml とも共通します。
Reason で書かれたコードを見ていると、ところどころで type t
という一文字の型名を目にします。例えばこういうの:
module Synthetic = {
type tag;
type t = synthetic(tag);
...
}
これは 1モジュール1データ型主義 という OCaml の一慣習から来ています(いると思われます)。
Reason のドキュメントにレコード定義の一例があります。
type person = {age: int, name: string};
type monster = {age: int, hasTentacles: bool};
どちらのレコードも age
という同名のフィールドを持ちます。 Reason のレコードは名前空間が独立しておらず共有されるため、型推論時の person
と monster
の区別はフィールド名の組み合わせで判断されます。レコードの生成時に name
フィールドがあれば person
型、 hasTentacles
フィールドがあれば monster
型と判断されます(もちろん明示的にレコード型を指定することもできます)。
/* person */
let me = {age:100, name:"me"};
/* monster */
let me = {age:100, hasTentacles:true};
では、まったく同じフィールドを持つレコード型があったらどうでしょうか。
type person = {age:int, name:string};
type monster = {age:int, name:string};
/* どっちのレコード? */
let me = {age:100, name:"me"};
特に型の指定がなければ、フィールド名では型を判断できません。このような場合、 Reason では最後に定義された型とされます。 me
は monster
型です。
ですので、次のように person
を引数に受け取る関数に me
を渡すと型エラーになります。
let me = {age: 100, name: "me"};
/* person 型の引数を取るだけで何もしない関数 */
let f = (who: person) => ();
f(me);
コンパイルエラー:
13 │ let f = (who: person) => ();
14 │
15 │ f(me);
This has type:
monster
But somewhere wanted:
person
me
は monster
型だと判断されました。この場合は me
の型を person
と明示的に指定すれば大丈夫です。
let me: person = {age: 100, name: "me"};
OCaml だとフィールド名が重複すればコンパイル時に警告されます。 Reason の唯一の工夫のように見えますが、かえって落とし穴ができてしまった気もします。
ただし、既存のレコードとフィールド名が重複していると警告 42 で注意されます。 (デフォルトの警告の設定では無効です)
9 │ };
10 │
11 │ let me:person = {age: 100, name: "me"}; // ``age`` の部分がハイライトされる
12 │
13 │ let f = (who: person) => ();
this use of age required disambiguation
ここで元の問題に戻ります。以上の通りレコードは名前空間にならないので、フィールド名が重複する複数のレコードを定義する場合は、フィールド名に適当な接頭辞をつければ問題は回避できます。でも person_age
や monster_age
といちいちやるのはあまりかっこよろしくない。だからそれぞれのレコード型に専用のモジュールを用意すればフィールド名の重複を気にかけなくていい:
module Person = {
type t = {
age: int,
name: string
};
};
module Monster = {
type t = {
age: int,
name: string
};
};
このときに使う型名が t
です("type" の "t" かな?)。かつてのレコード person
と monster
はそれぞれ Person.t
と Monster.t
として定義されます。 t
とつく型は、そのモジュールの中心的なデータ型だとわかります。インターフェースファイルである .rei
と t
を探せば、 API のだいたいの意味が推測できます。
以上です。 Reason は OCaml を丸ごと新しい文法で覆っただけなので、 OCaml の不便な点も、不便を解消するためのイディオムもそのまま引きずっています。改善すべき点は文法以外にもあるんじゃないですかね...
Top comments (0)