Standard MLのwithtypeの挙動

Standard MLにはtype ... and ...という構文があり、複数の型シノニムを同時に定義することができます。たとえば、次のコードは型シノニムabを定義します。

type a = int -> bool
and b = real * int

この例では、int -> boolreal * intを「解釈」した後にabを束縛します。

type ... and ...でしか出来ないことは「一時的な型識別子を導入することなく、複数の型識別子を入れ替える」ことです。次のコード例では、コンストラクタAを持つdatatype aと、コンストラクタBを持つdatatype bを定義した後に、型識別子abを入れ替えています。その結果、コンストラクタAは型bを、コンストラクタBは型aを持つようになります。

datatype a = A
datatype b = B

val _ : a = A
val _ : b = B

type a = b
and b = a

val _ : b = A
val _ : a = B

type ... and ...が無ければ、この例は次のように書き直されるでしょう。

datatype a = A
datatype b = B

local type a' = a in
  type a = b
  type b = a'
end

このように、localを使った方法では一時的な型識別子を導入する必要があり、また、その識別子はabとは異なるもの(ここではa')を選択しなければなりません。

しかしながら、このような型識別子の入れ替えは、あまり便利だとは思えません。type ... and ...が真に活躍するのは「複数の型定義の順番が重要ではないことを表す」ときでしょう。たとえば、次のコードは型シノニムwindowcursorを定義しています。

type window = {
  height : int,
  width : int
}

and cursor = {
  offset : int,
  active : bool
}

このとき、windowcursorの順番を入れ替えるのは簡単です。なぜなら、type ... and ...が「windowcursorの定義の順番が重要ではない」ことを示しているからです。

一方、もしこの例が連続したtype宣言で構成されていた場合、つまり次の例のとき、

type window = {
  height : int,
  width : int
}

type cursor = {
  offset : int,
  active : bool
}

windowの定義とcursorの定義を入れ替えるためには、windowの定義がcursorを含まないことと、cursorの定義がwindowを含まないことを確認する必要があります。

withtype

Standard MLにはwithtypeという機能があります。たとえば、次のように書かれます。

datatype 'a c = C of bool d -> 'a
withtype 'b d = ('b * int) c

このコードは、次のようなコードの糖衣構文です。

datatype 'a c = C of (bool * int) c -> 'a
type 'b d = ('b * int) c

withtype ... and ...について

Sample code

datatype s = C of t * u
withtype t = s -> s
and u = t * bool

SML/NJ

SML/NJでは通る。

Moscow ML

Moscow MLでは通らない。

! and u = t * bool
!         ^
! Unbound type constructor: t

sの定義から* uを取り除いても同じエラーが出ることから、脱糖後のtype t = s -> s and u = t * boolがDefinition通りにエラーになってることが分かります。

MLton

MLtonでは通らない。

Undefined type t.

MLKit

MLKitでも通らない。

  and u = t * bool
          ^
unbound type constructor t.

Alice ML

Alice MLでも通らない。

unknown type `t'

HaMLet

HaMLetでも通らない。

unknown type t

Poly/ML

Poly/MLでも通らない。

Type constructor (t) has not been declared

SML#

κeenさんによると、SML#でもDefinition通り、unbound type constructor or type alias: tというエラーがでるようです。(情報提供感謝)

他の処理系

TILTはインストールに失敗したので確認できませんでした。

付録: シグネチャ内でのwithtype

Definitionでは、withtypeletやストラクチャ内では使うことが出来るものの、シグネチャ内では使うことが出来ません。

SML/NJ, Poly/ML, Moscow ML, Alice MLではシグネチャ内でwithtypeを使うことができます。