Monday, May 28, 2007

La Perfecta Maquina Virtual

La idea de una maquina virtual no es nueva. Antes que C#, antes que Java, incluso antes que el microprocesador, ya habían maquinas virtuales operando en los "mini" computadores de la época.

De hecho, el término "maquina virtual" se ha ampliado haciéndose cada vez mas difuso. Al principio, las maquinas virtuales implicaban un soporte de hardware que permitía que varios sistemas operativos corrieran independientemente en un equipo.

Las maquinas virtuales al estilo Java y .Net son las que han traído el concepto al foco de atención de la mayoría de los programadores en los tiempos recientes. Este tipo de maquinas virtuales son "computadores implementados en software" que corren sobre el sistema operativo anfitrión y que se usan para ejecutar las aplicaciones.

Otro enfoque es el del sistema operativo. La idea es que, dado que la principal función del sistema operativo es hacer una abstracción del hardware sobre el que este corre, el propio sistema operativo constituye una maquina virtual. De hecho, muchas veces los núcleos de los sistemas operativos son llamados maquinas virtuales.

Y es aquí donde viene la atractiva idea de Lina. Si una maquina virtual puede ser tanto una aplicación como el núcleo de un sistema operativo, por que no aprovechar uno de los sistemas operativos libres mas populares y exitosos del mundo? Ese es el concepto tras OpenLina: correr el núcleo de linux sobre windows, OSX y el propio Linux. Las librerías de soporte mapearan llamadas estándar de lina a servicios del sistema anfitrión. Las aplicaciones de lina podrán correr así en cualquier plataforma. Si bien esto es poco mas o menos lo que hace Java, por ejemplo, la novedad esta en que la maquina virtual de lina (el núcleo de linux) es una pieza de software muy madura y excelentemente soportada y con librerías estandarizadas.

Monday, May 7, 2007

Diversión con Monadas

Para el programdor en Haskell, pocos conceptos son tan dificiles de aprehender como las monadas. Existen infinidad de tutoriales sobre el tema y al parecer estos no son suficientes. Su origen en la teoría de tipos y su nombre atemorizante intimidan, al punto que uno de los pilares de Haskell ha comentado que tal vez deban rebaustizarse como las monadas con el titulo de las "cosas difusas y calidas" (warm fuzzy things) con el objeto de hacerlas mas amistosas.

Hay dos elementos que contribuyen a la confusión del recien llegado con las monadas. El primero tiene que ver con su uso especifico para contener los efectos colaterales, es decir la programación no funcional, en particular el sistema de entrada/salida. Esto deja al recien iniciado preguntandose si monadas es sinonimo de I/O.

El segundo elemento que contribuye a la confusión inicial es un menejo del tema desligado de las implicaciones practicas. En Haskell una monada es una clase. Para convertir un tipo dado en una monada hay que implementar las funciones definidas en dicha clase y dichas funciones deben cumplir ciertas de reglas. Un importante conjunto de librerías de Haskell implementan monadas. Todo eso muy bien, pero ¿por que dichas librerías son implementadas así y no otras? ¿Cuando debería un programador desarrollar sus propias monadas?¿Como implemento mi monada?¿Como garantizar que las monadas creadas cumplan con dichas reglas?¿Que pasa si no cumplen con las mismas? Esas son preguntas mas interesantes.

La mejor forma de aprender el significado de las monadas es entender cuando tú como programador deberias implementar una solución basada en monadas. Una vez entendido esto, resulta fácil entender las librerías y saber por que el sistema de I/O de Haskell y el código que produce efectos colaterales es implementado de esa forma. En ese sentido, no hay mejor tutorial que "Tu pudiste haber inventado las monadas! (y tal vez ya lo hiciste)" por sigfpe.

La idea de sigfpe es simple abordar las monadas desde un punto de vista utilitario. ¿Para que sirven?. ¿Que problema resuelven?. Él hace un trabajo insuperable, por lo que aquí me limitare a reexpresar la conclusión a la que llega:

Desde el punto de vista del programador, las monadas son la extensión natural a los functores. Como ya vimos los functores son una forma de mapear las funciones que operan sobre tipos "simples" a funciones que operan sobre tipos "compuestos".
Lo que las monadas permiten es componer funciones cuyo dominio es el tipo "simple" y su rango es el tipo "compuesto" entre si. Sencillo, no?

Eso es todo lo que significa el operador "bind" de la clase Monad:
(>>=) :: m a -> (a -> m b) -> m b
El programador ha desarrollado bastante tiempo creando funciones que toman argumentos simples y devuelven argumentos complejos, como por ejemplo a -> m b, b -> m c y ahora desea combinar esas funciones. Pero como la primera devuelve un valor compuesto (m b) y la segunda requiere un valor simple (b), necesita alguna forma de "sacar" (b) de (m b) para introducirla en la segunda función. Eso responde todas las preguntas planteadas sobre las monadas, como se vera a continuación.


Cuando debería un programdor desarrollar sus propias monadas?

Cuando desarrolla su propio tipo "compuesto" y necesita componer funciones del tipo "simple" hacia el tipo "compuesto".


Por que ciertas librerías en Haskell estan implementadas como monadas?

Porque dichas librerías implican tipos "compuestos" y funciones que devuelven este tipo de valores y que es útil componer. Así por ejemplo, la librería IO no trabaja diretamente sobre enteros, reales y caracteres sino sobre tipos derivados {tipos que contienen de forma oculta el estado del universo y que podriamos sentirnos tentados a representar como (entero, mundo), (real, mundo) y (caracter, mundo)}.
Una revision a las librerías que son implementadas como monadas arroja esa constante: tipos compuestos involucrados (a veces habilmente ocultos) y funciones sobre ellos que es conveniente componer.


¿Como implemento mi monada?

Al crear el tipo compuesto; al necesitar componer las fuciones el programador ya sabe como implementar la monada. Sin un ejemplo en esta pagina puede sonar bastante abstracto, pero el tutorial de sigfpe ofrece suficientes ejemplos y ejercicios. Basta decir que la naturaleza misma del tipo compuesto y su uso deseado sirve para definir la monada.


¿Como garantizar que las monadas creadas cumplan con las reglas de las monadas?

Uno de los temores del programador recien iniciado a las monadas y que las aprendió por su definición es temer que su implementación de una monada sea invalida. Esto ocurre cuando se trata de imponer el concepto no bien internalizado de una monada sobre un problema. En el momento en que el programador se da cuenta que implementar una monada es implementar la composicion entre funciones de una forma que tenga sentido para el tipo "compuesto", el temor a implementar incorrectamente una monada desaparece.


¿Pero que pasa si mi monada no cumplen con la leyes?

Esa situación es perfectamente posible y tiene un término técnico que la describe, se le denomina bug. una monada que no cumple con sus leyes es, desde luego, inútil. Pero ello significa simplemente que tú como progrmador cometiste un error en la función que crea un valor "compuesto" a partir de un valor "simple", o que la función de composición no esta correctamente definida. Es un bug como cualquier otro, no hay nada místico en ello.