Implementazione dei comandi Scheme defmacro e lambda
Per entrare nel dettaglio della programmazione vediamo come è stata implementata la defmacro confrontandola con la lambda.
La lambda permette di creare una chiusura: un oggetto che comprende la funzione più l'ambiente di definizione della funzione. La defmacro permette di creare una macro che se applicata viene valutata in due passi: espansione e valutazione vera e propria.
In molti casi è possibile risolvere un problema utilizzando indifferentemente la lambda o la defmacro (vedi implementazione della mylet), ma il comportamento dei due costrutti si differenzia su aspetti fondamentali.
La principale differenza è che la lambda salva
il suo environment (localEnv che è una lista di liste) di definizione,
mentre la defmacro non lo fa.
In fase di applicazione la lambda viene eseguita
nel suo environment di definizione che temporaneamente sostituisce il LocalEnv
attuale.
Invece l'espansione della defmacro viene eseguita nel LocalEnv attuale in cui vengono temporaneamente aggiunti in testa i bindings prodotti dai parametri della defmacro. Poi la valutazione della forma prodotta avviene nell'environment corrente (LocalEnv in cui vengono eliminati i bindings precedentemente aggiunti).
Abbiamo testato la defmacro su tutti gli esempi presi
dalle dispense di Linguaggi e Traduttori ed il comportamento è effettivamente
quello desiderato.
Abbiamo infine aggiunto i controlli necessari per
segnalare tutti i possibili errori (es: un atomo al posto della lista degli
argomenti).
Un'ultima osservazione: la valutazione della defmacro è svolta dallo stesso metodo che valuta il body di una lambda. Questo stesso metodo è stato utilizzato anche per implementare la let (zucchero sintattico di lambda) ed il body della cond.