Può, ma è più pericoloso.
defmacro
ha tre caratteristiche principali:
- Viene eseguito in fase di compilazione;
- È una vera sostituzione sul posto; e
- A causa dell'intera natura della sintassi Lisp, funziona a un livello AST, non a un livello di codice sorgente.
Al contrario, eval
:
- Esegue in fase di esecuzione;
- Non è una sostituzione sul posto; e
- Funziona con le stringhe.
Perché è importante?
Per prima cosa, eval
ha una penalità di velocità: poiché deve valutare i suoi argomenti ogni volta che viene chiamata, invece di una sola volta in fase di compilazione, avrai un hit di velocità ad ogni esecuzione del programma.
In secondo luogo, non esiste una convalida in fase di compilazione in cui il eval
passi effettivamente qualcosa di sano. Puoi avere un errore di sintassi orribile nel tuo argomento eval
(o, peggio, un orribile errore di sintassi solo quando chiamato in determinati modi ), ma non verrà catturato fino a quando il tempo di esecuzione non sarà lungo la strada . Poiché le macro Lisp sono in fase di compilazione, non è possibile averlo. La tua macro potrebbe non funzionare, sia chiaro, ma verrà compilata per definizione a qualcosa di valido.
Terzo, eval
ha seri problemi di sicurezza rispetto a defmacro
. Poiché le macro Lisp funzionano con l'AST, c'è un alto livello di sicurezza. Indipendentemente dall'argomento che fornisci la macro in fase di runtime, tali argomenti non possono consentire a un chiamante malintenzionato di "uscire" dalla macro o di chiamare codice dannoso. Non è vero per eval
: se non pensi che la tua stringa sfugge molto, molto attentamente e consenti a qualsiasi forma di input generato dall'utente nella stringa eval
, rischi di consentire l'esecuzione di codice dannoso.
Ok, ma, qualunque sia, posso sentirti dire. L'utente dovrebbe farlo da solo, ed è già il suo browser, quindi perché preoccuparsi?
Perché ci sono molti modi per iniettare script in quella situazione. Richieste di script tra siti, ad esempio, o dimenticanza di eseguire correttamente una stringa che si archivia in un database. Ci sono lotti di modi per introdurre valori dannosi in quelle macro e, nel momento in cui ciò accade, i dati dei tuoi utenti sono tostati.
Infine, anche se tutto ciò non ti dissuade, il eval
di JavaScript in particolare è semplicemente meno potente. Ci sono strani modi in cui interagisce con l'ambito variabile incluso e l'ambito della variabile globale, ad esempio, e non esiste un modo corretto per fare l'equivalente del gensym
del Common Lisp per l'igiene delle variabili.
Puoi utilizzare eval
in alcuni dei luoghi che potresti utilizzare defmacro
? Sicuro. Ma sono difficilmente intercambiabili, e spero che la spiegazione di cui sopra chiarisca il motivo per cui assolutamente zero i buoni framework JavaScript che conosco usano eval
.
Invece, se davvero ti trovi in cerca di macro, vai a trovare un linguaggio compilato su JavaScript che le abbia, come ClojureScript. In questo modo otterrai i vantaggi delle macro Lisp senza gli svantaggi di eval
.