2012-09-17 29 views
13

Emacs 24爲本地變量添加了可選的詞彙綁定。我想在我的模塊中使用這個功能,同時保持與XEmacs和以前的Emacs版本的兼容性。Emacs中的詞彙範圍:與舊版Emacsen兼容

在Emacs 24之前,獲取閉包的最簡單方法是使用cl-macs中定義的lexical-let表單,該表單用一些聰明的宏觀欺騙來模擬詞法範圍。雖然這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下進入,並且通常會引起注意。有沒有更好的辦法?


編輯思想

兩個例子更聰明(不一定好)解決方案,允許代碼繼續使用標準的形式來創建關閉:

  • 使用的意見或編譯器宏使lexical-let擴展到letlexical-bindings下,如果lexical-let只分配到無論如何詞法範圍的符號。這個建議只會在foo.el的字節編譯過程中暫時激活,因此其餘的Emacs的lexical-let的含義保持不變。

  • 使用宏/代碼行走工具將let的未加前綴的符號編譯爲lexical-let在舊的Emacsen下。這將僅適用於foo.el的字節編譯期間。

如果這些想法有過度工程的味道,請不要驚慌:我不是建議按原樣使用它們。我對上述宏的替代品感興趣,其中該包得到更好的便攜式使用對於加載/編譯的一些額外複雜性的代價的收益。


EDIT 2

由於沒有一個已加強了一個解決方案,將允許模塊使用letlexical-let保持不破壞他們的Emacs的休息,我接受Stefan的回答,其中指出以上宏這樣做的方法。除此之外,通過使用bound-and-true-p併爲edebug和lisp-indent添加一個優雅的聲明,我的代碼的答案得到了改善。

如果有人對此兼容性層提供了替代方案,或者優雅地實現了上述想法,我鼓勵他們回答。

+0

如果我在通用lisp中編寫這個宏包裝,我可能會在這裏使用相同的基本方法。除了通過在頂層有一個defmacro bla-lexlet,然後有一個編譯時if結構來確定將代碼擴展到哪種形式(let或lexical),在這種情況下)。我不會把這作爲答案,因爲它是一種常見的口頭禪方式,也是你使用的基本方法。 –

+0

好點,我現在修改了這個例子,將'if'移至宏展開時間。然而,這個問題中指出的反對'foo-lexlet'的論據依然存在。 – user4815162342

回答

6

由於lexical-let和詞彙結合的let不這樣做完全一樣的(更具體lexical-let總是使用詞彙的結合,而let使用是動態綁定或詞法綁定取決於變種是否defvar「d與否) ,我認爲你的方法和它一樣好。您可以輕鬆Edebug踏進去,壽:

(defmacro foo-lexlet (&rest letforms) 
    (declare (indent 1) (debug let)) 
    (if (bound-and-true-p lexical-binding) 
     `(let ,@letforms) 
    `(lexical-let ,@letforms))) 

如果你不想依賴於declare,您可以使用(put 'foo-lexlet 'edebug-form-spec 'let)

+0

感謝您的「聲明」提示,我會使用它。我會推遲接受你的答案,以鼓勵其他想法。爲了使這更容易,我編輯了這個問題,以添加這些解決方案可能採取的方向示例。 – user4815162342

2

一個可能的解決方案是使用defadvice來掛鉤lexical-let擴展。我寫了以下建議,似乎工作正常。這也是byte-compile

(defadvice lexical-let (around use-let-if-possible (bindings &rest body) activate) 
    (if (and (>= emacs-major-version 24) 
      (boundp 'lexical-binding) 
      lexical-binding) 
     (setq ad-return-value `(let ,bindings . ,body)) 
    ad-do-it)) 
+0

您是否在您的'.emacs'中使用此建議作爲用戶,或者作爲您包中的第三方包作者?對我來說,激活這個建議並不會改變用戶背後所有其他地方的詞彙行爲。 – user4815162342

-1

詞彙-LET看起來有相同的arglist格式讓利,那麼,關於這樣的事情:

(if (older-emacs-p) 
    (setf (macro-function 'let) (macro-function 'lexical-let)) 
    (setf (macro-function 'lexical-let) (macro-function 'let))) 

該墊片應該允許新的Emacs讀取舊代碼詞彙,讓部分,以及另一種方式(允許較舊的Emacs讀取較新的代碼的一部分)。

雖然這是Common Lisp。任何人都有意將它轉換成Emacs?

如果lexical-let/let被實現爲特殊形式(而不是宏),你可能會遇到麻煩。

此外,如果在較早的Emacs中定義let,這可能會完全破壞向前兼容的情況。是嗎? (我對Emacs知之甚少;這不是我選擇的編輯器)。但無論如何,向後兼容的情況可能更爲重要。

+0

第二個'setf'似乎是多餘的 - 你的意思是用一個'psetf'來交換'let'和'lexical-let'的宏函數嗎?這看起來像是爲了編譯一個包而改變了Emacs會話的其餘部分的'let'的含義。沒有警告用戶的模塊會被嚴重破壞。 – user4815162342