二重キー
全然ネームスペースっぽくないので、メンバネームスペースという呼び方はやめて、二重キーと呼ぶことにします。
クラスメンバは、識別子(一次キー)と任意のオブジェクト(二次キー)で、二重のキー付けが出来ます。
二つ目のキーは、===演算子(同一のオブジェクトかを調べる演算子)により検索されます。
もし、一致するメンバが無く、二次キーがクラスオブジェクトの場合、そのスーパークラスを二次キーとして再検索が行われます。
それでも見つからない場合は、さらにそのスーパークラスを二次キーとして再検索が行われます。
二次キーを指定しない場合、二次キーにはnullが指定されたのと同じ意味となります。
AAA: class{} BBB: class(AAA){} Hoge: class{ // 識別子 foo とクラスオブジェクト AAA でメンバ定義 foo#AAA: "foo#AAA"; } Hoge::foo#AAA.p; // 一致するものがあるのでOK Hoge::foo#BBB.p; // BBBでは一致しないが、BBBのスーパークラスはAAAなので、OK Hoge::foo#Hoge.p; // HogeはAAAではなく、スーパークラスにもAAAはないので、NG Hoge::foo.p; // 二次キーはnullとなるが、Hoge::foo は無いので、NG
すべてのクラスの基底はAnyと決まっているため、二次キーとしてAnyを指定すれば、クラスオブジェクトを二次キーとして使う場合に、必ず検索されるメンバを定義することが出来ます。
// Hogeクラスに識別子 foo とクラスオブジェクトAnyでメンバ定義 Hoge::foo#Any: "foo#Any"; Hoge::foo#Hoge.p; // OK
コレが何の役に立つのか
他のライブラリと絶対被ることの無いメンバを、ビルトインクラスに追加することが可能となります。
// 二次キーを定義。ここではクラスオブジェクトを二次キーとして生成する。 secondary_key: class{} String::double#secondary_key: method{ return this ~ this; } "FOO!".double#secondary_key.p; //=> FOO!FOO!
他には、多重ディスパッチのようなことを簡単に記述できます。
多重ディスパッチとは
http://ja.wikipedia.org/wiki/%E5%A4%9A%E9%87%8D%E3%83%87%E3%82%A3%E3%82%B9%E3%83%91%E3%83%83%E3%83%81
上のwikipediaの「宇宙船や小惑星といったオブジェクトが出てくるゲーム」の例をXtalで書くと次のようになります。
Thing: class{} Asteroid: class(Thing){} // 小惑星。Thingを継承する Spaceship: class(Thing){} // 宇宙船。Thingを継承する // 二次キーが指定されていない場合に、引数のクラスを二次キーとしてcollide_withメソッドを呼びなおす。 Thing::collide_with: method(other){ return this.collide_with#(other.class)(other); } Asteroid::collide_with#Asteroid: method(other){ //小惑星が小惑星に衝突する場合を処理 } Asteroid::collide_with#Spaceship: method(other){ //小惑星が宇宙船に衝突する場合を処理 } Spaceship::collide_with#Asteroid: method(other){ //宇宙船が小惑星に衝突する場合を処理 } Spaceship::collide_with#Spaceship: method(other){ //宇宙船が宇宙船に衝突する場合を処理 } s: Spaceship(); a: Asteroid(); // Spaceship::collide_with#Asteroid が呼ばれる s.collide_with(a); // Spaceship::collide_with#Spaceship が呼ばれる s.collide_with(s);
宇宙船と惑星との衝突のようなメソッドは、単一ディスパッチのオブジェクト指向言語においてどうしたらよいのかわからず困った人が多いのではないかと思います。この機能はそれをある程度解決します。
また、a + b や a - b 等の2項演算子はすべてこの考えで定義されているので、簡単に特定の型との演算が可能となっています。