Messy Macro の特徴付け(草稿以前)

Beware of the Turing tar-pit in which everything is possible but nothing of interest is easy. -- Alan Perlis


世の中にはマクロ生成系というものがいくつかありますが、これらはどういう用途で使うのでしょう?明らかに汎用のコンピュータ言語でできるような変換処理を、場当たり的な書き換え規則で処理するツールです。

世の中にこういったツールが存在する理由について考えています。筆者不勉強につき、いまのところこの文章の結論はありません。
文字列というのは concatination で作れる以上、生成には道具を選ばないはずだが、printf よりは文法を意識したプロダクションシステムを使うべき場合がある。

そういう道具でどんな文字列を作るかというと、何かのパーザに食わせるための文字列である。

具体例を挙げよう。Makefile の雛形を生成する automake や、config.h を生成する autoconf は内部で m4 を使っている。また、sendmail.cf という悪名高い設定ファイルが昔あったが、これも automake 同様のメカニズムで生成することが通例であった。またHTMLを生成するテンプレートの類(広く考えればPHP自体がそうだが、ここではsmartyのようなものを考える)もこのような Messy Macro であり、C++ の萌芽的実装は、おそらく単なる cpp のマクロだった。make 自身も一種の宣言型プログラミング言語であると同時に、マクロプロセッサとして使うこともできる。apachemod_rewrite も本質的に能力は劣るもののマクロプロセッサの一種だし、Java 開発環境で make を置き換える ant や maven2 にもマクロの記述機能がある。

これらのマクロプロセッサに共通する特徴としては、まず算術を表立ってサポートしていなかったり、使いづらいことが挙げられる(ant や maven2 のことはよく知らない)。マクロプロセッサの能力を増強していけば、そのうち算術を記述できるようになる(Turing Complete)が、むりやりやっても整数の内部表現は純粋なラムダ計算や、空集合から順序数で定義したものと同様の非効率なものになる。だから、生成する文字列に数式の計算が必要な場合ならより一般的な道具、つまり普通のプログラミング言語を使うほうがよい。UNIXのシェルやtclなども算術のサポートは弱く、マクロ言語の同類と言ってよい。

なんで算術のサポートが弱いか?おそらく理由は二つある。演算子を最小限に留めたい理由があること。そして実際にあまり必要ないこと。前者の理由は簡単だ。m4 のようなマクロプロセッサは、たとえば C++ のような言語をデザインする場合に使われるので、生成の材料や生成結果の文字列に、「関数呼び出しのように見える文字列」が沢山出てくる。これを m4 自身の演算子と区別するには、結局は極力「ぶつからない名前」を選ぶしかない。後者、つまり算術のサポートがさほど要らない理由については目下のところ筆者があんまりわかってないので、後回しにしたい。

それと関係するのかもしれないこととして、評価の文脈が明確なこと。m4のbackquoteやtclのbracketのようなものだ。これはマクロの展開結果が複数行にわたる場合など、演算子と文字列の区別が記述性を損なう場合に、演算子の記述力を犠牲にする選択になっている。

ともあれマクロがどういうものか、かんたんな具体例で説明しよう。読者は Marc Jacobs のトートバッグをごぞんじだろうか。こんなのだ。

(ここに著作権処理済みの写真が入る予定)

Marc Jacobs のトートバッグのデザインの最も重要な点は、デザイナーの名前を連呼するうざいロゴである。これがまさにマクロによって定義される言語の例になっている。もちろんロゴはロゴとして唯一無二の文字列であり、これが別の名前であったら同じ印象は受けないのだが、とりあえず Jacobs を $1、Marc を $2 に置き換えてつぎの表現を得る。

$1 by $2 $1 for $2 by $2 $1 in collaboration with $2 $1 for $2 by $2 $1

この表現は架空の項書き換え系に与える文法記述の一部である。$1 と $2 がその上を動くユニバースないしアルファベットが与えられれば、言語を具体的に規定することになる。

Chomsky(1956)の分類では、これは有限状態マルコフ過程でもなければ句構造でもない生成文法の例になっている、はず。すなわち、これは正則表現(Kleene(1956))やBNF(Backus et al.(1960))でうまく定義することができない。後で導入された概念を使うと、これは文脈依存言語の一種である。

Thompson's regexp を使えば、この言語を受理するオートマトンは以下の正規表現(Kleene(1956)の意味での正則言語でないことに注意!)で実装できる。

^([^ ]+) by ([^ ]+) &1 for &2 by &2 &1 in collaboration with &2 &1 for &2 by &2 &1