Hay muchos idiomas que permiten algún tipo de metaprogramación . En particular, me sorprende no ver una respuesta sobre la familia de idiomas Lisp .
De wikipedia:
La metaprogramación es la escritura de programas informáticos con la capacidad de tratar los programas como sus datos.
Más adelante en el texto:
Lisp es probablemente el lenguaje por excelencia con facilidades de metaprogramación, tanto por su precedencia histórica como por la simplicidad y el poder de su metaprogramación.
Idiomas Lisp
Sigue una introducción rápida a Lisp.
Una forma de ver el código es como un conjunto de instrucciones: haz esto, luego haz eso, luego haz lo otro ... ¡Esta es una lista! Una lista de cosas que debe hacer el programa. Y, por supuesto, puede tener listas dentro de listas para representar bucles, etc.
Si representamos una lista que contiene los elementos a, b, c, d como esta: (ABCD) obtenemos algo que se parece a una llamada de función Lisp, donde a
es la función, y b
, c
, d
son los argumentos. De hecho, el típico "¡Hola Mundo!" programa podría escribirse así:(println "Hello World!")
Por supuesto b
, c
o d
podrían ser listas que evalúen algo también. Lo siguiente: (println "I can add :" (+ 1 3) )
luego imprimiría "" Puedo agregar: 4 ".
Entonces, un programa es una serie de listas anidadas, y el primer elemento es una función. ¡La buena noticia es que podemos manipular las listas! Entonces podemos manipular lenguajes de programación.
La ventaja de Lisp
Los Lisps no son tanto lenguajes de programación como un conjunto de herramientas para crear lenguajes de programación. Un lenguaje de programación programable.
Esto no solo es mucho más fácil en Lisps para crear nuevos operadores, sino que también es casi imposible escribir algunos operadores en otros idiomas porque los argumentos se evalúan cuando se pasan a la función.
Por ejemplo, en un lenguaje tipo C, supongamos que desea escribir un if
operador usted mismo, algo así como:
my-if(condition, if-true, if-false)
my-if(false, print("I should not be printed"), print("I should be printed"))
En este caso, ambos argumentos serán evaluados e impresos, en un orden dependiente del orden de la evaluación de los argumentos.
En Lisps, escribir un operador (lo llamamos macro) y escribir una función es casi lo mismo y se usa de la misma manera. La principal diferencia es que los parámetros de una macro no se evalúan antes de pasarlos como argumentos a la macro. Esto es esencial para poder escribir algunos operadores, como el if
anterior.
Idiomas del mundo real
Muestra cómo exactamente está un poco fuera de alcance aquí, pero le animo a que intente programar en un Lisp para obtener más información. Por ejemplo, podrías echar un vistazo a:
- Scheme , un viejo Lisp bastante "puro" con un núcleo pequeño
- Common Lisp, un Lisp más grande con un sistema de objetos bien integrado y muchas implementaciones (está estandarizado por ANSI)
- Racket a Lisp mecanografiado
- Clojure mi favorito, los ejemplos anteriores fueron el código Clojure. Un Lisp moderno que se ejecuta en la JVM. También hay algunos ejemplos de macros Clojure en SO (pero este no es el lugar correcto para comenzar. Primero miraría 4clojure , braveclojure o clojure koans )).
Ah, y por cierto, Lisp significa LISt Processing.
En cuanto a tus ejemplos
Voy a dar ejemplos usando Clojure a continuación:
Si puede escribir una add
función en Clojure (defn add [a b] ...your-implementation-here... )
, puede nombrarla +
así (defn + [a b] ...your-implementation-here... )
. De hecho, esto es lo que se hace en la implementación real (el cuerpo de la función está un poco más involucrado pero la definición es esencialmente la misma que escribí anteriormente).
¿Qué pasa con la notación infija? Bueno, Clojure usa una prefix
notación (o polaca), por lo que podríamos hacer una infix-to-prefix
macro que convertiría el código prefijado en código Clojure. ¡Lo que en realidad es sorprendentemente fácil (en realidad es uno de los ejercicios macro en los clojure koans)! También se puede ver en la naturaleza, por ejemplo, ver macro de Incanter$=
.
Aquí está la versión más simple de los koans explicados:
(defmacro infix [form]
(list (second form) (first form) (nth form 2)))
;; takes a form (ie. some code) as parameter
;; and returns a list (ie. some other code)
;; where the first element is the second element from the original form
;; and the second element is the first element from the original form
;; and the third element is the third element from the original form (indexes start at 0)
;; example :
;; (infix (9 + 1))
;; will become (+ 9 1) which is valid Clojure code and will be executed to give 10 as a result
Para llevar el punto aún más lejos, algunas citas de Lisp :
“Parte de lo que distingue a Lisp es que está diseñado para evolucionar. Puede usar Lisp para definir nuevos operadores Lisp. A medida que las nuevas abstracciones se vuelven populares (programación orientada a objetos, por ejemplo), siempre resulta fácil implementarlas en Lisp. Al igual que el ADN, ese lenguaje no pasa de moda ”.
- Paul Graham, ANSI Common Lisp
“Programar en Lisp es como jugar con las fuerzas primordiales del universo. Se siente como un rayo entre las yemas de los dedos. Ningún otro idioma se siente cercano ".
- Glenn Ehrlich, camino a Lisp