Previous: , Up: Variable Scoping   [Contents][Index]


12.10.4 レキシカルバインディングの使用

Emacs LispファイルのロードやLispバッファーを評価するとき、バッファーローカルな変数lexical-bindingが非nilなら、レキシカルバインディングが有効になります:

Variable: lexical-binding

このバッファーローカルな変数が非nilなら、Emacs Lispファイルとバッファーはダイナミックバインディングではなくレキシカルバインディングを使用して評価される(しかし特別な変数はダイナミックにバインドされたまま。以下を照)。nilならすべてのローカル変数にたいしてダイナミックバインディングが使用される。この変数は、通常はファイルローカル変数として、Emacs Lispファイル全体にたいしてセットされる(File Local Variablesを参照)。他のファイルローカル変数などとは異なり、ファイルの最初の行でセットされなければならないことに注意。

eval呼び出しを使用してEmacs Lispコードを直接評価するとき、evallexical引数が非nilなら、レキシカルバインディングが有効になります。Evalを参照してください。

レキシカルバインディングは*scratch*バッファーで使用されるLisp Interactionモード、および*ielm*バッファーで使用されるIELMモードでも有効であり、M-: (eval-expression)を通じた式の評価や、Emacsとemacsclient (emacsclient Options in The GNU Emacs Manualを参照)の--evalコマンドラインオプション(Action Arguments in The GNU Emacs Manualを参照)を処理する際にも有効です。

レキシカルバインディングが有効な場合でも、特定の変数はダイナミックにバインドされたままです。これらはスペシャル変数(special variable)と呼ばれます。defvardefcustomdefconstで定義されたすべての変数はスペシャル変数です(Defining Variablesを参照)。その他のすべての変数はレキシカルバインディングの対象になります。

値なしで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)
Function: special-variable-p symbol

この関数はsymbolがスペシャル変数(つまり変数がdefvardefcustomdefconstによる定義をもつ)なら非nilをリターンする。、それ以外ならリターン値はnil

これは関数なので永続的にスペシャルな変数には非nilをリターンできるが、カレントレキシカルスコープでのみスペシャルな変数では異なることに注意。

関数内での通常の引数としてのスペシャル変数の使用は、推奨されません。レキシカルバインディングモードが有効なときにこれを行うと、(あるときはレキシカルバインディング、またあるときはダイナミックバインディングのような)不定な動作が起こります。

Emacs Lispプログラムをレキシカルバインディングに変換するのは簡単です。最初にEmacs Lispソースファイルのヘッダー行でlexical-bindingtにして、ファイルローカル変数を追加します(File Local Variablesを参照)。次に意図せずレキシカルにバインドしてしまわないように、ダイナミックなバインドをもつ必要がある変数が変数定義をもつことを各変数ごとにチェックします。

どの変数が変数定義をもつ必要があるか見つけるシンプルな方法は、ソースファイルをバイトコンパイルすることです。Byte Compilationを参照してください。letフォームの外側で非スペシャル変数が使用されていれば、バイトコンパイラーはフリーな変数にたいする参照や割り当てについて警告するでしょう。非スペシャル変数がバインドされているがletフォーム内で使用されていなければ、バイトコンパイラーは使用されないレキシカル変数に関して警告するでしょう。バイトコンパイラーは、スペシャル変数を関数の引数として使用している場合も問題を警告します。

(使用されていない変数についての警告を抑制するためには単に変数名をアンダースコアーで開始する。そうすればバイトコンパイラーは変数が使用されないことを示すと解釈する。)