Secondo questo articolo la seguente riga di codice Lisp stampa "Hello world" sull'output standard.
(format t "hello, world")
Lisp, che è un linguaggio omoiconico , può trattare il codice come dati in questo modo:
Ora immagina di aver scritto la seguente macro:
(defmacro backwards (expr) (reverse expr))
all'indietro è il nome della macro, che accetta un'espressione (rappresentata come un elenco) e la inverte. Ecco di nuovo "Ciao, mondo", questa volta usando la macro:
(backwards ("hello, world" t format))
Quando il compilatore Lisp vede quella riga di codice, osserva il primo atomo nell'elenco (
backwards
) e nota che nomina una macro. Passa l'elenco non valutato("hello, world" t format)
alla macro, che riorganizza l'elenco in(format t "hello, world")
. L'elenco risultante sostituisce l'espressione macro ed è ciò che verrà valutato in fase di esecuzione. L'ambiente Lisp vedrà che il suo primo atomo (format
) è una funzione e lo valuterà, passandogli il resto degli argomenti.
Raggiungere questo compito in Lisp è facile (correggimi se sbaglio) perché il codice è implementato come elenco ( s-espressioni ?).
Ora dai un'occhiata a questo frammento di OCaml (che non è un linguaggio omoiconico):
let print () =
let message = "Hello world" in
print_endline message
;;
Immagina di voler aggiungere l'omoiconicità a OCaml, che utilizza una sintassi molto più complessa rispetto a Lisp. Come lo faresti? La lingua deve avere una sintassi particolarmente semplice per ottenere l'omonicità?
EDIT : da questo argomento ho trovato un altro modo per ottenere l'omoiconicità che è diverso da quello di Lisp: quello implementato nel linguaggio io . Potrebbe parzialmente rispondere a questa domanda.
Qui, cominciamo con un semplice blocco:
Io> plus := block(a, b, a + b) ==> method(a, b, a + b ) Io> plus call(2, 3) ==> 5
Va bene, quindi il blocco funziona. Il blocco positivo ha aggiunto due numeri.
Ora facciamo un po 'di introspezione su questo ometto.
Io> plus argumentNames ==> list("a", "b") Io> plus code ==> block(a, b, a +(b)) Io> plus message name ==> a Io> plus message next ==> +(b) Io> plus message next name ==> +
Muffa calda e fredda. Non solo puoi ottenere i nomi dei parametri di blocco. E non solo puoi ottenere una stringa del codice sorgente completo del blocco. Puoi intrufolarti nel codice e attraversare i messaggi all'interno. E la cosa più sorprendente di tutte: è tremendamente facile e naturale. Fedele alla ricerca di Io. Lo specchio di Ruby non riesce a vedere nulla di tutto ciò.
Ma, whoa whoa, hey ora, non toccare quel quadrante.
Io> plus message next setName("-") ==> -(b) Io> plus ==> method(a, b, a - b ) Io> plus call(2, 3) ==> -1