Rubyとの連携を考える(2)
私がRubyで書いているLisp方言、 [Nendo]の開発状況続き。
Rubyの世界へのアクセス手段として引きつづき .(ドット)オペレータを考えてみる。
リードマクロにこだわらずに考えてみる
要するに、LispからRubyに変換された時、instance.memberとかClass.memberという式に変換されるためにはどうなっていれば良いかだけを考えればいいのかな? Lispのリードマクロの範囲で出来るかどうかは置いといて順に考えてみる。
例えば、
(. Date new)
は、Rubyの次の式に相当する
Date.new
その流れでいくと、引数が指定された時は、次の様になるだろう。(リードマクロのsplicingはしないという流れです)
((. Kernel printf) "%d" 100)
対応するRubyの式
Kernel.printf( "%d", 100 )
そして、.(ドット)が2個登場するパターンは、.(ドット)に続いてシンボルをたくさん並べてもいいのかも。
(. (. array sort) reverse)
↓
array.sort.reverse
(. array sort reverse)
↓
array.sort.reverse
応用編として .(ドット)が3個登場する呼びだしのパターンを書くと
((. str split sort reverse))
↓
(str.split.sort.reverse)
となる。
これでも良い気がしてきた。が、後述のドットペア問題も残っているのだった…
set!も同様に処理可能かも
set!による代入
(set (. a b) 1)
これはコンパイルされた時にRubyの代入に変換される。
a.b = 1
ドットペア問題
shiroさんにも指摘頂いた、このコードがどうなるかを考えておかないと行けない
(foo . bar.baz)
単順にリーダで変換をかけると、次の様に変換される
(foo . (. bar baz))
それは、すなわちS式のセマンティクスで考えると、これと等価なのだ
(foo . bar baz)
うわー、たまたまドットを使っているせいでどちらのドットが残ったのかわかりにくいかも知れないが、実は2個目のドットがドットペアに置き換わったのだ。 うーん、まぎらわしい… しかし、一応ドットの後ろにシンボルが2個以上続くので syntaxエラーになる。これでいいのか?(多分、この形式がmacroの展開形に出てきた場合は、なにが起きているのか分かりにくい問題になるだろう。)
さらに最低限syntaxエラーになるようにガードするためには、
.read
は、内部で
(. nil read)
もしくは
(. Kernel read)
に変換して、必ず引数を2個以上にやる必要があるだろう。
今の感覚では、これでいいんじゃないかなぁと思っている。 記述しやすいかどうかはある程度の量のサンプルコードとか実用コードを書いてみないとわからん感じがするぞ。
※ 最後に、なるべく (. a b) という形式をサポートしたい理由は、例えば Rubyのメンバ変数をメタプログラミングで一気にいじりたいという場合に macro展開形で(. a b) 形式で扱えた方が便利だと考えているから。