Tuesday, June 24, 2008

Inferencia de tipos de variables locales

Meta D++ (el único lenguaje LayerD de alto nivel funcional hoy por hoy) basa su semántica en una copia exacta del lenguaje intermedio de la plataforma LayerD, el lenguaje Zoe.

Zoe posee características de orientación a objetos básica como polimorfismo, ocultación y herencia. Sin embargo, algunas características disponibles en otros lenguajes de programación orientados a objetos no se encuentran disponibles nativamente. Por otro lado, Zoe (y por tanto todo lenguaje LayerD de alto nivel como Meta D++) posee un diseño modular y procesamiento programable en "tiempo de compilación". Estas características de Zoe permite programar extensiones para los lenguajes LayerD de alto nivel sin depender de los fabricantes o diseñadores de lenguajes.

En esta nota mostraré cómo es posible incorporar en Zoe (y por extensión en Meta D++) inferencia de tipos para variables locales. Esta idea es desarrollada en algunos lenguajes como C# a partir de la versión 3.0 y trata simplemente de no requerir duplicar el tipo en una declaración "infiriéndolo" de su inicialización, por ejemplo en lugar de escribir:

int a = 12;
ArrayList lista = new ArrayList();
MiTipo[] matriz = new MiTipo[4];

En C# a partir de la versión 3.0 se puede escribir lo siguiente:

var a = 12;
var lista = new ArrayList();
var matriz = new MiTipo[4];

El lenguaje Meta D++ no soporta esta capacidad nativamente que en ocasiones suele ser útil permitiendo escribir unas cuantas letras menos a los programadores y también mejorar la legibilidad del código eliminando la redundancia. Pero ello no es limitación para incorporar la característica en Zoe usando una classfactory y luego poder usarla desde Meta D++.

El código en Meta D++ para incluir esta capacidad usando una classfactory es el siguiente:

public factory class var{
public:

//Declaro el constructor de tipo por defecto
type var(){
    try{
        //Si no es llamado como parte de una
        //declaracion de variable local es un error
01        if(context.get_Parent().get_TypeName()!="XplDeclarator")
            return new XplType();
        //Obtengo la declaración
02        XplDeclarator^ decla = (XplDeclarator^)context.get_Parent();
        //Busco la primera expresion de inicialización
03        if(decla.FindNode("/e")!=null){
            //Determino el tipo a partir de la expresion de inicialización
04            return ZoeHelper::MakeTypeFromString(    ((XplExpression^)decla.FindNode("/e")).get_typeStr() );
        }
        else{
            //Si no posee expresion de inicializacion
            //busco la primera asignación
05            XplNode^ temp = context.CurrentBlock.FindNode(
                        "/a/l/n("+decla.get_name()+")");
            if(temp!=null){
                //Determino el tipo a partir de la expresion
                //derecha de la primera asignación
06                return ZoeHelper::MakeTypeFromString( ((XplAssing^)temp).get_r().get_typeStr() );
            }
        }
    }
    catch(Exception^ error){
        //Si hubo algun error emito un mensaje y luego genero un error
07        Console::WriteLine(error.Message);
    }
    //Esto generará un error de compilación
08    return new XplType();
}

}

Efectivamente el código para agregar inferencia de tipos de variables locales lleva apenas 12 líneas de código efectivo en Meta D++. Y permite ser utilizado desde un programa cliente Meta D++, por ejemplo como sigue:

static int Main(string^[] args){
    ArrayList^ test = new ArrayList();
    ///Insert code here
    var n = "hola";
    var matriz = new int[] = {1,2,3,4};
    var matriz2 = new App[4];
    var matriz3;
    matriz3 = new int[10];
    var j;
    j = 12.4f;
    var y;
    y = new App();
    return 0;
}

El ejemplo de programa cliente inferirá que "n" es de tipo "string^", matriz de tipo "int[]", j de tipo "float", etc.

¿Cómo funciona la classfactory?

La classfactory "var" utiliza un tipo de constructores especiales accesibles por classfactorys. Estos constructores se denominan "Constructores de Tipo", específicamente el usado es un "constructor de tipo por defecto" ya que no toma ningún parámetro.

Los constructores de tipos en classfactorys son llamados cada vez que un programa cliente declara una variable o nombra el tipo de la classfactory, en el caso del ejemplo cada vez que se declara una variable local de tipo "var".

A continuación explico cada una de las líneas marcadas con números del programa:

01 – Si no se trata de una declaración de variable local retorno un tipo vacio (usando "new XplType()") lo cual generará un error en el programa cliente, en este caso es lo que deseo.
02 – Obtengo el nodo de declaración, es decir el padre del contexto de llamada al constructor de tipo.
03 – Busco la primera expresión a partir del nodo de declaración, la cual será la expresión de inicialización.
04 – Si encuentro una expresión de inicialización utilizo el tipo de dicha expresión para inferir el tipo de la variable.
05 – Si no encuentro una expresión de inicialización busco una asignación.
06 – Si no encuentro una asignación es un error, emito un tipo vacio para generar el error.
07 – Si ocurre un error inesperado cualquiera capturo la excepción.
08 – Si la ejecución llega a la última línea es un error, retorno un tipo vacio para generar el error.

Resumen

Para agregar una nueva funcionalidad, en este caso inferencia de tipo de variables locales, basta con desarrollar una classfactory de una decena de líneas. Luego, la funcionalidad esta disponible en cualquier lenguaje LayerD de alto nivel sin necesitar cambiar ningún compilador o modulo generador de código, simplemente código en el mismo lenguaje que el resto de un programa ordinario LayerD. Por supuesto que no todas las nuevas funciones que querramos agregar a los lenguajes LayerD serán tan fácil de desarrollar, sin embargo, si poseeremos en nuestras manos las herramientas y la libertad para hacerlo cuando creamos que lo necesitamos sin depender de los diseñadores de los lenguajes o los fabricantes de los compiladores.

El ejemplo sencillo presentado se puede mejorar emitiendo errores usando la interfaz "compiler" o incluso permitir la inferencia de variables locales sin utilizar el tipo "var". Más interesante, toda extensión semántica para el lenguaje, como la presentada, la podemos usar sólo donde lo queremos e incluso combinar funcionalidad.

Otros tipos de extensiones semánticas que podemos desarrollar para cualquier lenguaje LayerD, por nombrar algunas, incluyen: tipos genéricos, aspectos, tipos anónimos, precondiciones y postcondiciones, tareas, bloques para ejecución en paralelo, bloques for concurrentes para matrices o colecciones, estructuras de sincronización, bloques de recuperación, y un etcétera tan largo como tu propia imaginación y capacidad.

Monday, June 9, 2008

Un lenguaje de dominio especifico para interfaces gráficas

Muchas veces me paso que al desarrollar una aplicación de escritorio no incluí una ventana más para evitar el tedio de tener que abrir el diseñador de ventanas del entorno y genera otro archivo y otra clase más con todo el código generado (que en general es terrible je). Por tanto, para evitarme esa complicación termine reutilizando otra ventana que ya tenía aunque no fuera lo más adecuado.

Por otro lado, si hemos tenido la oportunidad de programar interfaces gráficas en más de un lenguaje o en más de una API aún en el mismo lenguaje, sabremos que cada quién trabaja las interfaces como quiere. En definitiva una ventana con un textbox y un botón aceptar, es eso. Sin embargo, si lo programamos en C# es de una forma con Windows Form, con WPF es de otra manera, si usamos C++ lo podemos hacer con el API de Windows, con MFC, con GTK o cualquier otra librería, en Java lógicamente es distinto, en Javascript también por supuesto es diferente. En cada plataforma los eventos se enlazan de manera diferente, los contenedores trabajan distinto, entre otros problemas.

El problema que encara el ejemplo del post, es la complejidad del código y la poca legibilidad. Por ejemplo, si construimos una ventana con C# usando Windows forms debemos de hacer una clase que herede de System.Windows.Forms.Form, luego estableceremos las propiedades en el constructor o en algún otro método. Los controles internos también los deberemos de definir en general como campos, agregarlos al contenedor, setearle las propiedades, capturar los eventos, etc. Todo este código hace que al final tengamos una clase la cual, si no fuera por la ayuda de los IDEs nos sería complicado de manipular y entender.

En lugar de tener que escribir todo ese código no necesariamente intuitivo para quien no uso antes la librería, en LayerD podemos desarrollar una classfactory que nos permita declarar y utilizar interfaces graficas de la siguiente forma:

GUI::Window(MiVentana3){

    Text = "Una ventana en un DSL con LayerD";

    Controls{

        Button(CSalir){

            Text="Salir"; AutoSize=true;

            Click{

                MessageBox::Show("Saliendo de la aplicación");

                this.Close();

            };

        };

    };

};

El código superior casi no necesita explicación si se toma el trabajo de leerlo. Declara una ventana cuyo nombre será "MiVentana3", establece el título de la ventana con "Text = "Una ventana en un DSL con LayerD"". Luego se especifican los controles directamente dentro del bloque "Controls". El ejemplo muestra un botón "CSalir" al cual se capturo el evento "Click" simplemente escribiendo "Click{…}" y proporcionando la implementación del evento entre llaves, las propiedades del botón se establecieron como simples asignaciones "Text = "Salir";" y "AutoSize = true;".

El lenguaje de dominio específico está pensado de tal forma que es posible utilizar cualquier clase de Windows Forms directamente dentro de la colección de controles, setear propiedades y capturar eventos de forma directa usando sólo el nombre del evento y proporcionando la implementación en un bloque.

En el SDK de LayerD podrá encontrar un ejemplo de uso de este lenguaje de dominio específico "EjemploGUI.dpp" y la classfactory que implementa el lenguaje de dominio especifico en "Zoe.DotNET.UtilsTemplate.dpp". Puede insertar un ejemplo en su código usando el modo interactivo con la instrucción "Zoe::DotNET::Utils::iGUI::Help()" (o sólo "iGUI::Help()" si ya tiene un using "Zoe::DotNET::Utils").

Si comparamos el código superior con el código de una ventana en C# o Java es muy probable que le parezca mucho más claro, más fácil de leer y hasta se anime a escribirlo a mano sin la ayuda de un diseñador :-) .

Por cierto, la classfactory que implementa este lenguaje de dominio específico no tiene miles de líneas de código :-). El ejemplo, encara uno de los problemas de las GUI en muchos de los lenguajes actuales volviendo el código de GUIs fácil de escribir, leer y mantener (lo cual no es necesariamente poco), pero sólo sirve para ser utilizada para desarrollar GUIs para .NET usando Windows Forms. Una mejora de este DSL será agregar la capacidad multiplataforma y permitir que sirva para Windows Forms, WPF, ASP.NET y Java. Ello es perfectamente posible en LayerD, simplemente hay que trabajar un poquito más :-), con seguridad uno de los ejemplos futuros.

A modo de avance les paso un ejemplo de un GUI similar para hacer paginas ASP.NET en LayerD:

///Pagina ASP.NET 2.0 implementada con un DSL

ASPNET::Page(TestPage){

    Title = "La primera pagina ASPNET en un DSL con LayerD";

    Controls{

    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">";

    "<html xmlns=\"http://www.w3.org/1999/xhtml\" >";

    "<head>";

    "<title>Pagina ASP.NET 2.0 implementada con un DSL usando Meta D++.</title>";

    "</head>";

    "<body>";

    "<h2>El título de mi primera pagina ASP.NET en LayerD</h2>";

    "<br/>";

    HtmlForm(form1){

    Controls{

        Label(l2){

            CssClass="miClaseCSS";

            Text="Label Inicio";            

        };

        "<br/>";

        Calendar(c1){

            ID="c1";            

        };

        "<br/>";

        Button(CSalir){

            Text="¡Hacer Algo!";            

            Click{

                l2.Text="Hola Mundo desde el Server";

            };

        };

        "<br/>";

        "<br/>";

        Button(CCambiar){

            Text="Cambiar página";

            Click{

                Response.Redirect("testajax.aspx");

            };

        };

    };

    };

    "</body>";

    "</html>";

    };

};

///Fin Pagina ASP.NET 2.0 implementada con un DSL

Hasta la próxima!!

Sunday, June 8, 2008

Mis colegas me deprimen versión 2.0

En un post anterior comentaba como "Mis colegas me deprimen" y hablaba de la poca "ingeniería" que a mi parecer enseñan aún excelentes profesores y como muchas de las "tecnologías" propuestas por las grandes empresas son "patéticas". A varias personas les pareció muy crítico, por tanto aprovecho a explicarme un poco mejor.

No voy a negar que muchas veces "mis colegas me deprimen" porque ello es verdad, lo que sí creo es necesario aclarar a que me refiero. La conclusión a la que llego al final del post anterior es que en realidad el problema está en nuestra profesión, en la ingeniería de software y no en los profesionales, por tanto concluyo que lo que me deprime es el estado del arte en la ingeniería de software y por extensión me "deprimen" mis colegas aún muchos de los más brillantes y admirables.

La ingeniería de software me parece precaria y deprimente por las siguientes razones:

  • El software es una abstracción, sin embargo nos pasamos la vida reescribiendo aplicaciones todo para que corran en diferentes runtimes o con diverso middleware.
  • El software que escribimos rara vez dura más de diez años.
  • El software no puede arreglarse a sí mismo.
  • Para escribir una aplicación web debo conocer y utilizar al menos tres lenguajes diferentes, todo porque los lenguajes actuales están acoplados a un runtime en particular y no son modulares.
  • Puedo diseñar una arquitectura muy buena para mi software, pero al implementarla dependeré de middleware y runtimes los cuales limitarán el tiempo de vida de mi aplicación.
  • Todavía seguimos aprendiendo todos los años (cada vez que un nuevo fabricante renueva una librería o lenguaje) a realizar lo mismo una y otra vez sin aprender nada realmente nuevo.
  • Las librerías de software que usamos son pasivas, no nos advierten si las usamos mal, tampoco nos enseñan a utilizarlas o de conflictos entre ellas.
  • Seguimos escribiendo la gran parte del software de forma artesanal.
  • Una línea de código sigue siendo capaz de colgar el más complejo de los sistemas en la mayoría de los sistemas.
  • Cada vez que necesitamos trabajar con un nuevo paradigma debemos de usar nuevos lenguajes y herramientas (en los próximos inmediatos años veremos como los lenguajes "main-stream" empiezan a "mutar" para incluir características que faciliten la programación concurrente).
  • Estamos obligados a usar una sintaxis en particular para beneficiarnos de lo único importante en un lenguaje que es su "semántica" y lo que es peor, estamos obligados a usar esa misma sintaxis para leer y analizar los programas.
  • El código fuente no puede analizarse a sí mismo en el común de los lenguajes de programación.
  • Quienes investigan e impulsan la "ingeniería de software" (hablando de metodologías, arquitectura, etc.) cada vez más se separan en sus estudios de los lenguajes y herramientas de implementación, lo cual humildemente me parece errado.

Mi objetivo, no es criticar profesionales, universidades o empresas. El medio es criticarlas, el objetivo es despertar a quienes son jóvenes y hacerles ver que nuestra profesión es primitiva y debemos de contribuir mucho para que algún día podamos estar orgullosos de estar en la industria del software. Creer que hoy día el software es avanzado, que los profesionales de software construimos obras de ingeniería memorables, es en términos generales una mentira a nosotros mismos. LayerD es mi propuesta para encarar muchos de estos problemas, no le quepa duda que no los solucionará a todos, pero humildemente creo que se trata de encarar la realidad con opciones un poco diferentes, al final entre todos o alguna mente brillante encontraremos una solución para estos problemas.

Si hay algo que me cautivo desde siempre del software, es que las posibilidades son cercanas al infinito. No creo que sea posible decir hasta donde se llegará, lo que estoy seguro es que en algún momento en el futuro las generaciones que vendrán mirarán hacia atrás y observarán un panorama primitivo, frágil, vacio, oscuro, pero también encontraran gente que con pasión y trabajo empujo un poquito y en suma a lo largo de las generaciones se logro llegar a algo realmente memorable.