- Lispの括弧が多く見える問題は、エディタ支援を使えばほぼ解消される。
- 現代のエディタは括弧のハイライトや構造編集に対応していて、人間が目で数える必要がない。
- Lispは構文が最小限なぶん、エディタ側の管理もシンプルで整合性が取りやすい。
- 【Emacs】SmartParensのよく使う操作(SlurpとBarf) – Chiilabo Note
- 【Emacs】Common LispのLoopインデントが深い?(lisp-loop-indent-subclauses) – Chiilabo Note
1. 括弧が多すぎる?
Lispを初めて見た人が、よく言われること。
それは、括弧が多すぎる、というものです。
(defun factorial (n)
(if (<= n 1)
1
(* n (factorial (- n 1)))))Code language: Lisp (lisp)
とくに、最後尾の部分。
ネストが深くなるほど閉じ括弧が末尾に連なって、どれがどれに対応しているのか追いきれない気がしてきます。
2. 括弧の構造はエディタが把握すること
たしかに、エディタ支援を知らずに生のコードを眺めれば、Lispの括弧が「難解に見える」という評価ももっともです。
多くのユーザーは、あまりエディタの存在を意識しないでもパソコンを利用できます。
エディタとは、Word、メモ帳、メールソフトとは別の、テキストやコードの編集に特化したツールです。
VS Code、Vim、Emacsなどがこれにあたります。
支援のある環境で実際に書いてみると、印象は大きく変わります。
現代のエディタは、括弧に対していろんなことができます。
- カーソルを括弧に当てると、対応するもう一方がハイライトされます。
- 自動インデントは構文の深さに従って字下げを調整するため、コードの形が構造をそのまま反映します。
- 構造編集と呼ばれる機能を使えば、式を丸ごと移動したり削除したりでき、括弧が壊れた状態をそもそも作りにくくなります1。
括弧の対応関係を人間の目で数えるのは大変でも、エディタにとっては簡単だからです。
この非対称性が、Lispの設計の鍵になっています2。
とくに、Emacsが1970年代後半に登場してLispと強く結びついたのも、こうした支援が可能だったからだと言えます3。
電卓を使えば複雑な計算が苦にならないのと同じで、括弧の対応管理はエディタに委ねればいいのです。
道具の使い方を知っているかどうかが、体験の分かれ目になります。
3. 可読性の問題をどこに置くか
多くのプログラミング言語では、括弧の変わりに何を使っているのでしょうか?
たとえば、VBAはその身近な例で、If、For End などそれぞれに対応するキーワードで文章のまとまりを表します4。
Function Factorial(n As Integer) As Long
If n <= 1 Then
Factorial = 1
Else
Factorial = n * Factorial(n - 1)
End If
End FunctionCode language: VBScript (vbscript)
入れ子が深くなると末尾に並ぶ複数の End If がそれぞれどの If に対応するかを、上に向かって文字列として追わないといけません。
もちろん、現代のエディタはVBAのコードの対応も理解していますが、どのキーワードが、構文に関係するのか細かく設定することで動作しています。
もし、言語仕様に新しい構文要素が追加されたら、そのままでは整合性が取れなくなってしまいます。
一方、Lispは構文を最小限で、エディタ側の管理もシンプルです。
3.1. インデントか括弧か?
Pythonでは、括弧の変わりにインデント(字下げ)を利用しています。
Pythonはオフサイドルールという仕組みを採用していて、インデントの深さがそのままブロック構造を決めます5。
そのため、見た目がすっきりしていて読みやすいです。
def factorial(n):
if n <= 1:
return 1
else:
return n * factorial(n - 1)Code language: Python (python)
ただ、Lispの括弧に慣れると、今度はPythonのほうが怖く見えてきます。
構造がインデントという人間の入力だけに依存しているからです。
たとえば、Lispの場合は、読みやすいように自由にインデントを調整しても意味は変わりません。
しかし、Pythonは、スペースが一つ余分に消えただけでコードの意味が変わることがあります。
しかも、それでもPythonは正しい構文として受け入れてしまい、実行するまで気づきにくいです。
とくにAI生成のPythonコードをペーストしていると、インデントが崩れてしまうことがありました6。
linterや静的解析ツールで一定のカバーはできますが、「構造が崩れたら、すぐに機械が気づく」という保証は、括弧ベースの言語のほうが強いです。
- EmacsにおけるLisp向けの構造編集プラグインとして、Paredit(paredit.el)が広く使われています。括弧の対応を常に保ちながら式単位での編集操作を可能にするもので、現在もEmacs Lispコミュニティで主流のツールの一つです。後継的な位置づけとしてParinferも普及しています。 – ParEdit – EmacsWiki
- Lispのコードはすべてリスト構造(S式、Symbolic Expression)で記述されます。コードとデータが同じ表現形式を持つ性質はホモイコニシティ(homoiconicity)と呼ばれ、「コードをデータとして操作できる」ことを意味します。この用語はAlan Kayが1969年の博士論文でLispを指して用いたとされています。 – Homoiconicity – Wikipedia
- Emacsの原型は1976年にDavid A. MoonとGuy L. Steele Jr.がTECOエディタ用のマクロ集として作成したものです。その後Richard Stallmanが開発を主導し、GNU Emacsへと発展しました。LispをそのままEmacsの拡張言語として採用したことで、エディタとLisp処理系が密接に統合されています。 – Emacs – Wikipedia
- キーワードでブロックを囲む構文の起源はALGOL 60(1960年)にさかのぼります。
beginとendでブロックを囲む設計がここで確立され、Pascal、Ada、VBAなど多くの言語に受け継がれています。 – Block (programming) – Wikipedia - 「オフサイドルール」という用語は、イギリスの計算機科学者Peter J. Landinが1966年の論文”The Next 700 Programming Languages”(Communications of the ACM)の中で定義したものです。サッカーのオフサイド規則をもじった名称とされています。 – Off-side rule – Wikipedia
- 最近は、AIエージェントでコードを直接書かせるので気にならなくなりましたが、以前はチャットベースで生成されたコードをコピーしていたので、よく貼り付け位置のインデントの深さと生成されるコードの深さが異なって、手動での微調整に気を使いました。