2012-09-17 20 views
13

Emacs 24 ha aggiunto associazioni binarie opzionali per le variabili locali. Vorrei utilizzare questa funzionalità nel mio modulo, pur mantenendo la compatibilità con XEmacs e le precedenti versioni di Emacs.Ambito lessicale in Emacs: compatibilità con la versione precedente di Emacsen

Prima di Emacs 24, il modo più semplice per ottenere chiusure era utilizzando il modulo lexical-let definito in cl-macs, che emulava lo scope lessicale con alcuni intelligenti trucchi della macro. Anche se questo non è mai stato molto popolare tra i programmatori elisp, ha fatto il lavoro, la creazione di chiusure reali ed efficienti, a patto che vi siete ricordati di metterli in lexical-let, come in questo pseudocodice:

(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 ... 
    ))) 

La domanda è: qual è la modo migliore per utilizzare i nuovi collegamenti lessicali, pur mantenendo il supporto per Emacs 23 e per XEmacs? Attualmente ho risolto definendo una macro-specific pacchetto che si espande in lexical-let o in azioni ordinarie let seconda che lexical-binding è legato e vero:

(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: 

Questa soluzione funziona, ma ci si sente goffo perché la nuova forma speciale è non -standard, non evidenzia in modo corretto, non può essere calpestato sotto edebug e generalmente attira l'attenzione su se stesso. C'è un modo migliore?


EDIT

Due esempi di idee per più intelligenti soluzioni (non necessariamente buono) che permettono il codice di continuare a utilizzare moduli standard per creare dispositivi di chiusura

  • uso di un consiglio o una macro del compilatore per rendere lexical-let espandibile a let sotto lexical-bindings se il lexical-let assegna solo ai simboli che sono lessicamente scoped comunque. Questo consiglio verrà attivato solo temporaneamente durante la compilazione di byte di foo.el, in modo che il significato di lexical-let rimanga invariato per il resto di Emacs.

  • Utilizzare una funzione macro/code-walker per compilare let di simboli non prefissati su lexical-let con Emacsen precedente. Ciò si applicherebbe nuovamente solo durante la compilazione di byte di foo.el.

Non allarmatevi se queste idee odore di overengineering: non sto proponendo di usarli così come sono. Sono interessato alle alternative alla macro di cui sopra, in cui il pacchetto ottiene il vantaggio di un utilizzo più portatile di chiusure per il prezzo di qualche complessità aggiuntiva di caricamento/compilazione.


EDIT 2

Come nessuno ha intensificato con una soluzione che permettesse il modulo di continuare a utilizzare let o lexical-let senza romperle per il resto della Emacs, sto accettando la risposta di Stefan, che afferma che la precedente macro è il modo per farlo. In aggiunta a ciò, la risposta migliora sul mio codice usando bound-and-true-p e aggiungendo una dichiarazione elegante per edebug e lisp-indent.

Se qualcuno ha una proposta alternativa per questo livello di compatibilità o un'elegante implementazione delle idee di cui sopra, li incoraggio a rispondere.

+0

Se stessimo scrivendo questo wrapper macro in un comune liff, probabilmente utilizzerei lo stesso approccio di base qui. Tranne che io nix 'l'eval, avendo un singolo defmacro bla-lexlet al livello più alto, e poi ho un costrutto-tempo se costrutto che determina quale modulo espandere il codice in (let o lessical-let in questo caso). Non sto mettendo questo come una risposta b/c è un approccio Lisp comune, e lo stesso approccio di base che hai usato. –

+0

Bene, ora ho modificato l'esempio per spostare il parametro 'if' in macroexpansion-time. Tuttavia, gli argomenti contro "foo-lexlet" evidenziati nella domanda rimangono validi. – user4815162342

risposta

6

Dal lexical-let e lessicale vincolante di let non fanno esattamente la stessa cosa (più precisamente lexical-let utilizza sempre lessicale vincolante, mentre let utilizza sia binding dinamico o lessicale vincolante a seconda che la var era defvar 'd o no) , Penso che il tuo approccio sia buono come sembra. Si può facilmente rendere Edebug passo in esso, tho:

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

Se non si vuole dipendere da declare, è possibile utilizzare (put 'foo-lexlet 'edebug-form-spec 'let).

+0

Grazie per il suggerimento 'dichlare, lo userò. Rinuncerò ad accettare la tua risposta ancora per un po ', per incoraggiare idee alternative. Per semplificare, ho modificato la domanda per aggiungere esempi della direzione che potrebbero prendere tali soluzioni. – user4815162342

2

Una possibile soluzione è utilizzare defadvice per collegare l'espansione lexical-let. Ho scritto il seguente consiglio, e sembra funzionare bene. Questo è anche byte-compile consapevole.

(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

Stai usando questo consiglio come utente nel tuo '.emacs' o come autore di pacchetti di terze parti nel tuo pacchetto? Non è ovvio per me che l'attivazione di questo consiglio non cambierà il comportamento di 'lessical-let' ovunque dietro la schiena dell'utente. – user4815162342

-1

lessicale-let sembra avere lo stesso formato arglist come let, quindi che dire qualcosa di simile:

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

Questo spessore dovrebbe consentire più recenti Emacs per leggere lessicale-lasciare porzioni di codice più vecchio, così come l'altro modo (consentire a Emacs meno recenti di leggere le parti del codice più recente).

Questo è comunque Lisp comune. Qualcuno ha cura di tradurlo in Emacs?

E potresti incontrare dei problemi se lessical-let/let è implementato come un modulo speciale (non una macro).

Inoltre, questo può interrompere completamente il caso di compatibilità diretta se let è definito nel vecchio Emacs. È? (So ​​molto poco di Emacs, non è il mio editor preferito). Ma il caso compatibile con le versioni precedenti potrebbe essere più importante a prescindere.

+0

Il secondo 'setf' sembra ridondante-intendevi usare un singolo' psetf' per scambiare le funzioni macro di 'let' e' lexical-let'? Sembra che cambierebbe il significato di 'let' nel resto della sessione di Emacs per la compilazione di un singolo pacchetto. Un modulo che l'ha fatto senza avvertire l'utente sarebbe stato seriamente danneggiato. – user4815162342

Problemi correlati