タプルは、ほかのタプルを継承できます。TUPLE: child < parent slots ;は、インスタンスがparentのインスタンスでもあるクラスを宣言します。そのインスタンスは、自分自身のスロットに加えて親のスロットも持ち、親のアクセサもそのまま使えます。ちょうど、子で定義されているかのようにです。
USING: accessors ;
TUPLE: animal name age ;
TUPLE: dog < animal breed ;
"Rex" 4 "labrador" dog boa .
! => T{ dog { name "Rex" } { age 4 } { breed "labrador" } }
"Rex" 4 "labrador" dog boa name>> . ! => "Rex" (inherited accessor)
boaのスロットの順序boaコンストラクターが受け取るスロットの順序は、宣言順で、親が先です。上のdogなら、次のようになります。
dog boa ( name age breed -- dog )
孫にあたるpuppy < dog にweeksを加えると、次のようになります。
puppy boa ( name age breed weeks -- puppy )
TUPLE:を宣言するたびに、述語(name?)が生成されます。継承を使うと、述語は入れ子になります。すべての子孫は、祖先の述語を満たします。
"Rex" 4 "labrador" dog boa
dup dog? . ! => t
dup animal? . ! => t
逆は成り立ちません。dogでないanimalは、dog?を満たしません。
ジェネリックメソッド(M:。genericsで学びます)も、階層をさかのぼってディスパッチします。animalに対するメソッドは、より具体的なオーバーライドが優先されないかぎり、すべての子孫で呼び出されます。この動きは、この演習のすぐあとにあるboatswains-bilgeで目にすることになります。
ある博物学者が、カルデラの周辺に住むドラゴンたちを分類しています。ドラゴンたちには共通の特徴があります。どのドラゴンにも名前、色、年齢があり、それぞれが独自の方法で特殊化しています。ここでは、ドラゴンのクラスの小さな継承ツリーと、どんな子孫でも年を取らせる1つのワードを作ります。
タプルクラスの継承ツリー全体を最初に定義します(それぞれのコンストラクターは、このあとのタスクで登場します)。
name、color、ageを持つdragon。dragonのサブクラスで、スロットflame-tempを追加したfire-dragon。dragonのもう1つのサブクラスで、スロットchill-tempを追加したice-dragon。fire-dragonのサブクラスで、スロットlava-volumeを追加したvolcano-dragon。次に、dragonに<dragon>コンストラクターを定義します。
"Falkor" "white" 100 <dragon> name>> . ! => "Falkor"
fire-dragonに<fire-dragon>コンストラクターを定義します。
"Smaug" "red" 500 1200 <fire-dragon> flame-temp>> . ! => 1200
"Smaug" "red" 500 1200 <fire-dragon> dragon? . ! => t
ice-dragonに<ice-dragon>コンストラクターを定義します。
"Frosty" "blue" 200 -40 <ice-dragon> chill-temp>> . ! => -40
"Frosty" "blue" 200 -40 <ice-dragon> fire-dragon? . ! => f (siblings)
volcano-dragonに<volcano-dragon>コンストラクターを定義します。
"Vesuvius" "red" 1000 1500 99 <volcano-dragon> dragon? . ! => t
"Vesuvius" "red" 1000 1500 99 <volcano-dragon> fire-dragon? . ! => t
age-dragonを定義して、どんなドラゴン(またはその子孫)のageスロットも1つ増やします。戻り値はありません。
"Falkor" "white" 100 <dragon> dup age-dragon age>> . ! => 101
同じワードがどの子孫でも動くはずです。これこそが、共通のアクセサーを使う目的です。