忌み嫌われているグローバル変数/関数をなるべく使用しないために、もう一つ小さな空間を用意している言語があります。僕のイメージでは、この空間はゆりかごです。ゆりかごの実現方法として、以下のようなものが挙げられるでしょう。
- C++、Java ならクラス
- JavaScript なら関数、あるいはオブジェクト・リテラル
- Scheme なら手続き(関数)
Emacs Lisp も Lisp なんですから、ある関数をゆりかごにして、中に関数を定義したいところです。でも、これはできません。さて、どうするかという話です。
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 では使っています。)