バッファのレキシカルバインディングを有効にする潜在的な落とし穴は何ですか?


12

これは、この質問での字句バインディング字句レットの議論に触発されました。字句結合あなたの人々はあなたがそれをすべての時間を有効になっていないだろう、なぜJavaScriptのような他の言語にするために使用することができる便利なクロージャを持ってすることができますか?

古いEmacsenとの後方互換性は、レガシーコードバッファーで有効にした場合、どのような落とし穴を探す必要があるのか​​を気にしませんか?

回答:


13

主要な落とし穴はのための結合セマンティクスということである、未定義の変数、すなわちで定義されていない変数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)状態に復元しました。
マラバルバ14

1
@Malababaいいえ、それは別の状況です。理由については、変数定義の最後の段落を参照してください。
lunaryorn 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.