Common Lisp le permite escribir macros que realizan cualquier transformación de origen que desee.
Scheme le ofrece un sistema higiénico de coincidencia de patrones que también le permite realizar transformaciones. ¿Qué tan útiles son las macros en la práctica? Paul Graham dijo en Beating the Averages que:
El código fuente del editor de Viaweb era probablemente de un 20-25% de macros.
¿Qué tipo de cosas terminan haciendo las personas con macros?
Respuestas:
Eche un vistazo a esta publicación de Matthias Felleisen en la lista de discusión de LL1 en 2002. Sugiere tres usos principales para las macros:
fuente
Principalmente uso macros para agregar nuevas construcciones de lenguaje que ahorran tiempo, que de lo contrario requerirían un montón de código repetitivo.
Por ejemplo, recientemente me encontré queriendo un imperativo
for-loop
similar a C ++ / Java. Sin embargo, al ser un lenguaje funcional, Clojure no vino con uno fuera de la caja. Así que lo implementé como una macro:Y ahora puedo hacer:
Y ahí lo tiene: una nueva construcción de lenguaje de tiempo de compilación de propósito general en seis líneas de código.
fuente
Escritura de extensiones de lenguaje o DSL's.
Para tener una idea de esto en lenguajes similares a Lisp, estudie Racket , que tiene varias variantes de idioma: Typed Racket, R6RS y Datalog.
Consulte también el lenguaje Boo, que le brinda acceso a la canalización del compilador para el propósito específico de crear lenguajes específicos de dominio a través de macros.
fuente
Aquí hay unos ejemplos:
Esquema:
define
para definiciones de funciones. Básicamente, hace una forma más corta de definir una función.let
para crear variables con ámbito léxico.Clojure:
defn
, de acuerdo con sus documentos:Igual que (nombre de def (fn [params *] exprs *)) o (nombre de def (fn ([params *] exprs *) +)) con cualquier cadena de documentos o atributos agregados a los metadatos var
for
: lista de comprensionesdefmacro
: irónico?defmethod
,defmulti
: trabajando con métodos múltiplesns
Muchas de estas macros hacen que sea mucho más fácil escribir código en un nivel más abstracto. Pienso que las macros son similares, en muchos sentidos, a la sintaxis en no Lisps.
La biblioteca de trazado Incanter proporciona macros para algunas manipulaciones de datos complejas.
fuente
Las macros son útiles para incrustar algunos patrones.
Por ejemplo, Common Lisp no define el
while
ciclo pero tienedo
que puede usarse para definirlo.Aquí hay un ejemplo de On Lisp .
Esto imprimirá "12345678910", y si intenta ver qué sucede con
macroexpand-1
:Esto devolverá:
Esta es una macro simple, pero como se dijo antes, generalmente se usan para definir nuevos lenguajes o DSL, pero a partir de este simple ejemplo, ya puede intentar imaginar qué puede hacer con ellos.
La
loop
macro es un buen ejemplo de lo que pueden hacer las macros.Common Lisp tiene otro tipo de macros llamado macro lector que se puede usar para modificar la forma en que el lector interpreta el código, es decir, puede usarlas para usar # {y #} tiene delimitadores como # (y #).
fuente
Aquí hay uno que uso para depurar (en Clojure):
Tuve que lidiar con una tabla hash enrollada a mano en C ++, donde el
get
método tomó una referencia de cadena no constante como argumento, lo que significa que no puedo llamarlo con un literal. Para facilitar el tratamiento, escribí algo como lo siguiente:Si bien es poco probable que surja algo como este problema, me parece particularmente agradable que pueda tener macros que no evalúen sus argumentos dos veces, por ejemplo, introduciendo un verdadero enlace . (Admitido, aquí podría haberlo evitado).
También recurro al truco terriblemente feo de envolver cosas en un
do ... while (false)
tal manera que puedas usarlo en la parte de un if y aún tener la parte de más que funcione como se esperaba. No necesita esto en lisp, que es una función de macros que operan en árboles de sintaxis en lugar de cadenas (o secuencias de tokens, creo, en el caso de C y C ++) que luego se analizan.Hay algunas macros de subprocesamiento incorporadas que se pueden usar para reorganizar su código de modo que se lea de manera más limpia ('subprocesamiento' como en 'sembrar su código', no paralelismo). Por ejemplo:
Toma la primera forma,
(range 6)
y lo convierte en el último argumento de la siguiente forma,(filter even?)
que a su vez se convierte en el último argumento de la siguiente forma y así sucesivamente, de modo que lo anterior se reescribe enCreo que el primero se lee mucho más claramente: "toma estos datos, haz esto, luego haz eso, luego haz lo otro y listo", pero eso es subjetivo; algo que es objetivamente cierto es que lees las operaciones en la secuencia en que se realizan (ignorando la pereza).
También hay una variante que inserta la forma anterior como el primer argumento (en lugar del último). Un caso de uso es la aritmética:
Se lee como "toma 17, resta 2 y divide entre 3".
Hablando de aritmética, puede escribir una macro que analice la notación infijada, de modo que pueda decir, por ejemplo,
(infix (17 - 2) / 3)
y escupe(/ (- 17 2) 3)
cuál tiene la desventaja de ser menos legible y la ventaja de ser una expresión válida de lisp. Esa es la parte del sublenguaje DSL / datos.fuente