La metaprogramación es una técnica de programación informática en la que los programas informáticos tienen la capacidad de tratar a otros programas como si fueran sus datos . Esto significa que un programa puede diseñarse para leer, generar, analizar o transformar otros programas, e incluso modificarse a sí mismo, mientras se ejecuta. [1] [2] En algunos casos, esto permite a los programadores minimizar la cantidad de líneas de código para expresar una solución, lo que a su vez reduce el tiempo de desarrollo. [3] También permite a los programas una mayor flexibilidad para manejar de manera eficiente nuevas situaciones sin tener que volver a compilar.
La metaprogramación se puede utilizar para mover cálculos del tiempo de ejecución al tiempo de compilación , para generar código utilizando cálculos en tiempo de compilación y para permitir la automodificación del código . La capacidad de un lenguaje de programación de ser su propio metalenguaje permite la programación reflexiva , y se denomina reflexión . [4] La reflexión es una característica valiosa del lenguaje para facilitar la metaprogramación.
La metaprogramación fue popular en los años 1970 y 1980, cuando se usaban lenguajes de procesamiento de listas como Lisp . El hardware de la máquina Lisp ganó notoriedad en los años 1980 y permitió el desarrollo de aplicaciones que podían procesar código. A menudo se usaban para aplicaciones de inteligencia artificial .
La metaprogramación permite a los desarrolladores escribir programas y desarrollar código que se enmarca en el paradigma de programación genérica . También es muy útil que el lenguaje de programación en sí sea un tipo de datos de primera clase (como en Lisp , Prolog , SNOBOL o Rebol ); esto se conoce como homoiconicidad . La programación genérica invoca una función de metaprogramación dentro de un lenguaje al permitir escribir código sin la preocupación de especificar tipos de datos, ya que se pueden proporcionar como parámetros cuando se utilizan.
La metaprogramación generalmente funciona de una de tres maneras. [5]
Lisp es probablemente el lenguaje por excelencia con facilidades de metaprogramación, tanto por su precedencia histórica como por la simplicidad y potencia de su metaprogramación. En la metaprogramación de Lisp, el operador de comillas (normalmente una coma) introduce código que se evalúa en el momento de la definición del programa en lugar de en el momento de la ejecución. El lenguaje de metaprogramación es, por tanto, idéntico al lenguaje de programación anfitrión, y las rutinas Lisp existentes se pueden reutilizar directamente para la metaprogramación si se desea. Este enfoque se ha implementado en otros lenguajes incorporando un intérprete en el programa, que trabaja directamente con los datos del programa. Existen implementaciones de este tipo para algunos lenguajes de alto nivel comunes, como Pascal Script for Object Pascal de RemObjects .
Un ejemplo simple de un metaprograma es este script de Shell POSIX , que es un ejemplo de programación generativa :
#!/bin/sh # metaprograma echo '#!/bin/sh' > programa para i en $( seq 992 ) do echo "echo $i " >> programa hecho
chmod +x programa
Este script (o programa) genera un nuevo programa de 993 líneas que imprime los números del 1 al 992. Esto es solo una ilustración de cómo usar código para escribir más código; no es la forma más eficiente de imprimir una lista de números. No obstante, un programador puede escribir y ejecutar este metaprograma en menos de un minuto, y habrá generado más de 1000 líneas de código en ese lapso de tiempo.
Un quine es un tipo especial de metaprograma que produce su propio código fuente como salida. Los quines suelen tener un interés lúdico o teórico únicamente.
No toda la metaprogramación implica programación generativa. Si los programas se pueden modificar en tiempo de ejecución o si está disponible la compilación incremental (como en C# , Forth , Frink , Groovy , JavaScript , Lisp , Elixir , Lua , Nim , Perl , PHP , Python , Rebol , Ruby , Rust , R , SAS , Smalltalk y Tcl ), se pueden utilizar técnicas para realizar metaprogramación sin generar código fuente.
Un estilo de enfoque generativo es el empleo de lenguajes específicos de dominio (DSL). Un ejemplo bastante común de uso de DSL implica la metaprogramación generativa: lex y yacc , dos herramientas utilizadas para generar analizadores y analizadores léxicos , que permiten al usuario describir el lenguaje mediante expresiones regulares y gramáticas independientes del contexto , e incorporar los algoritmos complejos necesarios para analizar el lenguaje de manera eficiente.
Uno de los usos de la metaprogramación es instrumentar programas para realizar análisis dinámicos de programas .
Algunos argumentan que existe una curva de aprendizaje pronunciada para hacer un uso completo de las características de la metaprogramación. [8] Dado que la metaprogramación brinda más flexibilidad y capacidad de configuración en tiempo de ejecución, el uso incorrecto o incorrecto de la metaprogramación puede resultar en errores injustificados e inesperados que pueden ser extremadamente difíciles de depurar para un desarrollador promedio. Puede introducir riesgos en el sistema y hacerlo más vulnerable si no se usa con cuidado. Algunos de los problemas comunes que pueden ocurrir debido al uso incorrecto de la metaprogramación son la incapacidad del compilador para identificar los parámetros de configuración faltantes, los datos inválidos o incorrectos pueden resultar en una excepción desconocida o resultados diferentes. [9] Debido a esto, algunos creen [8] que solo los desarrolladores altamente capacitados deberían trabajar en el desarrollo de características que ejerciten la metaprogramación en un lenguaje o plataforma y los desarrolladores promedio deben aprender a usar estas características como parte de la convención.
El IBM/360 y sus derivados tenían potentes funciones de ensamblador de macros que se utilizaban a menudo para generar programas completos en lenguaje ensamblador [ cita requerida ] o secciones de programas (para diferentes sistemas operativos, por ejemplo). Las macros proporcionadas con el sistema de procesamiento de transacciones CICS tenían macros de ensamblador que generaban instrucciones COBOL como paso de preprocesamiento.
Otros ensambladores, como MASM , también admiten macros.
Las metaclases son proporcionadas por los siguientes lenguajes de programación:
El uso de tipos dependientes permite demostrar que el código generado nunca es inválido. [15] Sin embargo, este enfoque es de vanguardia y rara vez se encuentra fuera de los lenguajes de programación de investigación.
La lista de sistemas de metaprogramación notables se mantiene en Lista de sistemas de transformación de programas .