DEV Community

SUZUKI Tetsuya
SUZUKI Tetsuya

Posted on

3 1

"type t" とは何か

※この記事は 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 のレコードは名前空間が独立しておらず共有されるため、型推論時の personmonster の区別はフィールド名の組み合わせで判断されます。レコードの生成時に 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 では最後に定義された型とされます。 memonster 型です。

ですので、次のように 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  

memonster 型だと判断されました。この場合は 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_agemonster_age といちいちやるのはあまりかっこよろしくない。だからそれぞれのレコード型に専用のモジュールを用意すればフィールド名の重複を気にかけなくていい:

module Person = {
  type t = {
    age: int,
    name: string
  };
};

module Monster = {
  type t = {
    age: int,
    name: string
  };
};

このときに使う型名が t です("type" の "t" かな?)。かつてのレコード personmonster はそれぞれ Person.tMonster.t として定義されます。 t とつく型は、そのモジュールの中心的なデータ型だとわかります。インターフェースファイルである .reit を探せば、 API のだいたいの意味が推測できます。

以上です。 Reason は OCaml を丸ごと新しい文法で覆っただけなので、 OCaml の不便な点も、不便を解消するためのイディオムもそのまま引きずっています。改善すべき点は文法以外にもあるんじゃないですかね...

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay