Risa (吉里吉里3コアのこと) のファイルシステムは、相当昔のエントリに書いたように、/ から始まる単一のディレクトリツリーの任意のポイントにファイルシステムをマウントできるようになっています。
ファイルシステムの種類としては、いわゆる RAM ディスクに相当する TmpFS やホスト OS のファイルシステムにアクセスするための OSFS、あるいは XP4 アーカイブファイルシステムなどがあります。
で、これらのファイルシステムはいまのところ Risse でも実装できるようになっています。といっても現状組み込みになっているファイルシステムはすべて C++ で記述されていますが、すべて Risse のインターフェースを通すようになっています。
ファイルシステムごとに実装の違うであろう、ファイルストリーム(ファイルへのアクセスを抽象化したもの) も Risse で記述できるようになっています。
というわけでファイルシステムをスクリプトで記述できるので、非常に柔軟に実装できるようになっているわけですが、あんまり有用な用途が思い浮かびません。データベースやCSVファイルのようなものをファイルシステムにマッピングするような用途や、SQLiteバインディングを利用した信頼性の高いファイルシステムの実装などはおもしろそうですが、他はどうにも。
たぶんファイルという枠にとらわれずにオブジェクトの永続化をシームレスに行うことができるオブジェクトデータベース(たとえば近い物としてはZopeのZODBなど)を実装した方がいろいろと便利な面もありそうだと思うことはよくあります。しかし、現状私たちの制作環境があまりにも「ファイル」という単位に束縛されている以上、それらとの連携を考えるならば、おそらくは、現状慣れしたんだファイルという概念をそのままの形で実装することが最優先だろうなと思っています。
Risse でファイルシステムを実装できるようになったのは Risse のマルチスレッド対応の恩恵です (ファイルシステムはいろいろなスレッドからアクセスされるところなので)。Risse 開発初期ではネイティブスレッド対応はやらない事にしていましたし、スクリプト言語でネイティブスレッドに対応することについては否定的な考え方を持っていましたが、今となっては対応できたらできたでいろいろと楽しみです。
あと、Risse を使わずに、汎用指向ならば Python や Ruby 、組み込み指向ならば Lua や Squirrel など、既存のスクリプト言語を使ってもよかったじゃん、Risseの言語仕様なんて他の言語の劣化コピーだし、と言われるとまったくその通りだったわけですが、Risse はネイティブスレッドに対応しているから、というのは、Risse を開発した割と大きな言い訳の一つになりそうです。後づけの理由ですが。
Risseには似たような演算子として :: と . があります。仮に前者をスコープ解決演算子、後者をメンバ選択演算子と呼ぶことにします。
オブジェクトのメンバを選択するという意味ではどちらも同じなのですが、使う場面としては大きく違います。
最近この演算子の動作を変えました。
以前のエントリで書いたとおり、以前の Risse では :: と . の演算子は、取得したメソッドなりプロパティなりのコンテキストをどのように束縛するかの違いしかありませんでした。:: は呼び出し元の this に束縛し、 . は . 演算子の左側のオブジェクトと束縛します。
ことの発端は toString() というメソッドの扱いでした。
たとえば Integer クラスに対して
Integer.toString()
とすれば、クラス名を文字列化した "Integer" が返るべきでしょう。Integer クラスのインスタンス、たとえば 6 に対して
6 .toString()
とすれば、6 が文字列化された "6" が返るべきでしょう。
このときの toString() には、以前の Risse では Integer.toString でも Integer::toString でもアクセスすることができましたが、どちらも同じ toString を表してしまい、どっちの意味の toString() にアクセスしたいのかを指定することができませんでした。
これは Class クラスのインスタンスとしての Integer と、Integer のインスタンス (たとえば 6 など)から見える「クラス」としての Integer の性質が一つの Integer の中に混在しているために起こる問題で、これらをなんとか分離しなければということになりました。
これにいまさら気づくなんて。いやどうも TJS2 の、よく言って「単純でフラット」な、悪く言って「いい加減な」オブジェクトモデルが抜けきれない。
いまの Risse では、 Integer.toString とした場合は、Class クラスのインスタンスとしての性質の方にアクセスできます。つまり
Integer.toString()
とした場合は "Integer" が返ります。逆に
Integer::toString
とした場合は、Integer のインスタンスから見えるメソッドとしての性質のほうにアクセスできます。
たとえば Integer::toString() とすると、といってもコンテキストがこれでは特定できないので、たとえば
(Integer::toString incontextof 6)()
とすると、"6" が返ります。
つまり、クラスに対して、演算子の左側を Class クラスのインスタンスとして扱いたい場合は . を使い、演算子の左側をクラスとして見なし、そのクラスに属するメンバにアクセスしたい場合は :: 演算子を使うという使い分けになります。取得したメソッドなりプロパティなりのコンテキストの違いは以前のままです。
スーパークラスの initialize を呼び出すとき、以前は super::initialize() としていましたが、これは変わりありません。 super に属するメンバにアクセスしたいのですから、:: 演算子を使います。
いままで たとえば hoge() と書くと、hoge がローカル変数などでないかぎり、暗黙の this がこれにつけられて this.hoge() と解釈されていました。また、たとえば
function fuga() { ... }
とかけば、
var this.fuga = function () { ... }
と解釈されていました。
これはこれからは this::hoge() あるいは this::fuga となります。
this がもしクラスインスタンスではない場合は、 :: と . 演算子の違いは前からあるコンテキストの束縛の違いだけですから、this. を this:: と書いても動作に違いはありません。
this がもしクラスインスタンスの時は、this:: が使われるので、クラスに属するメンバにアクセスすることになります。特に class { ... } 内での this は クラスインスタンスになりますから、そこで
var this::fuga = function () { ... }
が実行されることにより、そのクラスに属するメソッドが追加されるというのは理にかなっています。
内部的には、Class クラスのインスタンスはすべて members というメンバをもっていて、この中に、そのクラスが提供するメソッドなどが入っています。 :: 演算子を使ったときは、この members に実際の要求がリダイレクトされる仕組みになっています。このため、たとえば Integer::toString は Integer.members::toString と同じ意味になります。
えーとまぁ、長々と書きましたが、要するに :: と . 演算子の使い分けに注意しようということです。クラスインスタンスそのものの性質にアクセスしたいという場合はまれでしょうから、普段は、クラスが左側に来るときには :: を、クラスのインスタンスが左側に来るときには . を使うと覚えておけばOKかと思います。