Chapter 10. データ型

データ型は、データ (もしくは値) を分類するためのユーザ定義の型です。 ATS におけるデータ型とパターンマッチは ML に由来しています。

次のコードは、平日を示す値を表わす、名前 weekday のデータ型を宣言しています:

datatype weekday = | Monday | Tuesday | Wednesday | Thursday | Friday

ここでは weekday に関連する5つのデータコンストラクタがあり、それらは Monday, Tuesday, Wednesday, Thursday, Friday です。 これらのデータコンストラクタは全て無引数 (nullary) です。 すなわち、それらは (weekday 型の) 値を作るのに引数を取りません。

それぞれの無引数コンストラクタは (例えば 1024 より小さいような) 小さな整数として表現されます。 次の関数 weekday2int を使うと、weekday に関連するコンストラクタを表わす整数を調べることができます:

// staload UN = "prelude/SATS/unsafe.sats" // fun weekday2int (wd: weekday): int = $UN.cast{int}($UN.cast{intptr}(wd)) //

無引数コンストラクタを表わす小さな整数は、コンストラクタのタグとしばしば呼ばれます。 この場合、 Monday, Tuesday, Wednesday, Thursday, Friday に対するタグは、それぞれ 0, 1, 2, 3, 4 になります。

無引数コンストラクタ foo が与えられたとき、foo でコンストラクトされた値を参照するために、foofoo() の両方を使うことができます。 けれどもこの値にマッチするパターンとして使えるのは foo() のみです。 例えば次の関数は、与えられた型 weekday の値がコンストラクタ Friday で作られたかどうか確認しています:

fun isFriday(x: weekday): bool = case+ x of Friday() => true | _ => false

パターン Friday()Friday で置き換えられないことに注意してください。 パターンとして使われると後者は値として使われます。 一方で、次のアサートは実行時に扱われるので、FridayFriday() は同じ値を指します:

val () = assertloc (isFriday(Friday)) val () = assertloc (isFriday(Friday()))

データ型に関連したコンストラクタが1つだけの場合、このデータ型の値を表わすタグはありません。

データ型に関連する2つのデータコンストラクタとして一方は無引数 (nil-like) でもう一方が無引数でない (cons-like) があるなら、そのデータ型は list-like です。 例えば、次のように宣言されたデータ型 ab はリスト的です:

datatype ab = A of () | B of (bool)

list-like なデータ型の値は特別な方法で表現されます。 そのデータ型の値が与えられたとき; もし nil-like なコンストラクタでコンストラクトされていたら、それは NULL ポインタで表現されます; もし cons-like なコンストラクタでコンストラクトされていたら、それはそのコンストラクタに渡された引数全てを含む (ヒープに確保された) タプルで表現されます。 上記の場合、値 A() は NULL ポインタで表わされ、ブール b に対する値 B(b)b のみを含む (ヒープに確保された) シングルトンタプルとして表わされます。 この説明は次のコードで簡単に確認できます:

val () = assertloc (iseqz($UN.cast{ptr}(A()))) val () = assertloc (true = $UN.ptr0_get<bool>($UN.cast{ptr}(B(true)))) val () = assertloc (false = $UN.ptr0_get<bool>($UN.cast{ptr}(B(false))))

次の宣言は名前 abc のデータ型を導入しています:

datatype abc = | A of () | B of (bool) | C of (int, double)

abc に関連する3つのコンストラクタは A, B, C です; A は無引数です; B の引数は1つで、(abc 型の) 値を作るためにブールを取ります; C の引数は2つで、(abc 型の) 値を作るために整数と (double 制度の) 浮動小数点数を取ります。

一般に、なんらかの正の整数 n についてデータコンストラクタが n 個の引数を取るなら、コンストラクトされた値であるヒープに確保されたタプルは n+1 個の要素を持ちます。 このとき最初の1つはコンストラクタに割り当てられたタグで、残りはコンストラクタに渡された引数です。 例えば、次の関数を使うと abc に関連するコンストラクタに割り当てられたタグを調べることができます:

fun abc2tag (x: abc): int = let val p = $UN.cast{intptr}(x) in // case+ 0 of | _ when p < 1024 => $UN.cast{int}(p) | _ (*heap-allocated*) => $UN.ptr0_get<int>($UN.cast{ptr}(p)) // end // end of [abc2tag]

この場合、A, B, C に割り当てられたタグはそれぞれ 0, 1, 2 です。

データ型は再帰的に定義できます。 例えば、次の宣言は再帰的な定義された (算術整数式を表わす) データ型 intexp を導入しています:

datatype intexp = | Int of int | Neg of (intexp) | Add of (intexp, intexp) | Sub of (intexp, intexp)

例えば、(1+2)-3Sub(Add(Int(1), Int(2)), Int(3)) のように表現できます。 別の例として、次のコードは2つの相互再帰的に定義されたデータ型 intexpboolexp (それぞれ整数式とブール式を表わします) を導入しています:

datatype intexp = | Int of int | Neg of (intexp) | Add of (intexp, intexp) | Sub of (intexp, intexp) | IfThenElse of (boolexp, intexp, intexp) and boolexp = | Bool of bool // constant | Not of (boolexp) // negation | Less of (intexp, intexp) // Less(x, y): x < y | LessEq of (intexp, intexp) // LessEq(x, y): x <= y | Conj of (boolexp, boolexp) // Conj(x, y): x / y | Disj of (boolexp, boolexp) // Disj(x, y): x / y

以下コードは、整数式とブール式の評価を表わす2つの相互再帰関数 eval_intexpeval_boolexp それぞれ実装しています:

// symintr eval // extern fun eval_intexp : intexp -> int extern fun eval_boolexp : boolexp -> bool // overload eval with eval_intexp overload eval with eval_boolexp // (* ****** ****** *) // implement eval_intexp (e0) = ( // case+ e0 of | Int (i) => i | Neg (e) => ~eval(e) | Add (e1, e2) => eval(e1) + eval(e2) | Sub (e1, e2) => eval(e1) - eval(e2) | IfThenElse (e_test, e_then, e_else) => if eval(e_test) then eval(e_then) else eval(e_else) // ) (* end of [eval_intexp] *) // implement eval_boolexp (e0) = ( // case+ e0 of | Bool (b) => b | Not (e) => ~eval(e) | Less (e1, e2) => eval(e1) < eval(e2) | LessEq (e1, e2) => eval(e1) <= eval(e2) | Conj (e1, e2) => eval(e1) && eval(e2) | Disj (e1, e2) => eval(e1) || eval(e2) // ) (* end of [eval_boolexp] *) // (* ****** ****** *)

この章で紹介したデータ型は単純なデータ型です。 データ型のより進んだ形として、多相データ型, 依存データ型, 線形データ型 があります。 それらをどこかで紹介することになるでしょう。

この章で使ったコード全体とテストコードは オンライン から入手できます。