💬

Emacs Lispの核構文の決定プロセスが謎

2024/11/01に公開

when-letを廃止するぞ

先日の東京Emacs勉強会 オクトーバーフェスティバル2024で、最近のEmacs Lispではwhen-letを廃止して、when-let*に統一するという話を耳に挟んだ。
そんなコア構文を簡単に廃止するもんなのかと不思議に思ったので調べてみると、Emacs Lispの核構文の決定プロセスが謎だなと思ったので色々書いてみる。

何ゆえwhen-letを廃止したいの?

イシューを追い掛けていくと、そもそもはwhen-letがどうのというよりも、and-let*when-let*と何が違うのという話だったらしい。

and-let*は、Scheme(SRFI)からパクってきたものだと思うが、when-let*との違いは、束縛部に述語が取れるところである——

(and-let* ((x 3)
           ((zerop 0)))
  'foo)

——と思ったらelispのwhen-let*and-let*の挙動と同じ実装だった(if-let*から派生)。
when-letは、Common LispのライブラリのAlexandriaに由来しているのではないか、という話もあったが、こちらは変数なしで束縛部に述語は記述できない、というか普通できるようにしないと思う。

それで、when-letや、if-let*付きに統合されるのは、内部的に*付きの構文と同じだからということらしい。
束縛の並列と順次評価の違いという*の意味はどこにいった?と思ったりしたが、*付きに統一されれば、とりあえず統一感はある。

構文は、Alexandriaに由来しているという話だが、本当かなと思って履歴を追い掛けてみたところ、スレッディングマクロと一緒に入っているのでどうもClojure由来にみえる。

どうせならもっとClojureにかぶれたらどうか

どうも昔からEmacs Lispのsubr.elをいじっている人にはSchemeにかぶれている人が多かった。and-let*や、letrecがあったりするのは、Schemeかぶれ。最近は、Clojureにかぶれている人が多いのかもしれない。
どうせClojureにかぶれるなら、elispもClojureもベクタを[]と表記することを活かして、そのままClojureの見た目で導入してみたらどうだろうか。

(defun remove-comma (bindspec)
  (mapcar (lambda (b)
            (if (and (consp b) (eq '\, (car b)))
                (nth 1 b)
              b))
          bindspec))

(defmacro *when-let (bindspec &rest body)
  (declare (indent 1))
  `(when-let* (,(coerce bindspec 'list))
     ,@body))

(defmacro *let (bindspec &rest body)
  (declare (indent 1))
  `(let (,@(cl-loop for (var val) on (remove-comma (coerce bindspec 'list)) by #'cddr
                    collect (list var val)))
     ,@body))

無駄に,が使えるようにしてみた。

(*when-let [x (zerop 0)]
  (list x))(t)

(*let [x 3, y 2, z 1]
  (list x y z))(3 2 1)

まとめ

Emacs Lisp界は構文のコアにこなれた機能でもないものが沢山入っている印象。取捨選択のプロセスが謎。

Discussion