ローカル変数のバインディングは現代的なLisp方言ではスペシャル変数にたいして(以下参照)、古いLisp方言ではすべての変数にたいしてダイナミックです(Lisp方言の選択を参照)。ダイナミックな変数バインディングにも用途はあるものの、レキシカルバインディングに比べて一般的にはエラーが発生しやすく効率に劣り、ダイナミックバインディングを使用したコードではコンパイラーが誤りを発見しにくくなります。
ある変数がダイナミックにバインドされていると、Lispプログラムの実行における任意のポイントでのカレントバインディングは、単にそのシンボルにたいしてもっとも最近作成されたダイナミックなローカルバインディング、またはそのようなローカルバインディングが存在しなければグローバルバインディングになります。
以下の例のように、ダイナミックバインディングはダイナミックスコープとダイナミックエクステントをもちます:
(defvar x -99) ;xは初期値として−99を受け取る (defun getx () x) ; この関数内ではxは自由に使用される (let ((x 1)) ;xはダイナミックにバインドされている (getx)) ⇒ 1 ;;letフォームが終了した後に ;;xは前の値−99にリバートされる (getx) ⇒ -99
関数getxはxを参照します。defun構文自体の中にxにたいするバインディングが存在しないという意味において、これはフリーな参照です。xが(ダイナミックに)バインドされているletフォーム内からgetxを呼び出すと、ローカル値(つまり1)が取得されます。しかしその後letフォームの外側からgetxを呼び出すと、グローバル値(つまり−99)が取得されます。
以下はsetqを使用してダイナミックに変数をバインドする例です:
(defvar x -99) ;xは初期値として−99を受け取る (defun addx () (setq x (1+ x))) ;xに1加算して新しい値をリターンする (let ((x 1)) (addx) (addx)) ⇒ 3 ;addxを2回呼び出すとxに2回加算される ;;letフォームが終了した後に ;;xは前の値−99にリバートされる (addx) ⇒ -98
レキシカルバインディングが有効な場合でも、特定の変数はダイナミックにバインドされたままです。これらはスペシャル変数(special
variable)と呼ばれます。defvar、defcustom、defconstで定義されたすべての変数はスペシャル変数です(グローバル変数の定義を参照)。その他のすべての変数はレキシカルバインディングの対象になります。
値なしでdefvarを使用することにより、他の場所ではレキシカルにバインドされている状態のまま、単一ファイルやファイルの一部だけで変数をダイナミックにバインドすることが可能になります。たとえば:
(let (_) (defvar x) ;xへのletバインドはこのlet内ではダイナミック (let ((x -99)) ; これはxのダイナミックバインド (defun get-dynamic-x () x))) (let ((x 'lexical)) ; これはxのレキシカルバインド (defun get-lexical-x () x)) (let (_) (defvar x) (let ((x 'dynamic)) (list (get-lexical-x) (get-dynamic-x)))) ⇒ (lexical dynamic)
この関数はsymbolがスペシャル変数(つまり変数がdefvar、defcustom、defconstによる定義をもつ)なら非nilをリターンする。、それ以外ならリターン値はnil。
これは関数なので永続的にスペシャルな変数には非nilをリターンできるが、カレントレキシカルスコープでのみスペシャルな変数では異なることに注意。
関数内の正式な引数としてのスペシャル変数の使用はサポートされていません。
Emacs Lispでのダイナミックバインディングは、シンプルな方法で実装されています。シンボルはそれぞれ、シンボルのカレントのダイナミック値(または値の不在)を指定する値セルをもちます。シンボルの構成要素を参照してください。あるシンボルがダイナミックなローカル値を与えられたとき、Emacsは値セルの内容(または値の不在)をスタックに記録して、新しいローカル値を値セルに格納します。バインディング構文が実行を終えたとき、Emacsはスタックから古い値をpopして値セルにそれを配置します。