Emacs Lispの核となるホモイコニック性—プログラムとデータの二元性

Lisp系の言語、特にEmacs Lisp(Elisp)の最も顕著な特徴の一つは、「ホモイコニック性」にあります。これは、プログラムがその言語自身のデータ構造で書かれていることを意味します。

Elispでは、ソースコードそのものがリスト、シンボル、文字列、数値といったデータとして表現されます。これは、consで構築し、carやcdrで分解するような通常のリストと全く同じ形をしているのです。

Emacs Lispでは、ソースコードはリスト、シンボル、文字列、数値として扱われ、コードとデータが同じ形式で表現されます。

このプログラムとデータの区別は、特別な文字である ' (クォート) によって制御されます。例えば、( + 1 2 ) は評価されると 3 になるプログラムですが、'( + 1 2 ) は評価されずに + というシンボルと 1, 2 という数値からなるリストデータとして扱われます。

そして、このクォートされたリストに対して eval 関数を実行すると、再びプログラムとして解釈され、結果として 3 が返されます。(出典)

このプログラムとデータのデュアリズムこそが、Elispの言語の心臓部であり、メタプログラミングを可能にする鍵です。私自身、普段TypeScriptやPythonで開発していると、コードとデータが明確に分離されていることが当たり前だと感じますが、Elispのこの特性は、プログラムの自己変革能力において非常に大きなアドバンテージを持っていると感じます。

プログラムとデータの融合—Möbiusの帯に例えるLispの二面性

記事では、このプログラムとデータの二元性をM.C.エッシャーの「メビウスの帯 II」に登場するアリに例えています。メビウスの帯は表と裏の区別がなく、連続した一つの表面です。同様にElispのホモイコニック性も、プログラムとデータという二つの側面が、実は連続した一つの実体であることを示唆しています。

つまり、Elispではコードの断片を、たった一つの文字を変更するだけで、検査可能な値にしたり、変換・再構築したりできます。そして、適切に整形された値は、関数呼び出し一つでプログラムとして実行できるのです。

特性 ほとんどの言語 Lisp(Elisp)
プログラムの形式 独自の構文ツリーなど 言語自身のデータ構造 (リストなど)
データとの関係 明確に分離 同一の形式で表現される
メタプログラミング 専用のAPIやツール 言語の根幹機能として提供

この特性は、コンパイラやインタプリタといった言語処理系の内部的な動作を、言語自身で記述できることを意味します。これは、他の多くの言語では専用のレイヤーやツールが必要となるメタプログラミングが、Lispでは言語の根幹機能として自然に利用できるという点で、非常に強力だと感じます。特に、AI関連のプロジェクトでコード生成を行う際などには、この柔軟性が大いに役立つのではないでしょうか。

マクロの真髄—コードを生成するプログラム

Elispにおけるマクロは、このホモイコニック性を最大限に活用するユーティリティです。マクロは、評価される前に入力されたコードを変換し、新しいコードを生成する特殊な関数と考えることができます。これにより、開発者は言語の構文を拡張したり、繰り返し作業を自動化したり、ドメイン固有言語(DSL)を効果的に構築したりすることが可能になります。

マクロは、実行されるコードそのものを操作し、動的にコードを生成することで、言語の柔軟性を飛躍的に高めます。

例えば、Emacsの多くの設定ファイル(.emacs.d/init.elなど)には、defvarやrequireといったおなじみのマクロが使われています。これらは一見すると普通の関数のように見えますが、実際にはコンパイル時にLispコードを生成し、そのコードが実行されるという仕組みです。これにより、より簡潔で表現豊かなコーディングが可能になります。

マクロを理解し活用することで、Emacsの設定や拡張がいかにパワフルであるかを実感できるでしょう。個人的には、AIがコード生成を行う現代において、マクロのようなコードをコードとして扱う機能は、より一層その価値を増していくと感じています。

マクロ理解のためのツール群

マクロの挙動は一見すると魔法のように感じるかもしれませんが、Emacsにはその内部動作を理解するための優れたツールがいくつか存在します。

  • macroexpand:式がどのように展開されるかを一度だけ表示します。
  • emacs-lisp-macroexpand:指定された式が最終的にどのように展開されるかを再帰的に表示します。
  • macrostep:マクロの展開プロセスをステップバイステップで視覚的に追跡できます。

これらのツールを使うことで、マクロが具体的にどのようなLispコードを生成しているのかを把握し、デバッグや学習に役立てることができます。私もClaude CodeなどでElispのスクリプトを書く際には、これらのツールを積極的に活用して、意図したコードが生成されているかを確認したいと感じます。

EmacsのLiquid Glassエフェクトとマクロ—表現力豊かなカスタマイズ

ところで、この記事とは直接関係ありませんが、Emacsの表現力の豊かさという点で、最近「Liquid Glass effect」が話題になっていますね。これはmacOS環境でEmacsフレームに半透明のガラスのようなエフェクトを適用するもので、背景のぼかしや透過度を設定できます。

特定のパッチを適用してEmacsをビルドすることで、Ghosttyのようなモダンなターミナル風の見た目を実現できます。(出典)

background-opacity = 0.01
background-blur = macos-glass-regular

この機能は、単なるテーマとは異なり、ネイティブのNSGlassEffectViewをサポートすることで実現されています。EmacsはLispで書かれているがゆえに、このようなフレームワークレベルの深いカスタマイズが、時にはC言語のパッチを当てつつも、Emacs Lispの設定と組み合わせて可能となるのです。

これもまた、Lispの持つ柔軟性、そしてユーザーが環境を「曲げる」能力の一例ではないでしょうか。自作PCでローカルLLMを動かすように、Emacsもまた、とことん自分好みにカスタマイズできる開発環境の究極形だと感じます。