Emacs Lisp の dynamic binding と lexical binding
GNU Emacs をカスタマイズするのに使われる Emacs Lispはdynamic bindingとlexical bindingという二種類のbindingを提供しているので、それらを比較してみます。dynamic bindingとlexical binding自体やclosureなどは既知であることを想定しています。
Dynamic binding
デフォルトでは、Emacs Lispは変数に対してdynamic bindingを使います。dynamic bindingでは、変数の値はlambda式が定義された時点での環境ではなく、lambda式が呼び出された時点での変数環境が使われます。以下にdynamic bindingの例を示します。
;; lexical bindingを無効化してdynamic bindingを使う
(setq lexical-binding nil)
;> nil
;; ラムダ関数を返す関数を定義する
(defun retcl (x)
(lambda () (+ x 1)))
;> retcl
;; 引数10で関数を呼び出す
(setq f (retcl 10))
;> (lambda nil (+ x 1))
;; xが20であるコンテキスト内で関数fを呼び出す。retcl内の変数環境ではなく呼出時の変数環境が使われるので、結果は21になる
(let ((x 20))
(funcall f))
;> 21
この例では、~(let ((x 20)) …)~ ブロック内で (funcall f)
を呼び出すと結果は 21
となります。以下にdynamic bindingの例を示します。これは、動的束縛では、変数 x
の値が関数呼び出し時の環境から取得されるためです。この場合、それは 20
で、ラムダ関数内で 1
が加えられて 21
となります。
Lexical binding
一方、lexical bindingは、関数が定義された時点での変数環境を使い続けます。以下にlexical bindingの例を示します。
;; lexical bindingを有効化して、同じ例を動かす
(setq lexical-binding t)
;> t
;; ラムダ関数を返す関数を定義する
(defun retcl (x)
(lambda () (+ x 1)))
;> retcl
;; 引数10で関数を呼び出す。そもそもこの時点で (x . 10) という環境の元でのclosureということが表示されているが…
(setq f (retcl 10))
;> (closure ((x . 10) t) nil (+ x 1))
;; xが20であるコンテキスト内で関数fを呼び出す。呼出時の変数環境ではなくretcl内の変数環境が使われるので、結果は11になる
(let ((x 20))
(funcall f))
;> 11
この場合、出力は 11
となります。これは、lexical bindingにおいて、ラムダ関数 (+ x 1)
が定義されたときの環境、つまり x
の値が 10
である環境を保持しているためです。したがって、~(let ((x 20)) …)~ ブロックで x
が 20
と定義されていても、ラムダ関数内の x
は関数 f
が定義されたときの 10
を参照しています。