何故Xtalは変数定義構文を持つ?
Xtalでは
変数名 : 初期値;
という書式で変数の定義が出来ます。スクリプト言語と呼ばれるモノの多くは、この明示的な変数の宣言の構文を持たず、最初の代入式がそれを兼ねるものが多い中、何故こういう方式を採用したのか、を説明します。
Xtalは {} が変数のスコープを形成します。
foo : 5; { foo : 10; println(foo); // => 10 } println(foo); // => 5
JavaScriptは{}で変数スコープが形成されないので、同様の書き方をすると二つ目のprintlnは10になってしまいます。JavaScriptで書いているときに微妙に戸惑う部分です。
さて、Xtalのように{}でスコープが形成できる言語で、変数の定義構文を持たず、最初の代入が定義を兼ねる、という方式だとどうなるでしょう?
foo = 5; { foo = 10; // さて、これは定義なのか、外側の変数への代入なのか? }
Rubyでもブロックで同様の混乱が(少なくとも私個人は)起こります。
foo = 5 100.times{ foo = 10 # 宣言?代入? }
Rubyでは、外側に既に同名のローカル変数があると代入になり、無い場合はそのブロック内だけで有効な変数となります。
変数スコープをあまり形成しないRuby等ではこれで大抵は上手くいきますが、Xtalでは無謀です。なぜなら、{}だけでなく、classやmoduleもローカル変数スコープを形成するからです。
Foo : class{ goo : method(){ // ここではgooもFooもBarもローカル変数としてアクセスできる } Bar : class{ hoge : method(){ // ここではhogeにもBarにもgooにもFooにもローカル変数としてアクセスできる } } }
この問題は、変数定義構文を持つだけで簡単に解決できます。「スクリプトは簡潔であるべきだ!」という声に答えるため、代入の'=' を コロン ':' に変えるだけ、という書式を採用しています。
ローカル変数参照ルール
上に関連した話です。
ローカル変数参照の解決は、次のルールによって決定されます。
- 外側のスコープに向かって、一番近い同名の変数を静的に探す。
- 静的に見つからない場合、toplevelオブジェクトのメンバから動的に検索される。
- toplevelに無い場合、例外が投げられる
以上です。つまり、thisを暗黙的な検索対象にはしません。
C++やRubyでは、メソッドの中では、this (Rubyではself)を暗黙的に検索するため、少々戸惑うかもしれません。
// C++ class Foo{ public: virtual void hoge(); virtual void bar(); }; void Foo::hoge(){} void Foo::bar(){ hoge(); // this->hoge(); }
// Xtal Foo : class{ hoge : method(){} foo : method(){ // ↓と書けるが、このhogeは静的にローカル変数として取得されている。 hoge(); } }
これにより、Fooが継承されてhogeがオーバーライドされたとしても、相変わらずfooメソッドの中ではFoo::hogeが呼ばれつづけることになります。デザインパターンで言う「テンプレートメソッド」にならないわけです。
これをテンプレートメソッドとして動作させたいのならば、this.hoge(); と書きます。これで動的にhogeが検索されます。
何故このような挙動になっているのでしょうか?いくつか理由があります。
- 実行速度が速くなる。
- 下底クラス実装者が予期していなかったメソッドのオーバーライドをされても大丈夫。
Rubyも上と同じような、self.をつけた時だけオーバーライド呼び出しとする、という挙動にするかどうか論議があったようです。
他の言語に言及しているところでは自分は勘違いをしている可能性があります。もし間違いがありましたら指摘していただけると嬉しいです。