yuniport

mosh 0.3.0を目標にyuniportと呼んでいる機構を開発している。これは主にWindowの処理やpacket-portのために使われている。
yuniportの実態は(mosh concurrent)のwrapperで、(僕が研究しているSchemeであるところの)yuniSchemeに似た一般化ポートを提供する。
一般化ポートはheadを持ったkey-valueペアのタプルをやりとりする事が出来る。R6RSのportは文字列やbytevectorしかやりとりできないので、それに比べて、より一般化された機能を提供するということになる。
要するに、

(key-down :sym 41 :shift? #f)

のようなlistをwrite:(コロンが付くことに注意)することで、相手先は、

  • head = key-down
  • (key . value) = ( (sym . 41) (shift? . #f) )

のようなメッセージを受け取ることになる。
メッセージの分解は専用の構文でしか行えない*1。それがdispatch-loop:構文で、

(dispatch-loop: port
  ((key-down sym) (display sym)(newline)))

のようにすることで、メッセージをパースして受け取ることが出来る。shift?が要らないのなら、単純に、書かなければ良い。
この構文はGaucheにあるようなキーワード引数の構文に影響を受けている。当初(yuniで)はキーワード引数と区別するためにコロンを後置するスタイルにしていたが、統一した。
分解後のアクセスはコロンを伴わない。これはyuniの悪い習慣で、コロンやスラッシュを特別扱いしていて通常のアクセスでは別名を使う。スクリプト上に存在しないシンボルを使うのはあまり健全でない。(言い方を変えれば、write:に与えるリストはサブ言語でありSchemeでないという扱い。本来、これらのスロット名はdefine-packet-typeのような構文定義から抽出されるため、そもそもスクリプト中には現れない。)
現実的にはdispatchはもっと柔軟に記述できないといけない。yuniはdefine-dispatchとdispatch-continueを組み合わせることで、ディスパッチloopを切り替えることが出来る。

yuniportの構造

yuniportは贅沢にprocessを使用する。
通常は3つのprocessがyuniportに関わる。外部からのイベントを受け取り、yuniportシステムで処理するケースを考えると、

  • generator : Windowsからメッセージを受け取るprocess
  • dispatcher : generatorとconsumerの両方からメッセージを受け取り、メッセージを配送するprocess
  • consumer : dispatch-loop:のあるprocess

基本的に(mosh concurrent)のprocessはメッセージの受信でしか駆動できない。要するに、generatorは(mosh concurrent)のメッセージでは駆動していないので、generatorを終了するためのクリーンな手段は存在しないことになる(せめてメッセージのpeekが有れば*2。。ただし、異なるメッセージシステムを同時に使う以外でpeekが必要になることは無い。)
もっとも、通常のシチュエーションではsocketがクローズするとかWindowハンドルが無効になるといった、generatorが処理を諦める何らかのイベントが発生するので大きな問題ではない。ただし、ジェネリックなcloseは書けないことになる。

メリット

yuniportはコンパイラによるプロトコルの生成やコード合成による最適化を目的としてデザインしているので、mosh上では基本的にメリットはない((mosh concurrent)を直接使って書いた方が高速に実行できる)が、yuniportは複数のmoshプロセスを組み合わせないと行えないことを抽象化してより簡単に書けるようにする。

  • 選択的なreceive、mux

(mosh concurrent)のreceiveは自分宛のメッセージを受信する機能しかない。yuniportは送信元のportをラベリングして選択的に受信できる。
もっとも、選択的に受信するだけではあまり意味はなく、複数のポートを同時に待ち受けるマルチプレクスのほうがずっと重要と言える。マルチプレクスはまだ構文を規定していないので使えない。

  • リプライ(同期的オペレーション)

基本的にyuniportは一方通行となるが、同期的に処理をしたいというケースは少なからず存在する。
yuniportでは受信動作を能動的に行うことが出来るので、同期的な処理を入れる余地はある。今のところ適切なAPIが用意できていないが、これが書けるようになると対話的なプロトコルの実装に有利に働く。

  • ポートの移送

原理的には、yuniportは移送することができる。要するに、メッセージの送信先pidを変更するだけだが、pidオブジェクトそのものを健全に転送できるかどうかは未定義なので微妙(いまのところ出来ているように思える)。

*1:当初は出来るように考えていたが、yuniSchemeとの互換性のためにやめた。yuniSchemeはコンパイラによる最適化のために、当初からメッセージの構築とパーズは専用の構文でしか行えない。

*2: (mosh concurrent)には受信タイムアウトは有るがpeekは無い。通常のシチュエーションでは、peekのためにタイムアウト 0を使うのは正しくない(yieldするため)。