Matt explication est parfaitement bien, et il prend un tir à une comparaison avec C et Java, je ne vais pas le faire -- mais pour certaines raisons, j'ai vraiment plaisir à discuter de ce sujet de temps en temps, donc voici mon coup à une réponse.
Sur les points (3) et (4):
Les Points (3) et (4) sur votre liste semble le plus intéressant et encore pertinents aujourd'hui.
Pour les comprendre, il est utile d'avoir une image claire de ce qui se passe avec code Lisp-sous la forme d'un flux de caractères saisie par le programmeur -- sur sa façon d'être exécuté. Prenons un exemple concret:
;; a library import for completeness,
;; we won't concern ourselves with it
(require '[clojure.contrib.string :as str])
;; this is the interesting bit:
(println (str/replace-re #"\d+" "FOO" "a123b4c56"))
Cet extrait de Clojure code imprime aFOObFOOcFOO
. Notez que Clojure sans doute ne satisfait pas pleinement le quatrième point sur votre liste, car lisez-le temps n'est pas vraiment s'ouvrir à l'utilisateur le code; je vais discuter de ce que cela signifierait pour que cela soit autrement, si.
Alors, admettons que nous avons ce code dans un fichier quelque part et nous demandons à Clojure pour l'exécuter. Aussi, supposons (pour des raisons de simplicité) que nous avons passé la bibliothèque d'importation. Le morceau intéressant commence à (println
et se termine à l' )
loin vers la droite. C'est lexed / analysée comme on pouvait s'y attendre, mais déjà un point important se pose: le résultat n'est pas spécial à compilateur spécifique AST représentation, c'est juste un régulier Clojure / Lisp structure de données, à savoir une liste imbriquée contenant un tas de symboles, de cordes et de -- dans ce cas, un seul compilé regex modèle de l'objet correspondant à l' #"\d+"
littérale (en savoir plus sur ce ci-dessous). Certains Lisps ajouter leur propre peu de rebondissements pour ce processus, mais Paul Graham a été essentiellement se référant à la Common Lisp. Sur les points pertinents à votre question, Clojure est similaire à la CL.
L'ensemble de la langue au moment de la compilation:
Après ce point, tout le compilateur traite (ce serait également vrai pour un interpréteur Lisp; Clojure code arrive toujours à être compilé) est Lisp structures de données programmeurs Lisp sont utilisés pour la manipulation. À ce stade, une merveilleuse possibilité devient évident: pourquoi ne pas permettre Lisp les programmeurs d'écrire des fonctions Lisp qui manipulent Lisp données représentant programmes Lisp et de sortie des données transformées représentant transformé programmes, pour être utilisé à la place des originaux? En d'autres mots, pourquoi ne pas autoriser les programmeurs Lisp pour inscrire leurs fonctions en tant que compilateur plugins de toutes sortes, appelé macros en Lisp? Et, en effet, que tout bon système Lisp a cette capacité.
Ainsi, les macros sont régulièrement fonctions Lisp d'exploitation sur le programme de la représentation au moment de la compilation, avant la fin de la phase de compilation lorsque l'objet même code est émis. Depuis il n'y a pas de limites sur les types de code macros sont autorisées à s'exécuter (en particulier, le code qui ils courent est souvent lui-même écrit avec de la libre utilisation de la fonction macro), on peut dire que "toute langue est disponible au moment de la compilation".
L'ensemble de la langue au temps de lecture:
Revenons à qui #"\d+"
regex littérale. Comme mentionné ci-dessus, cela se transforme en un véritable compilé modèle de l'objet à lire l'heure, avant le compilateur entend la première mention du nouveau code, être préparé pour la compilation. Comment est-ce possible?
Eh bien, la façon Clojure est actuellement mis en œuvre, la situation est quelque peu différente de ce que Paul Graham avait à l'esprit, même si tout est possible avec un astucieux hack. En Common Lisp, l'histoire serait un peu plus propre sur le plan conceptuel. Les bases sont cependant similaires: le Lecteur Lisp est une machine d'état qui, en plus d'effectuer la transition de l'état et, éventuellement, de déclarer si elle a atteint un "état acceptant", crache Lisp structures de données les personnages représentent. Ainsi, les personnages 123
devenir le nombre 123
etc. Le point important est maintenant: cette machine d'état peut être modifié par le code de l'utilisateur. (Comme indiqué plus haut, c'est tout à fait vrai dans la CL; pour Clojure, un hack (déconseillé & pas utilisé dans la pratique) est nécessaire. Mais je m'égare, c'est PG de l'article, je suis censé être en élaborant, donc...)
Donc, si vous êtes un Common Lisp programmeur et que vous aimez l'idée de Clojure-style vecteur littéraux, il vous suffit de brancher dans le lecteur d'une fonction à réagir de manière appropriée à certains séquence de caractères -- [
ou #[
éventuellement -- et le considérer comme le début d'un vecteur littérale se terminant à la correspondance ]
. Une telle fonction est appelée un lecteur de macro et une macro, il peut exécuter n'importe quelle sorte de code Lisp, y compris le code qui a été écrit avec funky notation activé précédemment enregistré lecteur de macros. Donc, il y a toute la langue à lire de temps pour vous.
Conclusion:
En fait, ce qui a été fait jusqu'à présent est que l'on peut exécuter régulièrement des fonctions Lisp à lire l'heure ou le moment de la compilation; la seule étape que l'on doit prendre à partir de là pour comprendre comment la lecture et de la compilation sont eux-mêmes possible à lire, de compilation ou de l'exécution, c'est réaliser que la lecture et de la compilation sont elles-mêmes réalisées par des fonctions Lisp. Vous pouvez les appeler read
ou eval
à tout moment à lire en Lisp de données de flux de caractères ou de compiler & exécuter du code Lisp, respectivement. C'est l'ensemble de la langue-là, tout le temps.
Notez comment le fait que Lisp, satisfait point (3) à partir de votre liste est essentiel à la façon dont elle parvient à satisfaire le point (4) -- la saveur particulière de macros fournies par Lisp s'appuie fortement sur le code représenté par la régularité de Lisp de données, ce qui est quelque chose activé par (3). D'ailleurs, seulement "l'arbre-ish" aspect du code qui est vraiment essentiel, ici, vous pourrait avoir un Lisp écrite à l'aide de XML.