場合によっては、レキシカル束縛と動的束縛のどちらも同じように動作します。しかし、場合によっては、プログラムの意味が変わってしまうことがあります。例えば、レキシカル束縛の下でこのコードで何が起こるか見てみましょう:
;;; -*- lexical-binding: t -*-
(setq x 0)
(defun getx ()
x)
(setq x 1)
(let ((x 2))
(getx))
⇒ 1
ここでは、(getx) の結果は 1
となります。字句束縛の下では、getx は let 式の値を参照しません。これは、getx の本体が
let 式の本体の外側にあるためです。getx
はコードのグローバルレベル(つまり、いかなる let
式の本体内でもない場所)で定義されているため、グローバルレベルでも x
を探し、見つけ出します。getx を実行する際、x の現在のグローバル値は 1 であるため、getx はその結果を返します。
代わりに動的バインディングを使用すると、挙動が異なります:
;;; -*- lexical-binding: t -*-
(defvar x) ;; Use dynamic binding for 'x'.
(setq x 0)
(defun getx ()
x)
(setq x 1)
(let ((x 2))
(getx))
⇒ 2
さて、(getx) の結果は 2
になります!これは、動的バインディングにおいて、getx を実行する際、スタックの最上部にある
x の現在のバインディングが、let
のバインディングによるものだからです。今回、getx は x のグローバル値を参照しません。というのも、そのバインディングが、バインディングスタックにおける let 式のバインディングよりも下にあるためです。
(上記の defvar
宣言により、変数が「特殊」なものとなり、デフォルトのバインディング規則ではなく動的バインディング規則に従うようになります。defvar による変数の初期化
を参照してください。)