名前空間

なでしこには、名前空間[*1]が実装されているため、きちんとプログラムを書けば名前の衝突回避に役立ちます。また、関数のオーバーライドなども可能になります。

# 違う名前空間上で新たに関数を定義することにより、
# 関数のオーバーライドを実現
●起動({文字列=?}PATHを)
 PATHを表示
 PATHをシステム:起動

ところが、その名前空間をプログラムの途中で変えるためにあると思われる「ネームスペース変更」命令が上手く動いていないというバグ報告 (@460) がありました。

Aとは整数=1
Aを表示。 #=> 1
!『test.nako』を取り込む。# こちらではA=2が宣言されている
test:Aを表示する。 #=> 2 (名前空間を明示したので)
『test』にネームスペース変更。# ここでエラー!
Aを表示する。 #=> 2 (…となって欲しい)

さて、困りました。プログラムを構造化するためにソースファイルを細分化して「取り込む」命令を活用する僕としては、当然直して欲しいバグです。

「ネームスペース変更」命令

今回は、調査過程は飛ばして、バグ調査で明らかになった結論の方から行きましょう。

まず結論として、通常の実行プロセスで「ネームスペース変更」命令を実行しても意味がありません。なぜならば、なでしこは最初にソースコードを解析した「構文木」を作り、それを元にプログラムを実行しているからです。構文木を最初に一旦作ってしまう、というのはつまり、変数の型や呼び出すべき関数などがプログラムを実行する前に既に全て明らかになっているということです。そしてもちろん、構文木における個々の変数や関数の属する名前空間も、この時点で既に決定されてしまっています[*2]

ゆえに、そもそも通常の実行プロセスで名前空間を変更できても何も意味がないのです。じゃぁどうすんねん、という感じですが、実はちゃんと「ネームスペース変更」はできます。プリプロセス段階で「ネームスペース変更」命令を呼び出せばいいのです。

Aとは整数=1
Aを表示。
!『test.nako』を取り込む。# こちらではA=2が宣言されている
!『test』にネームスペース変更。
Aを表示する。 #=> 2
!『システム』にネームスペース変更。
Aを表示する。 #=> 1

不可解な実装

じゃぁ正しい使い方も分かったし、解決かと思いきや、そうではありません。

そもそもこのバグが報告されたのは、「ネームスペース変更」命令が上手く動いていないからでした。では、上手く動いていなかったのは正しい使い方をしていなかったから?いいえ、違います。

実は、正常に動くプリプロセス段階の「ネームスペース変更」と、通常の実行プロセスで呼び出されるシステム命令「ネームスペース変更」は、別々に存在していたのです。今回バグ報告がなされたのは後者ですね。そして、マニュアルなどで命令の分類を確認する限り、そもそもプリプロセス段階で実行する方の「ネームスペース変更」命令は存在すらしないのです(普通の命令としての「ネームスペース変更」に関する記述しか見当たらない)。

実際、ソースコードで定義を参照すると、プリプロセスでのみ呼び出し可能な「取り込む」命令と同列にあるべき「ネームスペース変更」命令はなく、普通のシステム命令としてしか定義されていません。そして上手く動くのはプリプロセスの方であり、普通の命令の方はエラーで実行できない。うーむ、不可解です。

ちなみに、システム命令の方は名前空間を探すための ID の指定が誤まっているため、エラーが出てしまっています。一方、プリプロセスの方はきちんと実装されているので、予約語登録がされていないだけで正常に動きます。

予想ですが、プリプロセスの「ネームスペース変更」命令は実装したものの予約語登録するのを忘れていたのでしょう。普通の命令の方は、命令一覧にリストアップされてないので、誤まって実装してしまったのかもしれません。

余談: 通常版でコンパイルするための設定

ついでに、今回勉強したことを一つ。プロジェクトのコンパイルを通常版と DELUX 版で切り替える方法について。

実は GoogleCode から SVN チェックアウトしたなでしこのプロジェクトのデフォルト設定では、なでしこを DELUX 版としてコンパイルするようになっています。そのため DELUX 版をコンパイルするのに必要なユニットがないと、そのままではコンパイルできません。

で、通常版でコンパイルできるようにする方法。コンパイルしたいプロジェクトのオプション (Shifr+Ctrl+F11) を開き、ディレクトリ/条件の条件定義(&C)の項の DELUX_VERSION を削除します。それだけです。ちなみにこの設定が必要なのは dnako, vnako, gnako, cnako です(多分)。

単に (r213) のコミット時にうっかり DELUX 版の設定のままコミットしてしまっただけ、のような感じもする f(^^;

関連・参考リンク

注釈

*1
マニュアルを見る限りでは、"名前空間"ではなく"ネームスペース"という表現で一貫されていますが、ここでは単純に好みの問題で"名前空間"という表記に統一しています。一応命令の解説の方では"名前空間"という表現も使っていますしね。(まぁその命令が全然使えなかった訳ですがf(^^;)
*2
型や参照、そして属する名前空間等が既に決定された状態だからこそ、単純なインタプリタに比べて実行速度が速くなります。もしこれらが決定されていなければ、その構文木を実行する度にそれらを調査せねばならず、オーバーヘッドがかさんでしまうでしょう。