La explicación de Matt está perfectamente bien, y toma una oportunidad en comparación con C y Java, lo cual no haré, pero por alguna razón realmente disfruto discutiendo este tema de vez en cuando, así que aquí está mi oportunidad en una respuesta
En los puntos (3) y (4):
Los puntos (3) y (4) en su lista parecen ser los más interesantes y relevantes ahora.
Para comprenderlos, es útil tener una idea clara de lo que sucede con el código Lisp, en forma de una secuencia de caracteres ingresados por el programador, en camino a su ejecución. Usemos un ejemplo concreto:
;; 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"))
Este fragmento de código Clojure se imprime aFOObFOOcFOO
. Tenga en cuenta que podría decirse que Clojure no satisface completamente el cuarto punto de su lista, ya que el tiempo de lectura no está realmente abierto al código del usuario; Sin embargo, discutiré lo que significaría que esto sea de otra manera.
Entonces, supongamos que tenemos este código en un archivo en algún lugar y le pedimos a Clojure que lo ejecute. Además, supongamos (por simplicidad) que hemos superado la importación de la biblioteca. La parte interesante comienza en (println
y termina en el )
extremo derecho. Esto es lexed / parsed como uno esperaría, pero ya surge un punto importante: el resultado no es una representación AST específica del compilador específica: es solo una estructura de datos Clojure / Lisp normal , es decir, una lista anidada que contiene un montón de símbolos, cadenas y, en este caso, un único objeto de patrón regex compilado correspondiente a la#"\d+"
literal (más sobre esto a continuación). Algunos Lisps agregan sus propios pequeños giros a este proceso, pero Paul Graham se refería principalmente a Common Lisp. En los puntos relevantes para su pregunta, Clojure es similar a CL.
Todo el lenguaje en tiempo de compilación:
Después de este punto, todo lo que trata el compilador (esto también sería cierto para un intérprete de Lisp; el código Clojure siempre se compila) son estructuras de datos de Lisp que los programadores de Lisp están acostumbrados a manipular. En este punto, se hace evidente una posibilidad maravillosa: ¿por qué no permitir que los programadores de Lisp escriban funciones de Lisp que manipulan datos de Lisp que representan programas de Lisp y generan datos transformados que representan programas transformados, para ser utilizados en lugar de los originales? En otras palabras, ¿por qué no permitir que los programadores de Lisp registren sus funciones como complementos de compilación, llamados macros en Lisp? Y, de hecho, cualquier sistema Lisp decente tiene esta capacidad.
Entonces, las macros son funciones regulares de Lisp que operan en la representación del programa en tiempo de compilación, antes de la fase final de compilación cuando se emite el código objeto real. Dado que no hay límites en los tipos de código que se permite ejecutar las macros (en particular, el código que ejecutan a menudo se escribe con el uso liberal de la función de macro), se puede decir que "todo el lenguaje está disponible en tiempo de compilación ".
Todo el idioma en el momento de la lectura:
Volvamos a ese #"\d+"
literal regex. Como se mencionó anteriormente, esto se transforma en un objeto de patrón compilado real en el momento de la lectura, antes de que el compilador escuche la primera mención del nuevo código que se está preparando para la compilación. ¿Como sucedió esto?
Bueno, la forma en que Clojure se implementa actualmente, la imagen es algo diferente de lo que Paul Graham tenía en mente, aunque todo es posible con un truco inteligente . En Common Lisp, la historia sería un poco más limpia conceptualmente. Sin embargo, los conceptos básicos son similares: el Lisp Reader es una máquina de estados que, además de realizar transiciones de estado y eventualmente declarar si ha alcanzado un "estado de aceptación", escupe las estructuras de datos de Lisp que representan los caracteres. Por lo tanto, los caracteres se 123
convierten en el número, 123
etc. El punto importante viene ahora: esta máquina de estado puede modificarse por código de usuario. (Como se señaló anteriormente, eso es completamente cierto en el caso de CL; para Clojure, se requiere un truco (desalentado y no utilizado en la práctica). Pero estoy divagando, es el artículo de PG sobre el que se supone que estoy elaborando, así que ...)
Entonces, si eres un programador de Common Lisp y te gusta la idea de los literales vectoriales de estilo Clojure, puedes conectar al lector una función para reaccionar adecuadamente a alguna secuencia de caracteres, [
o #[
posiblemente, y tratarla como El comienzo de un vector literal que termina en la coincidencia ]
. Dicha función se denomina macro de lector y, al igual que una macro normal, puede ejecutar cualquier tipo de código Lisp, incluido el código que ha sido escrito con notación funky habilitada por macros de lector registradas previamente. Así que hay todo el lenguaje en el momento de la lectura para ti.
Envolviendolo:
En realidad, lo que se ha demostrado hasta ahora es que uno puede ejecutar funciones regulares de Lisp en tiempo de lectura o tiempo de compilación; el único paso que se debe tomar desde aquí para comprender cómo la lectura y la compilación son posibles en el tiempo de lectura, compilación o ejecución es darse cuenta de que la lectura y la compilación son realizadas por las funciones de Lisp. Puede llamar read
o eval
en cualquier momento para leer datos de Lisp de secuencias de caracteres o compilar y ejecutar el código de Lisp, respectivamente. Ese es todo el lenguaje allí, todo el tiempo.
Observe cómo el hecho de que Lisp satisface el punto (3) de su lista es esencial para la forma en que logra satisfacer el punto (4): el sabor particular de las macros proporcionadas por Lisp depende en gran medida del código representado por datos regulares de Lisp, que es algo habilitado por (3). Por cierto, solo el aspecto de "código de árbol" del código es realmente crucial aquí: posiblemente podría tener un Lisp escrito usando XML.