回答:
主要な落とし穴はのための結合セマンティクスということである、未定義の変数、すなわちで定義されていない変数defvar
や友人-変更を伴うlexical-binding
:それがなければ、let
動的な、しかしと結合するすべてlexical-binding
有効に未定義の変数が束縛されている字句、および未使用の場合でも、現在のレキシカルスコープで完全に省か。
古いコードはこれに依存する場合があります。オプション機能のハード依存を回避するために、対応するライブラリを必要とせずに、または変数自体を宣言せずに動的変数をバインドします。
(let ((cook-eggs-enabled t))
(cook-my-meal))
クッキング機能がオプションの場合、ユーザーに不要な依存関係を強制したくないため(require 'cook)
、使用せず、代わりにcook-my-meal
関数の自動読み込みに依存します。
人間の読者にとって、cook-eggs-enabled
ローカル変数ではないことは明らかですが、cook
ここではライブラリのグローバルな動的変数を参照しています。lexical-binding
このコードがないと、意図したとおりに機能し cook-eggs-enabled
ます。定義されているかどうかにかかわらず、動的にバインドされます。
lexical-binding
:しかし、それは壊れ cook-eggs-enabled
、今バインドされている字句(それが使用されていないので、離れた後、最適化された)グローバル動的変数をので、cook-eggs-enabled
されていない今までにまだまったく触れて nil
、時間によってcook-my-meal
呼び出された私たちは意外にどんな卵を持っていないので、私たちの食事で。
幸いなことに、これらの問題は非常に簡単に見つけることができます。バイトコンパイラは、ここで未使用の字句バインディングについて自然に警告します。
修正は簡単です:((require 'cook)
とにかく実際にはオプションではない機能の場合)を追加するか、ハード依存関係を回避するために、独自のコードで変数を動的変数として宣言します。これには特別なdefvar
形式があります:
(defvar cook-eggs-enabled)
これはcook-eggs-enabled
動的変数として定義しますが、変数のバインドの性質を除き、docstring、load-history
(および、したがってfind-variable
友人)、またはその他には影響しません。
cook-eggs-enabled
にバインドlet
解除されませんか?私は以前にこのようなバグに遭遇したことがあると確信しています。defvarはの内部let
で発生し、let
後で変数を初期(void)状態に復元しました。