星田さんの記事に対するコメント。
っつーか、ちと悪い事しちまったかなぁ、と思ってる。
そしてこちら(-_-;) 以前からちょくちょくあるけど、何も調べずにテキトーに書いてしまってCametanさんのお手を煩わせるという・・申し訳ありません!
とりあえず今後はちゃんと調べたり実験した上で、どうしても解決できない事だとか疑問だとかの形になった時点で書くようにします。
いや、全然構わないです。
もちろん自分で調べたり実験するのも大事だけど、ガンガン「ポロッと」書いていって良いと思っています。
なんせ「日記」なんで。好きに書くべきなんですよ。
星田さんの強みは、前にも書いたけど、何でもブログでメモを取る事だ、と思っています。要するに「メモ魔」である事が星田さんの強みだと思っている。
だから僕が書いたコメントが「メモ魔」の邪魔になってるなら、むしろこっちこそ申し訳ねぇかなぁ・・・と。
この「ブログでメモを取る」って簡単に出来るようで実はなかなか出来ない事だと思ってるんですよね。
前にも書いたけど、Web媒体でプログラミング言語を学ぶ、とか言うのってフツーはあんま役に立たないんですよ。でも星田さんの場合は有効だった。何故ならメモを取りまくってるから、です。
だから星田さんは「勝手に思いついた事を」メモしていけばいい。僕もそれに対して「勝手に」コメントしていく(笑)。
それでエエんちゃうか、と思ってるわけです。
ええとね、マクロで何故に言いよどんだかと言うと。
星田さん、マクロを避けてるでしょ?
でもそれは総体的に見ると「正しい」ってのも事実だからなんです。
Lispを学ぼう、って人って大体2タイプに分かれるんです。
- 伝統的な関数型言語なんで、関数型言語の「考え方」を知りたい、と言う人。
- 他のプログラミング言語に存在しない、「強力なマクロ」を覚えたい人。
んで、問題は後者、なんです。
僕なんかもポール・グレアムのエッセイに触発されたタイプなんで、最初は後者だったんですよね。
ところが、だ。やってくとLisp学ぶ、特に後者のタイプって
「マクロ書きたい病」
ってのに羅漢する(笑)。つまり、必要もないのに「マクロを書くネタは無いのか?」とか思っちゃって、不必要にマクロを書こう、とする病気に必ず一回はかかっちゃうんですよ。
そしてそれは実は望ましいスタイルじゃあない。
加えると、「いつマクロを書くべきか?」ってのもハッキリしないんです。
ポール・グレアムもいくつか指針を提示してはいるんだけど、やっぱ絶対じゃないし、そもそも、いくつかマクロのノウハウってのはあるんだけど、全体的に整理はされていない。
1960年代に登場したLispなのに、ポール・グレアムが初めてマクロノウハウ本(On Lisp)を出したのは1990年代初頭です。30年前後も経っている。
つまり、それくらい「Lispのマクロ」ってのは「プログラミングスタイル」としては実は確立してきてなかった、って事でしょう(皆がもっとガンガン使ってたら「知見」が累積されてる筈だから)。
それくらい「曖昧模糊」としてるのがマクロであり、だから星田さんが「距離を取ってる」のは実は正しいんです。
で、その適切な距離を取ってる人に余計な事書くべきかなぁ・・・って悩んだ、ってのがホントのトコなんです。
誤解させちゃったかもしれないけど。
ここから余談ね。
ANSI Common Lispだと、関数以外では、スペシャルオペレータ(かつてのScheme用語で言うと特殊形式)ってのがあって、そしてマクロがある。一応定義的には分かれていて、「ユーザーが定義出来ない」ものがスペシャルオペレータ、ユーザーが拡張出来るものを「マクロ」って呼んでるんですが。
ところが、Schemeだと関数以外のものは一貫して「構文」って呼んでます。そしてSchemeの中では「構文を定義する方法」をマクロって呼んでるのかな。
だからANSI Common Lispだと defmacro 、つまり「マクロを定義」なんだけど、Schemeだと define-syntax なんです。後者はある意味「構文を定義する限定的な機能」と言う意図が言外に存在する。
ANSI Common Lispだと「構文を定義するのもマクロの大きな役割の1つ」なんだけど、意図はそこに限定しない。この2つはだから、名前に込められた意味がちと違う気がしますね。
例えば、「マクロをいつ使うかよく分からん」って例として、ポール・グレアムは on-cdr と言うマクロを自著で紹介している。これは使うとマクロ展開して再帰形式を産み出すマクロで、Schemeのマクロの命名から考えると完全に外してるマクロの例です。
んでね、でも今星田さんはfoldとかfoldlと格闘してるわけじゃない?機能的に言うとon-cdrってのはfoldとかfoldlのマクロ版なんですよ。ってこたぁ、「それをマクロで書き直す必要があるのか?」って疑問がon-cdrにはいつも付きまとう。そもそもポール・グレアムが掲げた「関数とマクロがあるなら関数を優先させるべきだ」に反してるんですよね。
他にポール・グレアムはマクロで計算効率を上げる例も紹介してる。パターン的には例えば引数が可変長引数だった時にマクロで書いた方がコンパイル時に計算されるから有利だ、と言う論調で、例として平均を計算するマクロを挙げている。Schemeで書くとこんなカンジ?
(define-syntax avg
(syntax-rules ()
((_ arg ...)
(/ (+ arg ...) (length `(,arg ...))))))
ただ、これはSchemeに於いては「計算効率が上がるかどうかは」仕様上保証の限りじゃない、と。何故ならSchemeには(仕様上)コンパイラがないから。
つまり、実装依存になっちゃう。ある処理系では計算速度は上がるだろうけど、別の処理系だとそうじゃないだろう、と。
って事はANSI Common LispだとO.K.なテクニックでもSchemeじゃ汎用性があるアイディア、ってワケじゃなくなるでしょ?
あとは、例えばこんな話も書いてる。つまり、長い名称をマクロで短くしようぜ、と。
でもこれは、素のviなんかの「自動補完が無いテキストエディタ」では有効な論、なんだけど、Emacsを始めとする「自動補完」アリの環境だとあまり説得力のあるマクロだとは思えないのね(笑)。だって「自動補完アリ」のIDEだと例えばtab叩くだけで補完出来ちゃうじゃない。「短くする為に」マクロを書く、ってのは素のviユーザーには魅力的だけど、「支援機能」が色々ある環境だとそこまで魅力的なアイディアでもない。
結果ね。On Lispに紹介されているマクロでも「どうなのこれは?」って思うマクロがやっぱ多いんだよな。「なるほど」とは思うけど、それ以上ではねぇ、っつーか(笑)。
それくらい、マクロって「使いどころが難し」くて、やっぱ鉄壁のノウハウがない。結果、「古典的な使い方」である「構文作成」が一番正しい、って事になっちゃう。Schemeの慧眼、ってのはその通りなんだわ。
でも、例えば、「構文を作りましょう!」ってんでフツーの言語で言うforやwhileは確かに作れる。作れるんだけど、Schemeプログラマのレベル10でも書かれてる通り、
ループを書くのにnamed letを自然に使うようになる。 C言語でもつい再帰で書いてしまうようになる。 何故forループがあんなに欲しかったのかわからない。
でしょ(笑)?そうなっちゃうんだって(笑)。
最初あれだけ「再帰!?」って身構えてたのに、今じゃなんともないでしょ(笑)?
だから漸化式の方が人間には自然なんだって(笑)。
つまり「マクロで繰り返し構文を作らな!」とか言う必然性も無くなっちゃう(笑)。
繰り返し構文も作る必要が無くなっちゃうわけだから、そう、要するに「マクロの出番」ってのはマジでほぼ「無い」んだ。
と言うわけで、星田さんが「マクロに適切な距離感を持ってる」ってのは正しいんです。
だから心配してたのは、そのマクロに対する「距離感」を下手に壊さないかな、って事だったんですよ。
まぁ、それでもリスト組み立ての逆クオート、コンマ、コンマ@の使い方は覚えるべきだとは思っていますが。
いずれにせよ、今後Pythonに移るにせよ、あるいはWindowsでF#使ってWindowsアプリを作るにせよ、Scheme/Racketでの経験は間違いなくプラスに働くでしょう。
ただし、その範疇だとあんまマクロは関係ないですね(何故なら他の言語には存在せんから・笑)。
個人的には、Scheme/Racketだとマクロよりも継続に慣れるべきだとは思ってますし。