週刊mosh - 実際のアプリケーション / R7RS / 非同期I/O

ギャー全然週刊になってない。。諸般の事情というかmoshを使ったヘビーな開発が幾つか走っているのであまりコードを動かせないんです。。
もちろん通常のシチュエーションではブランチすれば良いんだけど、FASL互換性のない複数のmoshがインストールされていると事件が起こるので。

実際のアプリケーション

最近nmoshを方々にdeployする機会が多いんですが、トラブルのTop3は:

  • キャッシュファイル名が長すぎる
  • 必要なMSVCランタイムが導入されていない
  • Darwin上で突然死する

というもので、Darwinでの突然死がかなり難しい問題(手元では再現しない)。Win32でもGCをPARALLEL MARKにするとGCが終了しないケースが稀に観測されたり。
キャッシュファイル名問題は、キャッシュファイル名をフルパスで命名することによる問題。。
nmosh(やGuileのようなバイトコードをキャッシュするタイプのScheme)はキャッシュのファイル名を、"展開元のスクリプトのフルパスをエンコードしたもの"とするので、ファイルのフルパスがMAX_PATH文字まで長くなるとすると、キャッシュファイルへのパスはMAX_PATH+キャッシュディレクトリまでのパスの長さになり、OSの最大パス長制限を超えてしまうことがあります。
これを避けるにはPythonのようにバイトコードキャッシュをソースコードと同じディレクトリに置くか、フルパスを直接使うのをやめるようにする必要が。。
最近のnmoshは結構多種多様な使われ方をしていて、ドキュメントの分析や生成のようなイカにもScheme向けっぽい作業から、GUIアプリケーションのプロトタイプまで。。個人的にもnmoshは主にプロトタイピングや実験のためのツールとして活用してます。
もちろん、言語処理系として普及を目指すならAPIを安定させて"寿命の長い"アプリケーションも安心して書けるようにすべきなんですが、これがなかなか難しいことで。。

R7RS

R7RS対応はevalとモジュール構文、|によるシンボル表記以外はだいたい完了。psyntax-moshではバッファportがサポートされないのでnmoshをお勧めします。。現在のcurrentでは普通に(import (scheme base))のようにしてR7RS拡張のsyntax-rulesやcaseを使えます。
R7RS draft5は公開されましたが機能的にはdraft4とほぼ同一なのでdraft5向けの修正は有りません。
モジュール構文のサポートは準備中。R7RSはモジュール構文にサポートが難しい要素が追加されていて:

  • cond-expand

いわゆるSRFI-0がモジュール構文に取り込まれた。さらに、R7RSはbodyでもcond-expandが使える。
で、moshは一旦スクリプトVMバイトコードに変換し、それをキャッシュするため、cond-expandの状況が変化していないかどうかをキャッシュの監視対象に含める必要がある。
以外とわすれがちだけど、cond-expandはCPUやインストールされたライブラリ等の要素でも変動するので、一度キャッシュしたライブラリが、実行ホストの変更によって無効になる可能性はある。

  • include

R7RSはincludeを持っているのでカレントディレクトリのstatを省略できない
I/Oの数はスクリプトの起動時間に非常にクリティカルな影響を与えるので、nmoshでは幾つかのライブラリ名(srfi/rnrs/scheme/mosh/nmosh)を予約してしまって、ユーザにはoverrideできないようにすることで標準ライブラリのstatでかかる時間を削減しようとしていました。nmoshのR7RS実装ではこれに加えて標準ライブラリに対するinclude pathは追加できないというルールをつけようと考えてます。

非同期I/O

TCP経由の非同期I/Oはそれなりにmatureになってきていて、POSIX pollとWin32のIOCP実装は結構使っています。
非同期I/O APIを直接使うのは非常に面倒なので、0.2.8ではMessagePackのwrapperを提供します。これはTCP socketのsendやrecvのかわりに、Schemeオブジェクトを送受信するようになっていて、MessagePack/RPCなRubyスクリプト等と直接インターフェースできるので地味に便利。
非同期I/OのAPIを設計していて難しかったのはエラーの取り扱いで、結局、すべてのcallbackで最初の引数が偽だったら失敗というルールを導入しました。