pick ADT
styxlibの話に入る前にpickについて書いておく。メッセージパッシングのプログラムでは、メッセージをあるフォーマットのデータ構造にpack/unpackする必要があるが、pickはそんなときに便利な機能だ。Cでは、構造体の先頭にメッセージの種類(or タグ)を格納しておいて、その後にunionを使って、種類ごとに異なるデータ構造が続く。LimboにはCのunionのようなpickという構文がある。以下では説明のために、一つのpickと関数からなるApick ADTを宣言する*1。Apickの各タグ(Stirng、Int、Real)は一つのメンバしかもっていないが、もちろん複数のメンバを持つことができる。
Apick : adt { pick { String => val: string; Int => val: int; Real => val: real; } printeval : fn(this : self ref Apick); };
ここでpickへの書き込みは次の通り。
r := ref Apick.Real; r.val = 3.14; s := ref Apick.String("Hello, World!"); # s := ref Apick.String; s.val = "Hello, World!"でもOK
関連して、LimboにはCにはないtagof演算子がある。これは与えられたリファレンスがpick ADTの何番目のタグか返す。上の例では"tagof s"は0、"tagof r"は2となる。tagofを使って、Apickリファレンスの型名を返す関数を定義してみる。
tag(t : ref Apick): string { r: string; case tagof t { tagof Apick.String => r = "String"; tagof Apick.Int => r = "Int"; tagof Apick.Real => r = "Real"; } return r; }
最期にpick文を使った、printval関数を見てみよう。pick文はcaseに似ているが、ラベルとしてpickの要素を指定する。
Apick.printval(this: self ref Apick) { pick t := this { String => sys->print ("%s: %s\n", tag(t), t.val); Int => sys->print ("%s: %d\n", tag(t), t.val); Real => sys->print ("%s: %f\n", tag(t), t.val); } }
*1:説明のコードはInfernoのマニュアルから。