Selon cet article, la ligne suivante de code Lisp imprime "Hello world" sur la sortie standard.
(format t "hello, world")
Le lisp, qui est un langage homoiconique , peut traiter le code comme des données de cette manière:
Imaginez maintenant que nous avons écrit la macro suivante:
(defmacro backwards (expr) (reverse expr))
en arrière est le nom de la macro, qui prend une expression (représentée sous forme de liste) et l'inverse. Voici à nouveau "Bonjour, monde", cette fois en utilisant la macro:
(backwards ("hello, world" t format))
Lorsque le compilateur Lisp voit cette ligne de code, il examine le premier atome de la liste (
backwards
) et remarque qu'il nomme une macro. Il transmet la liste non évaluée("hello, world" t format)
à la macro, qui réorganise la liste en(format t "hello, world")
. La liste résultante remplace l'expression de macro et c'est ce qui sera évalué au moment de l'exécution. L'environnement Lisp verra que son premier atome (format
) est une fonction et l'évaluera en lui passant le reste des arguments.
En Lisp, cette tâche est facile (corrigez-moi si je me trompe) car le code est implémenté sous forme de liste ( expressions s ?).
Jetez maintenant un œil à cet extrait de code OCaml (qui n'est pas un langage homoiconique):
let print () =
let message = "Hello world" in
print_endline message
;;
Imaginez que vous souhaitiez ajouter une homoiconicité à OCaml, qui utilise une syntaxe beaucoup plus complexe que Lisp. Comment feriez-vous cela? Le langage doit-il avoir une syntaxe particulièrement simple pour atteindre l'homoiconicité?
EDIT : à partir de ce sujet, j'ai trouvé un autre moyen d'atteindre l'homoiconicité qui est différent de Lisp: celui implémenté dans le langage io . Il peut répondre partiellement à cette question.
Ici, commençons par un simple bloc:
Io> plus := block(a, b, a + b) ==> method(a, b, a + b ) Io> plus call(2, 3) ==> 5
D'accord, donc le bloc fonctionne. Le bloc plus a ajouté deux nombres.
Faisons maintenant une introspection sur ce petit bonhomme.
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 ==> +
Moule chaud et froid. Non seulement vous pouvez obtenir les noms des paramètres de bloc. Et non seulement vous pouvez obtenir une chaîne du code source complet du bloc. Vous pouvez vous faufiler dans le code et parcourir les messages à l'intérieur. Et le plus étonnant de tous: c'est terriblement facile et naturel. Fidèle à la quête d'Io. Le miroir de Ruby ne peut rien voir de tout ça.
Mais, whoa whoa, hé maintenant, ne touchez pas ce cadran.
Io> plus message next setName("-") ==> -(b) Io> plus ==> method(a, b, a - b ) Io> plus call(2, 3) ==> -1