[LSP42] Io
(この記事はLISP Implementation Advent Calendar 6日目のためのエントリです。)
IoでLISPを作りました。
https://github.com/zick/IoLisp
Ioは prototype-based のオブジェクト指向言語でSmalltalkの影響を強く受けています。多分。でも私はSmalltalkのことをよく知らないのでなんとも言えません。
動機
今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその8つ目です。
Ioを選んだ理由は同僚に「なかなか面白そうな言語だから」と推薦されたからです。触れば触るほど面白さが伝わってくる言語でしたが、面白さが全部伝わる前にLISPが完成してしまいました。
文法
Ioの文法は初めて見るとちょっとだけ不思議な感じがします。
fact := method(n, if ( n == 0, 1, n * fact(n - 1)))
注目すべきは method(引数, 本体)
と if (condition, then, else)
という部分です。LISPとCを折衷したような独特な文法です。少々風変わりですが慣れてしまえば書きやすいです。たまにカンマを書き忘れて、それで挙動が変わってしまうことがあるのがちょっと困りモノです。
オブジェクト
Ioはいわゆる prototype-base のオブジェクト指向言語なのでクラスは存在しません。
LObj := Object clone makeCons := method(a, d, cell := LObj clone cell tag := "cons" cell car := a cell cdr := d cell)
既存のオブジェクトのクローンを作って新しいオブジェクトを作っていくわけです。「俺はオブジェクト指向言語だ! プログラムを書きたかったらまずクラスを作れ! クラスを作るたびにファイルを作れ! ヒャッハー!!!」などといった横暴な言語と違って、穏やかな優しい印象を受けます。「なんで tag とか作ってるの? 別にいらないでしょ?」とか思った人は後で職員室に来るように。
高階関数
SUBRを作るために高階関数を作ろうとしたら少し困りました。
f1 := method(arg, body) f2 := f1
などと書いても f2 には nil が代入されてしまいます。こうなる理由は調べた気がするんですがもう忘れてしまいました。回避策としてオブジェクトのスロットにメソッドを代入して、そのオブジェクトを持ちまわることにしました。少し考えてみたらまったく同じことを過去にもやったことを思い出しました。RubyでLISPを実装した時です。さらに考えてみると、RubyもSmalltalkの影響を受けている言語です。これを踏まえてマニュアルを読み直してみると、SmalltalkにもRubyにもIoにも block という概念があるのに気付きました。
「そうか、わかったぞ!」
と、思ったところまでは覚えているのですが何を分かったのか忘れてしまいました。誰か思い出させてください。
職員室
Ioの オブジェクトは hasProto というメソッドを持ち、これでオブジェクトがどのオブジェクトを元に作られたが調べられるので、わざわざ tag などといったものを付けなくても実行時に型を調べられるわけです。
Cons := LObj clone makeCons := method(a, d, cell := Cons clone cell car := a cell cdr := d cell) ... if (obj hasProto(Cons), ...)
しかし、これを使ってLISPを書くと速度が低下しました(6.2sec -> 6.6sec)。恐らく、 prototype のチェーンをたどるより文字列を比較するほうが速いのが要因でしょうが、原因の究明は読者の練習問題とします。
小学生並みの感想
やっぱり書きやすい言語はいいと思いました。
[…] Iokeは簡潔に言うと「JVMで動くIoみたいな言語」です(*)。JVMで動く言語は当たりが多いし、Ioは既に使ったことがあるので簡単に出来る予感がしました。 […]
[…] なお、R、Icon、Ioke、Eはスタックオーバフローのため計測できず。Scratch、Io、Tcl、Falcon、Ceylon、Eiffelは5分以内に終わらなかったので打ち切りました。 […]