[LSP42] Groovy
(この記事はLISP Implementation Advent Calendar 10日目のためのエントリです。)
GroovyでLISPを作りました。
https://github.com/zick/GroovyLisp
動機
今年の春、訳あって42個のプログラミング言語でLISP処理系を実装することになりました。これはその16個目です。
Groovyという言語を選んだのは、ずばり楽そうだったからです。毎週末頑張ってLISPを書いていたのにまだ全体の3分の1を超えた程度という事実に疲れたら、ふと「JVM上で動くJava以外の言語はみんな『Javaと較べてこんなに簡潔です』と言ってるし簡単に書けるに違いない」という(根拠の無い)発想にいたり、名前とJVMで動くということしか知らないGroovyを選びました。
外観
さて、実際にはどれくらい簡潔だったかと言いますと、
def makeCons(a, d) { return [tag: 'cons', car: a, cdr: d] }
こりゃ簡潔だ。何よりも型を書かないのがいいですね。
rangeの罠
イマドキの言語にはよく部分配列や部分文字列を取り出すための構文があります。Pythonだと str[start:end]
だとかRubyだと str[start...end]
といった具合です。Groovyにも同様に str[start..end]
という構文があります(ただし、 end は inclusive)。また、 start から終端までの部分配列/文字列を取得する場合は str[start..-1]
と書けるのですが、これにはPythonやRubyにはない罠がありました。
if (str[0] == kLPar) { // 先頭の文字が括弧なら return readList(str[1..-1]) // 残りの文字列をリストとして読み込む }
一見正しそうなコードですが、 str の長さが1の場合、なんと out of range 扱いで例外を投げます。これはおかしい。 「だってstr[1]は取れないでしょ」と思った人は「部分」という言葉についてもっと考えるべきです。この仕様は不自然ですし何よりもメリットを感じません。誰かこの仕様の意味を教えて下さい。
スコープの罠
Groovyはその見た目からいってどう見てもイマドキの言語。PythonやRubyみたいなものだと思って書いていました。そうしたら見事罠に引っかかりました。
def foo() { bar = 1 ... }
こんなコードを書いていたのですが、Groovyではこの場合 bar はグローバル変数として扱われます。ローカル変数として扱いたければ def を付けて
def foo() { def bar = 1 ... }
と書きます。知っていればそれだけの話で、「JavaScriptのvarと同じだろ」と言ったところなのですが、マニュアルをよく読んでいなかった私は見事にしてやられました。言い訳をしておくと、Groovyのマニュアルが読みにくいのが悪いです。あと、スコープという大事な話を下の方に書いているのもいただけない。そんな下の方読みませんよ。
小学生並みの感想
普通に使いやすかったので言いがかり的な文句くらいしか書くことがありませんでした。