あどけない話

インターネットに関する技術的な話など

Emacs Lisp の構造化

忌み嫌われているグローバル変数/関数をなるべく使用しないために、もう一つ小さな空間を用意している言語があります。僕のイメージでは、この空間はゆりかごです。ゆりかごの実現方法として、以下のようなものが挙げられるでしょう。

Emacs LispLisp なんですから、ある関数をゆりかごにして、中に関数を定義したいところです。でも、これはできません。さて、どうするかという話です。

defun で defun

defun の中で defun したいところですが、これはうまく行きません。外側の関数が実行されると、内側の関数がグローバル空間に定義されてしまうからです。

(defun foo (n)
  (defun bar (n)
    (1+ n))
  (bar n))

(fboundp 'bar)nil
(foo 2)3
(fboundp 'bar)t

ローカル変数

ローカル変数に関数を定義するとどうなるでしょうか? こんな感じです。

(defun foo (n)
  (let ((bar (lambda (n) (1+ n))))
    (bar n)))
(foo 2) → エラー

これは実行時にエラーになります。なぜなら、Emacs Lisp では変数名と関数名の空間は分かれているからです。

funcall

では、funcall を使うとどうなるでしょうか? こんな感じです。

(defun foo (n)
  (let ((bar (lambda (n) (1+ n))))
    (funcall 'bar n)))
(foo 2) → エラー

これも実行時にエラーになります。なぜなら、funcall の第一引数のシンボルは、グローバルの関数名の空間を意味するからです。

最後の手

さて困りました。関数の中に関数は定義できそうにありません。しかし、もう一つだけ手が残されています。funcall の第一引数は、シンボルではなく、lambda 式そのものでもいいのです。

つまり、

(defun bar (n) (1+ n))
(funcall 'bar 2)3

と書く代わりに、

(funcall (lambda (n) (1+ n)) 2)3

と書けます。

という訳で、先ほどの 'bar のクオートを外すだけで、ウソのように動いてしまいます。

(defun foo (n)
  (let ((bar (lambda (n) (1+ n))))
    (funcall bar n)))
(foo 2)3
(fboundp 'bar)nil

所感

Emacs Lisp でも、関数の中に関数を定義することが模倣できるとは、ほどんど誰も知りません。なので、このテクニックを使ったコードを見かけたことはありません。(Mew では使っています。)