La metaprogramación es una técnica de programación en la que los programas de computadora tienen la capacidad de tratar otros programas como sus datos. 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 el número 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 eficientemente 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 habilitar código que se modifica automáticamente . La capacidad de un lenguaje de programación para ser su propio metalenguaje se llama reflexión . [4] La reflexión es una característica valiosa del lenguaje para facilitar la metaprogramación.
La metaprogramación fue popular en las décadas de 1970 y 1980 utilizando lenguajes de procesamiento de listas como LISP . Las máquinas de hardware LISP fueron populares en la década de 1980 y permitieron aplicaciones que podían procesar código. Se utilizaban frecuentemente para aplicaciones de inteligencia artificial .
La metaprogramación permite a los desarrolladores escribir programas y desarrollar código que se ajuste al paradigma de programación genérica . Tener el propio lenguaje de programación como tipo de datos de primera clase (como en Lisp , Prolog , SNOBOL o Rebol ) también es muy útil; 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 usan.
La metaprogramación suele funcionar de tres maneras. [5]
Lisp es probablemente el lenguaje por excelencia con posibilidades de metaprogramación, tanto por su precedencia histórica como por la simplicidad y el poder de su metaprogramación. En la metaprogramación Lisp, el operador sin 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 tiempo de ejecución; consulte Formularios de autoevaluación y citas en Lisp . Por tanto, el lenguaje de metaprogramación es 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 idiomas incorporando un intérprete en el programa, que trabaja directamente con los datos del programa. Existen implementaciones de este tipo para algunos lenguajes comunes de alto nivel, como RemObjects ' Pascal Script para Object Pascal .
Un ejemplo simple de metaprograma es este script POSIX Shell , que es un ejemplo de programación generativa :
#!/bin/sh # metaprograma echo '#!/bin/sh' > programa para i en $( seq 992 ) haz echo "echo $i " >> programa terminado
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 sólo una ilustración de cómo usar código para escribir más código; No es la forma más eficaz 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 período de tiempo.
Un quine es un tipo especial de metaprograma que produce su propio código fuente como salida. Los quines generalmente tienen sólo un interés recreativo o teórico.
No toda la metaprogramación implica programación generativa. Si los programas se pueden modificar en tiempo de ejecución o si hay compilación incremental disponible (como en C# , Forth , Frink , Groovy , JavaScript , Lisp , Elixir , Lua , Nim , Perl , PHP , Python , REBOL , Ruby , Rust , SAS , Smalltalk , y Tcl ), entonces se pueden utilizar técnicas para realizar metaprogramación sin generar código fuente.
Un estilo de enfoque generativo es emplear lenguajes de dominio específico (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 , permiten al usuario describir el lenguaje usando expresiones regulares y gramáticas libres de contexto , e incorporar los algoritmos complejos necesarios para analizar de manera eficiente el idioma.
Un uso 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 funciones de metaprogramación. [8] Dado que la metaprogramación brinda más flexibilidad y capacidad de configuración en tiempo de ejecución, el mal uso o el uso 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 utiliza 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, datos no válidos o incorrectos que pueden generar excepciones desconocidas o resultados diferentes. [9] Debido a esto, algunos creen [8] que solo los desarrolladores altamente capacitados deberían trabajar en el desarrollo de funciones que ejerciten la metaprogramación en un lenguaje o plataforma y los desarrolladores promedio deben aprender a usar estas funciones como parte de la convención.
IBM /360 y sus derivados tenían potentes funciones de macroensamblador que a menudo se usaban para generar programas completos en lenguaje ensamblador [ cita necesaria ] o secciones de programas (para diferentes sistemas operativos, por ejemplo). Las macros proporcionadas con el sistema de procesamiento de transacciones CICS tenían macros ensambladoras que generaban declaraciones COBOL como paso previo al procesamiento.
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 .