domingo, 12 de abril de 2015

Metaprogramación: Programas que hacen programas

La metaprogramación o programación genérica es más antigua de lo que se puede pensar.  Es un concepto que trata de programas que hacen programas, y según esta definición, es una programación que manipula los símbolos de otro lenguaje como si fuesen datos de sus variables, formando sentencias correctas del lenguaje manipulado, para después ser ejecutadas como las sentencias del lenguaje manipulador.  

En general, son mecanismos que permite a un programador ganar una mayor compresión y expresividad en sus programas.  En un principio la metaprogramación surgió como herramienta para mejorar la productivada de los ingenieros de software. 

Nos podemos preguntar,  ¿no es suficiente complejidad un sólo lenguaje de programación?, ¿por qué deberíamos utilizar este tipo de recursividad?.
 Veamos algunos ejemplos, comenzando con el primer metaprograma, sin ir mas lejos sería el compilador. 

Obra del pintor M.C. Escher que inspirar el concepto de recursividad 


Los Compiladores 


Un compilador permite manejar indirectamente los 0s y 1s de un ordenador, a través de un conjunto de instrucciones y sentencias bien definidos y estructurados.  Los compiladores son un tema muy extenso, y no es mi intención hacer una entrada sobre ellos, aunque los describiré brevemente, ya que son una forma de metaprogramción.

Proceso de un compliador: Léxico, Sintáctico, Semántico, Optimización y Generación.


En la imagen anterior, se describe el proceso que sigue un compilador con su lenguaje fuente a compliar.  En primer lugar, a partir del código fuente, se verifica el léxico de cada sentencia. Segundo, se hace una parsing o análisis sintáctico y semántico. Además se realiza una optimización del código intermedio antes de generar el código en ensamblador y finalmente en código máquina.
En conclusión, se puede ver un compilador cómo programa que analiza un lenguaje para obtener otro lenguaje. Es posible, que no sea estrictamente un metalenguaje, ya que un compliador no expresa, procesa, y puede ser visto quizá como un simple traductor. De cualquier forma, existen programas generadoras de analizadores léxicos y sintácticos como Lex y Yacc, que potencian y elevan a los compiladores a una gramática formal.


PHP formateando HTML

PHP es un lenguaje orientado a WEB, interpretado y  ejecutado en el servidor web. El código PHP se encarga de reunir los datos de diferentes fuentes, formatearlos en una presentación utilizando etiquetas HTML, y enviarlos al navegador del cliente o usuario.

Funcionamiento del código PHP en un entorno de servicio web



Veamos el ejemplo mas sencillo posible.

<!DOCTYPE html>
<html>
<body>

<?php
$txt = "Metaprogramacion en Estalogica!";
$x = 5;
$y = 10.5;

echo $txt;
echo "
<br>";
echo $x;
echo "
<br>";
echo $y;
?>


</body>
</html>


Estamos ante un lenguaje de programación que manipula símbolos de otro lenguaje de programación, es por tanto, que se puede decir que estamos haciendo metaprogramación sobre HTML.
En el listado anterior, en color rojo, el código PHP, que incluye en sus datos, código HTML.
Entonces, tenemos que PHP genera código HTML a partir de sus variables. Podemos presentar el código HTML intermedio como sigue:

<!DOCTYPE html>
<html>
<body>
MetaProgramacion en Estalogica!
<br>5
<br>10.5
</body>
</html>

Finalmente la ejecución del HTML anterior producirá en el navegador del usuario la salida:

MetaProgramacion en EstaLogica!
5
10.5

Plantillas de C++


En esta subsección, presentaré la programación genérica de C++, la cual hace uso de tipos de datos arbitrarios dentro de los programas. Se habla de plantillas o templates, los cuales, permite manipular símbolos representando estos tipos de datos abstractos, y sobre los cuales se aplica un procedimiento genérico (supongo que el lector conoce la sintaxis básica de C++, en otro caso no estarías aquí leyendo esta entrada).

Diagrama de los mecanismos de metaprogramación


Veamos un ejemplo sencillo. Suponer que queremos un código genérico para  calcular el máximo de dos números. Si queremos que los números sean entereos, utilizaremos el tipo de datos int .

int maximo(int a, int b )
{
   if (a<b)
      return b;
   return a;
}

En cambio, si queremos que sean número con decimales, utilizaremos el tipo de datos float.

float maximo(float a, float b )
{
   if (a<b)
      return b;
   return a;
}

Como podemos observar, el cuerpo es exactamente igual, y lo único que cambia son las palabras clave float e int indicando del tipo de dato.  ¿Cómo podemos fusionar ambos códigos en unos solo?, para esto utilizaremos los templates de C++, como se puede ver a continuación:

template <class TipoGenerico>
TipoGenerico maximo(TipoGenerico a, TipoGenerico b )
{
   if (a<b)
     return b;
   return a;
}

La utilización de la función, puede hacerse en ambos casos, tanto si los tipos de datos de los argumentos son enteros como flotantes,

....
int a=5, int b=8, c;
float d=3.0, e=5.1, f;
c=maximo(a,b);
f=maximo(d,e);
...

Estamos ante un ejemplo de metaprogramación, desde que hacemos referencia a la forma de especificar en C++ los tipos de datos.