あるとき思ったのだが、「Lispはカッコが優しくない」という言説は、すくなくともいまの日本においては、まつもとさんを介して広まっており維持されているのではないかという気がする。実際にはそんなことはないと思うが、まつもとさんがそう思うのは勝手だし、そう言われると、なんとなく気持ちはわかるのだよね。わかるので、わりとみんな「Lispのカッコとは」みたいな文章を書きたくなってしまうし書いてしまうわけだ。
まつもとさんは別の文脈では、言語の字句構文的なテイストみたいなものが思考をスイッチするという話も引き合いに出す。 C と Ruby はけっこう見た目が違うので、脳が簡単にスイッチできるっていうやつね。これはぼくもそう思うのだ。そして Lisp も見た目が違うのでスイッチは容易だ、とぼくは思うのだ。独特の見た目はべつに欠点ではない。ついでに、 Common Lisp と Scheme は似ているので、一方に慣れてしまうともう一方がどうにも気持ち悪く見えてしまうのかもしれない。ふむ、 CLer と schemer の宗教戦争のポイントはそこにあるのだろうか。
閑話休題。
さて、こういう風に2つの主張をとりだすと、まつもとさんの論旨は矛盾しているようにぼくからは見えるのであるが、もちろん彼の中では整合しているのだろう。だからべつにぼくはそれを問題視したいわけではないのだ。ただ、まつもとさんは当たり前だけど物凄く偏った人である。執拗に(とぼくには見える)Lispについてはカッコがよくない、と繰り返す姿勢はいただけないが、それがその人の中で重要な論点なのであればそれは仕方ない。その点についてはまともな議論にならないだろうが、言っても仕方のないことなのである。そういう偏りのない人の方が珍しいし、人間ってのは偏ってるもんだ。満遍なく均質な人間なんて気持ち悪かろう。
でまあ、つまり問題なのは主張が間違ってるとか矛盾してるとか主観的であるとか偏っているとか、そういったことではなく、発言者が影響力を持っているということだ、ということになる。影響力があるということは、まともに検証をせず鵜呑みにする層が生まれるということだからだ。まあ、「LispってAIの言語でしょ」みたいな20年前にはカビが生えていたようなことをいまさら言われて困るようなシチュエーション(実話)よりはだいぶマシだが……。
この辺を煮詰めるとネットイナゴ論やトンデモ理論のビリーバーたちと相通じるような気がする。だって、ひとことでまとめると鵜呑みにするやつが一番がバカなので困ると、ぼくはそのように主張しているわけだ。しかしまあ、ぼく自身がそんなに完璧なわけでもなかろうし、だいいちすべての話を検証していたんでは身が保たないので、ある種の「権威」を信じるというのは決して悪いことではないだろう。ではどうしたものか。
そういう視点を持つと、その都度反論するというのが、いちばん地味だがいちばん大切なことなのかもしれないようにも思える(笑い飛ばす、という「と学会」方式もありだ)。いずれにせよ、反論は鵜呑みにしている人に対して「あれっ、この人の意見は正しくないのかな、どうなのかな」という揺さぶりをかけることができる。揺さぶりは検証を促すはずである。たぶん、いくらかは。逆にいちばんまずいのは感情的な反論をすることだろう。たとえ相手の言い分がデタラメであっても、感情的な反論によっては聴衆に検証を促すことがやりづらいからだ。
で、ここまでは Lisp のカッコの話のふりをしてきたのだったよね。この話を、たとえばPHPとかに話を広げるのは、皆さんへの課題ということにしておこう(というかそれはマシントラブルのあいだに arton さんにされてしまっているという気もする)。
ところで話はずれるが、開発に非常に近いところにある人間とってのまつもとさんはべつに教祖ではないだろうと思う。というか教祖なのであれば、教祖の下知を皆が受け取ればいいわけであり、だから教祖にはならないしコミッタにもなる必要はない。逆にまつもとさんはダメだと思われているだろう。何もしなくてもまともな Ruby が出来上がっているなら、誰も開発に手を染めたりしない。自分がやらなきゃやってくれないのでやっているわけである。ということは、開発者やアーリーアダプターにとって、その対象の評価は「惜しい」がつねにベストということになりそうである。話題になっているモノとかは惜しいものなのかもしれない。
「どうせ MzScheme なんだから Gauche への移植くらい簡単じゃね?」とは、たぶんすぐ思いつくことなんですが、やってみたら意外と大変ですね、というのが結論ぽい感じですね。
arc は MzScheme の上に乗ってるわけで、 Gauche に移行するには arc が使っている MzScheme の機能を移植、もしくは代替する機能に割り当てなければならない。で、そういう「MzSchemeの機能」なんて大して気にしていませんでしたが、一部のものは移植が大変であると。たとえばモジュールや名前空間は頑張ればそれほど大変じゃなさそうですし、セマフォも mutex と condition variable を組み合わせれば実装はできそうです。でも current-gc-milliseconds とかは Gauche レベルではちょっと実装できそうにないし、 Gauche の gc は Boehm を使ってるから Gauche を改造すればできるかどうかも怪しい。あと thread 関係は意味論が微妙に違うものがあり、 break-thread とか thread-dead? とかは Gauche でどう実装したものか、なんて問題もあるのかも。
というわけで飽きてしまった。だめだな。まあ arc にはそこまでして移植したいという気が起きないといったところか。
ところで、「ac.scm で使われていて Gauche で定義されていないシンボル」を取り出すというスクリプトを書いてみようと思ったらこれが意外と手間だった。とりあえず切り出したのは、
>
(ac-macex all-defined arc< arc> args break-thread call-with-semaphore char current-gc-milliseconds current-milliseconds current-process-milliseconds current-seconds delete-file directory-exists? directory-list else exn-message exn:fail? exn? flush-output fn hash-table-count hash-table-remove! int kill-thread lib make-limited-input-port make-semaphore make-thread-cell module mzscheme namespace-set-variable-value! namespace-variable-value once parameterize path->string pretty-print print-hash-table random sleep sym system tcp-accept tcp-addresses tcp-close tcp-listen tcp-listener? thread thread-cell-ref thread-cell-set! thread-dead? type vals with-handlers)
といったところ。まだノイズがあるのでイマイチですが、おおむねこんな感じなのかな。なお切り出しに使ったスクリプトは下記の通り。
(use srfi-1)
(use util.match)
(define (search-symbol sym)
(define m (find-module 'gauche))
(global-variable-bound? m sym))
(define (check-symbols-in-file filename)
(define symbol-set (make-hash-table))
(define defined-set (make-hash-table))
(define (symbol-defined? tables sym)
(any (cut hash-table-exists? <> sym) tables))
(define (symbols-resolv tables obj)
(match obj
(('quote x) #t)
(('define (var args ...) body ...)
(if (hash-table-exists? symbol-set var)
(hash-table-delete! symbol-set var))
(hash-table-put! (car tables) var #t)
(let1 new-set (make-hash-table)
(for-each (cut hash-table-put! new-set <> #t) args)
(symbols-resolv (cons new-set tables) body)))
(('define var body ...)
(if (hash-table-exists? symbol-set var)
(hash-table-delete! symbol-set var))
(hash-table-put! (car tables) var #t)
(let1 new-set (make-hash-table)
(symbols-resolv (cons new-set tables) body)))
(('let ((vs . ds) ...) body ...)
(symbols-resolv tables ds)
(let1 new-set (make-hash-table)
(for-each (cut hash-table-put! new-set <> #t) vs)
(symbols-resolv (cons new-set tables) body)))
(('let* (vars ...) body ...)
(let1 new-set (make-hash-table)
(for-each (lambda (def)
(cond ((pair? def)
(symbols-resolv (cons new-set tables)
(cdr def))
(hash-table-put! new-set (car def) #t))))
vars)
(symbols-resolv (cons new-set tables) body)))
(('let-values ((vs . ds) ...) body ...)
(symbols-resolv tables ds)
(let1 new-set (make-hash-table)
(for-each (cut for-each
(cut hash-table-put! new-set <> #t) <>) vs)
(symbols-resolv (cons new-set tables) body)))
(('lambda args body ...)
(let1 new-set (make-hash-table)
(if (list? args)
(for-each (cut hash-table-put! new-set <> #t) args)
(hash-table-put! new-set args #t))
(symbols-resolv (cons new-set tables) body)))
((body bodies ...)
(symbols-resolv tables body)
(symbols-resolv tables bodies))
((? symbol? sym)
(if (not (symbol-defined? tables sym))
(hash-table-put! symbol-set sym #t)))
(_ #t)))
(define (read-and-scan p)
(let1 obj (read p)
(cond ((eof-object? obj) symbol-set)
(else (symbols-resolv (list defined-set symbol-set) obj)
(read-and-scan p)))))
(call-with-input-file filename read-and-scan)
(filter (lambda (s) (not (search-symbol s)))
(hash-table-keys symbol-set)))
えーと、マシンが壊れてました。
少し前に書いてましたがディスクが不調の兆しを見せておりまして、「いつ交換しようかなー」と思っていたらある日突然ブッ壊れて大変でした。本機は jmuk.org の DNS サーバ、メールサーバ、webサーバを兼任しているのでナンにもできなくなる。実際の復旧作業はそれほど時間はかかりませんでしたが、まだ復旧できてない機能があるんじゃないかとびくびく。あ、 darcs と mercurial 入れてないな、そういえば。