Emacs 24 では、ローカル変数のオプションのレキシカル バインディングが追加されました。XEmacs および以前の Emacs バージョンとの互換性を維持しながら、この機能をモジュールで使用したいと考えています。
Emacs 24 より前では、クロージャーを取得する最も簡単な方法は、 でlexical-let
定義されたフォームを使用することでした。このフォームはcl-macs
、巧妙なマクロトリックでレキシカル スコープをエミュレートしていました。これは elisp プログラマーの間であまり人気がありませんでしたが、lexical-let
次の疑似コードのように でラップすることを覚えていれば、実際に効果的なクロージャーを作成して機能しました。
(defun foo-open-tag (tag data)
"Maybe open TAG and return a function that closes it."
... do the work here, initializing state ...
;; return a closure that explicitly captures internal state
(lexical-let ((var1 var1) (var2 var2) ...)
(lambda ()
... use the captured vars without exposing them to the caller ...
)))
問題は、Emacs 23 と XEmacs のサポートを維持しながら、新しいレキシカル バインディングを使用する最善の方法は何かということです。現在、バインドされているかどうかに応じlexical-let
て通常に展開または通常に展開するパッケージ固有のマクロを定義することで解決しました。let
lexical-binding
(defmacro foo-lexlet (&rest letforms)
(if (and (boundp 'lexical-binding)
lexical-binding)
`(let ,@letforms)
`(lexical-let ,@letforms)))
(put 'foo-lexlet 'lisp-indent-function 1)
... at the end of file, turn on lexical binding if available:
;; Local Variables:
;; lexical-binding: t
;; End:
この解決策は機能しますが、新しい特別なフォームが非標準であり、適切に強調表示されず、 の下edebug
にステップインできず、通常はそれ自体に注意を引くため、ぎこちなく感じます。より良い方法はありますか?
編集
コードが引き続き標準フォームを使用してクロージャーを作成できるようにする、よりスマートな (必ずしも良いとは限らない) ソリューションのアイデアの 2 つの例を次に示します。
アドバイスまたはコンパイラ マクロを使用して、レキシカルスコープのシンボルへの割り当てのみを下に
lexical-let
展開します。このアドバイスは、 のバイトコンパイル中に一時的にのみアクティブになるため、残りの Emacs ではの意味は変更されません。let
lexical-bindings
lexical-let
foo.el
lexical-let
マクロ/コード ウォーカー機能を使用して、
let
プレフィックスのないシンボルをlexical-let
古い Emacsen にコンパイルします。これも、 のバイトコンパイル中にのみ適用されますfoo.el
。
これらのアイデアがオーバーエンジニアリングのにおいがしても心配しないでください。私はそれらをそのまま使用することを提案していません。上記のマクロに代わるものに興味があります。パッケージは、ローディング/コンパイルの複雑さを犠牲にして、クロージャーの移植性の高い使用の利点を得ることができます。
編集2
モジュールが残りの Emacs でモジュールを使用し続ける、let
またはそれらを壊すことなく使用できるようにするソリューションを誰も提供していないため、上記のマクロがlexical-let
それを行う方法であると述べている Stefan の回答を受け入れます。それに加えて、edebug と lisp-indent のエレガントな宣言を使用および追加することで、コードが改善されます。bound-and-true-p
誰かがこの互換性レイヤーの代替提案、または上記のアイデアの洗練された実装を持っている場合は、回答することをお勧めします.