メンバネームスペースの応用
ActionScript3では、名前空間の機能をpublic、privateなどの可触性に応用していました。Xtalでは可触性には応用しないのですが、「2項演算子」に応用します。
2項演算子は困り者
静的な型を持たない言語にとって、+や*などの2項演算子は困り者です。
a + bという演算を見てみましょう。
aがInt、bがIntの場合、結果はIntです。
しかし、aがInt、bがFloatの場合、結果はFloatにする必要があります。*1
つまり相手の型によって、演算を変えなければなりません。
これを単純に解決すると、次のようになります。
Int::op_add: method(other){ if(other is Int){ return /* Intを返す */ } else if(other is Float){ return /* Floatを返す */ } throw "unknown"; }
これは、将来Intと演算したい他の型が出てきたとき死にます。IntとFloat以外には対応できないコードです。
Xtalでの、従来の解決方法
Xtalでは、次のように解決していました。
Int::op_add: method(other){ return other.op_add_r_Int(this); } Int::op_add_r_Int: method(other){ return /* Intを返す */ } Float::op_add_r_Int: method(other){ return /* Floatを返す */ }
一言で説明すると、ダブルディスパッチをしていた、ということです。これなら、op_add_r_Intというメソッドを追加すれば、どんなクラスでも演算が可能となります。
Xtalのこれからの解決方法
これはこれでまぁいい方法だと思っていたのですが、今回追加したメンバネームスペースをこれに応用して、もっと楽にやることにしました。
a + bという演算は、a.op_add#(b.class)(b) という意味となり、次のようなコードで各型との2項演算子の定義が出来るようになります。
// Intとの演算 Int::op_add#Int: method(other){ return /* Intを返す*/ } // Floatとの演算 Int::op_add#Float: method(other){ return /* Floatを返す*/ } // 2次元ベクトル Vector2D::op_mul#Float: method(other){ return Vector3D(this.x*other, this.y*other); }