Zgodnie z tym artykułem następujący wiersz kodu Lisp wypisuje „Hello world” na standardowe wyjście.
(format t "hello, world")
Lisp, który jest językiem homoiconic , może traktować kod jako dane w następujący sposób:
Teraz wyobraź sobie, że napisaliśmy następujące makro:
(defmacro backwards (expr) (reverse expr))
wstecz to nazwa makra, która przyjmuje wyrażenie (reprezentowane jako lista) i odwraca je. Oto znowu „Witaj, świecie”, tym razem za pomocą makra:
(backwards ("hello, world" t format))
Gdy kompilator Lisp widzi ten wiersz kodu, patrzy na pierwszy atom na liście (
backwards
) i zauważa, że nazywa makro. Przekazuje nieocenioną listę("hello, world" t format)
do makra, które przestawia listę na(format t "hello, world")
. Wynikowa lista zastępuje wyrażenie makr i jest to, co zostanie ocenione w czasie wykonywania. Środowisko Lisp zobaczy, że jego pierwszy atom (format
) jest funkcją, i oceni go, przekazując mu pozostałe argumenty.
W Lisp realizacja tego zadania jest łatwa (popraw mnie, jeśli się mylę), ponieważ kod jest implementowany jako lista ( wyrażenia s ?).
Teraz spójrz na ten fragment OCaml (który nie jest językiem homoiconic):
let print () =
let message = "Hello world" in
print_endline message
;;
Wyobraź sobie, że chcesz dodać homoikoniczność do OCaml, który używa znacznie bardziej złożonej składni w porównaniu do Lisp. Jak byś to zrobił? Czy język musi mieć szczególnie łatwą składnię, aby osiągnąć homoikoniczność?
EDYCJA : w tym temacie znalazłem inny sposób osiągnięcia homoikoniczności, który różni się od Lispa: ten wdrożony w języku io . Może częściowo odpowiedzieć na to pytanie.
Tutaj zacznijmy od prostego bloku:
Io> plus := block(a, b, a + b) ==> method(a, b, a + b ) Io> plus call(2, 3) ==> 5
Okej, więc blok działa. Blok plus dodał dwie liczby.
A teraz zróbmy introspekcję na tym małym człowieku.
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 ==> +
Gorąca święta zimna pleśń. Nie tylko możesz uzyskać nazwy parametrów bloku. Nie tylko możesz uzyskać ciąg pełnego kodu źródłowego bloku. Możesz zakraść się do kodu i przeglądać wiadomości w nim zawarte. I najbardziej niesamowite ze wszystkich: jest to okropnie łatwe i naturalne. Wierny zadaniu Io. Lustro Ruby tego nie widzi.
Ale, zaraz, hej, nie dotykaj tej tarczy.
Io> plus message next setName("-") ==> -(b) Io> plus ==> method(a, b, a - b ) Io> plus call(2, 3) ==> -1