Chapter 15. ボックス化されたタプルとレコード

ボックス化 されたタプル/レコードは、フラットなタプル/レコードが保管されたメモリ位置への単なる参照です。 しばしばフラットなタプル/レコードを表現するのに、アンボックス化 という用語も使われます。

次のコードでは、型 abc_tupabc_rec はそれぞれボックス化タプルとボックス化レコードを表わしています:

typedef abc_tup = '(int, int, string) // for tuples typedef abc_rec = '{a=int, b=int, c=string} // for records

単にタプルは、n がタプルの長さであるとき、タプルのラベルが 0 から n-1 の範囲固定であるような特殊なレコードであることに注意してください。 abc_tup の場合、それに関連した3つのラベルは 0, 1, そして 2 です。 '('{ は ATS における特殊なシンボルであり、シングルクオートとの間にスペースをはさめないことに注意してください。 このような特殊なシンボルの使用を避ける場合、型 abc_tupabc_rec を次のような少し異なる構文で宣言できます:

typedef abc_tup = $tup(int, int, string) // for tuples typedef abc_rec = $rec{a=int, b=int, c=string} // for records

また、キーワード $tup$rec はそれぞれ $tuple$record で置き換えできます。

次のコードはタプルとレコード生成し、それらの要素 (すなわちフィールド) を取り出す方法を示しています:

// val x_tup = '(0, 1, "2") : abc_tup val x_rec = '{a=0, b=1, c="2"} : abc_rec // val ((*void*)) = assertloc(x_tup.0 = x_rec.a) val ((*void*)) = assertloc(x_tup.1 = x_rec.b) val ((*void*)) = assertloc(x_tup.2 = x_rec.c) //

x_tupx_rec は両方とも不変であることには注意すべきでしょう。 もし次のコードを型検査すると、x_tup.0x_rec.a が左辺値ではないというエラーになります:

// val () = x_tup.0 := 100 // *ERROR*: x_tup.0 not a left-value val () = x_rec.a := 100 // *ERROR*: x_tup.0 not a left-value //

可変フィールドのタプル/レコードを得るためには、フラットなタプル/レコードへの参照を作るだけです。 次のコードでは、フラットなタプルとレコードを表わす型 abc_tup_abc_rec_ をそれぞれ宣言しています:

// typedef abc_tup_ = @(int, int, string) // for tuples typedef abc_rec_ = @{a=int, b=int, c=string} // for records //

そして、型 abc_tup_rabc_rec_r はそれぞれ abc_tup_abc_rec_ で分類されたタプルとレコードへの参照を表わします:

// typedef abc_tup_r = ref(abc_tup_) // for tuples typedef abc_rec_r = ref(abc_rec_) // for records //

次のコードは、可変フィールドのタプルとレコードを生成し、それらのフィールドにアクセス/更新する方法を示しています。

// val x_tup_r = ref<abc_tup_>(@(0, 1, "2")) val x_rec_r = ref<abc_rec_>(@{a=0, b=1, c="2"}) // (* ****** ****** *) // val () = assertloc(x_tup_r->0 = x_rec_r->a) val () = assertloc(x_tup_r->1 = x_rec_r->b) val () = assertloc(x_tup_r->2 = x_rec_r->c) // (* ****** ****** *) // val () = x_tup_r->0 := 100 // *OKAY*: x_tup_r.0 is a left-value val () = x_rec_r->a := 100 // *OKAY*: x_rec_r.a is a left-value //

あるフィールドが読み出しのみで他のフィールドは更新できるような型のレコードが欲しいなら、ATS の抽象型のサポートを使うことで実現できます。 次の例では、myrec は抽象的に宣言されています; myrec に関連した3つのフィールドがあり、それらは名前 a, b, c です; 最初の2つは更新可能であるが、3つめは読み出ししかできません:

(* ****** ****** *) abstype myrec = ptr (* ****** ****** *) // extern fun{} myrec_make ( a: int, b: int, c: string ) : myrec // end-of-function // extern fun{} myrec_get_a : myrec -> int extern fun{} myrec_set_a : (myrec, int) -> void extern fun{} myrec_get_b : myrec -> int extern fun{} myrec_set_b : (myrec, int) -> void extern fun{} myrec_get_c : myrec -> string // overload .a with myrec_get_a overload .a with myrec_set_a overload .b with myrec_get_b overload .b with myrec_set_b overload .c with myrec_get_c // (* ****** ****** *) local // assume myrec = abc_rec_r // in (* in-of-local *) // implement {}(*tmp*) myrec_make (a, b, c) = ref(@{a=a, b=b, c=c}) // implement{} myrec_get_a(x) = x->a implement{} myrec_set_a(x, a) = x->a := a // implement{} myrec_get_b(x) = x->b implement{} myrec_set_b(x, b) = x->b := b // implement{} myrec_get_c(x) = x->c (* // // there is no update for the c-field: // implement{} myrec_set_a(x, c) = x->c := c *) // end // end of [local] (* ****** ****** *)

次のコードは型 myrec のレコードを生成し、生成されたレコードのいくつかのフィールドをアクセス/更新しています:

// val x_abc = myrec_make(0, 1, "2") // val ((*void*)) = assertloc(x_abc.a() = 0) val ((*void*)) = assertloc(x_abc.b() = 1) val ((*void*)) = assertloc(x_abc.c() = "2") // val ((*void*)) = x_abc.a(100) val ((*void*)) = assertloc(x_abc.a() = 100) // val ((*void*)) = x_abc.b(101) val ((*void*)) = assertloc(x_abc.b() = 101) // (* val ((*void*)) = x_abc.c("102") // *ERROR*: unsupported *) //

この例 (myrec) は読み出しのみのフィールドと更新可能なフィールドの両方を含むレコードをコンストラクトするアプローチを示していますが、このアプローチは少し冗長です。 1つの可能性としては、このような冗長さを大きく減少させるために (ボイラープレートコードのため) メタプログラミングの機能を開発することです。

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