lunes 12 de septiembre de 2011

Abracadabra: componiendo criterios de hibernate en grails.

"Como decíamos ayer..."  Fray Luis de León, Miguel de Unamuno et alii
En la ultima aplicación que estoy escribiendo en el trabajo he tenido la suerte de usar grails como plataforma de desarrollo. Pese a todos los problemas que nos encontramos, casi todos originados en ser una tecnología relatívamente joven y que esta en continuo desarrollo, ha supuesto para nosotros un salto en el tiempo de ocho años. Hasta ahora estábamos usando la JDK 1.4, J2EE 1.4, struts 1 y persistencia manual en java y SQL, con lo que el cambio ha sido cualitativo.

En mis primerizos intentos de conseguir que el código sea reutilizable y evitar su duplicación no siempre he encontrado facilidades en la librería, o, lo mas seguro, no he sabido usarla de la forma correcta.

Uno de los puntos donde he encontrado dificultad ha sido a la hora de separar en elementos manejables las closures estáticas que contienen llamadas a métodos que forman un minilenguaje (o DSL) en si mismos. Estos métodos normalmente no existen en el contexto sintáctico donde están esas closures sino que pertenecen a algún otro objeto (normalmente un "builder"), aunque puede también usar las variables y métodos definidos en su ambito sintactico. En algún momento la librería "introduce" a la closure dentro del contexto del builder (por ejemplo con with) con lo que los métodos ya tienen sentido, la closure esta totalmente cerrada y puede ser ejecutada. Un ejemplo, usado en grails, es el uso de las Criteria de hibernate para definir consultas sobre entidades en la base de datos:


No tiene mala pinta pero una vez que te pones a usarlas te encuentras algunas limitaciones, una de ellas es que no puedes componer facilmente las closures para poder separar los diferentes criterios y hacerlos genéricos para no repetirlos una y otra vez. Si usas directamente el api de Criteria puedes componer los criterios creando instancias de los mismo y añadirlos a la instancia misma de la Criteria. Sin embargo en la solución de grails esto no es posible ya que aunque es mas breve y expresiva no expone los métodos para añadir dinámicamente criterios (o al menos no he encontrado la manera de hacerlo). Mi objetivo final era conseguir algo así:


O sea poder componer la closure final con closures mas pequeñas y manejables. También quería conseguir poder parametrizarlas tanto con datos externos como con los valores de la instancia en la que están definidas, consiguiendo un filtro que usa los campos de la instancia sobre la que es llamada como datos de ejemplo para realizar la consulta. En mi caso los métodos genéricos que encapsulan los criterios están en la superclase:



Solamente se añade el filtro de cada propiedad si esta informado y el carácter del filtro es lo mas abierto posible, usando "ilike" en los campos de cadena, "in" en los campos que definen relaciones uno a muchos con otras entidades, etc. También quiero subrayar que las entidades "maestras" se recuperan de forma recursiva, reutilizando el filtro definido en su clase correspondiente.

La pieza que falta para completar el puzzle es el operador ">>>" que se esta utilizando en el código para combinar las closures. Este operador no existe como tal en la versión actual de groovy. De hecho hace relativamente poco que en groovy (en la versión 1.8) se han añadido operaciones y métodos funcionales que actúan sobre las closures, tales como su aplicacion parcial y su composición. Supongo que la reciente recuperación del paradigma funcional (con lenguajes como Clojure, Scala o Haskell) ha favorecido que finalmente estén disponibles. En mi caso todavía estoy usando la versión 1.7 y tenia que añadir la operación de composición. Sin embargo al copiarla de la versión actual la cosa no marchaba. Lo vemos en este ejemplo que simula el comportamiento del builder de grails y la composición oficial de la versión 1.8:


Como se puede comprobar en la consola de groovy se produce un error ya que al componer ambas closures no se propaga el contexto de ejecución de la closure resultante a las closures que la componen, por lo que groovy busca los métodos "eq" e "in" en el contexto mas amplio que es el script mismo donde estamos escribiendo el código. Al no encontrarlo en ningún nivel nos devuelve el consabido error.

La closure se liga al contexto de un objeto usando la propiedad delegate de la misma. Para poder conseguir la ligazón tuve que implementar un método de composición que propagara esa propiedad de la closure resultante a las que están siendo compuestas:


Como se puede observar en el código "simplemente" se crea una nueva closure que llama a las dos que están siendo compuestas, a las que previamente les ha asignado su propio delegate para introducirlas en el contexto de computación. La implementación no esta muy cuidada y tal vez tenga errores, vamos, que no hay garantías.

La verdad es que no acabo de sentirme cómodo con la solución a la que he llegado, aunque en el camino he aprendido algo del funcionamiento interno de las closures, me parece que es demasiado enrevesada para lo que quería conseguir. En principio soy contrario al monkey patching más aun si se hace sobre clases básicas y al uso de la sobrecarga de operaciones. Seguramente se puede hacer de una manera más sencilla, aunque no la he encontrado. Como siempre vuestros comentarios tal vez puedan arrojar un poco mas de luz sobre el asunto.

Hasta mañana.

jueves 9 de diciembre de 2010

Primeros pasos con Clojure

La documentación de Clojure igual no es la mejor del mundo. En un lenguaje joven y mucha información esta repartida entre los blogs de los cada vez mas numerosos entusiastas del lenguaje. Sin embargo se ha dado un paso mas para que los principiantes tengan un referente principal a la hora de instalar y configurar Clojure de una forma sencilla. Todo ello gracias a la labor de Stuart Halloway, autor de un interesante blog y del primer libro sobre Clojure: Programming Clojure.

La pagina principal de la wiki es esta. En ella hay una lista de enlaces a concisos tutoriales sobre las posibles formas de instalar y usar Clojure, según el IDE o editor que mas rabia nos de:
Como veis hay donde elegir. También tanto Stuart Halloway como el creador de Clojure, Rich Hickey han publicado un proyecto base pensado para aprender e iniciarse en Clojure con lo todo lo necesario: labrepl.

ACTUALIZACION

Afortunadamente los recursos para iniciarse con clojure crecen dia a dia. Entre los mas interesantes que han aparecido despues de este post estan los siguientes:
  • Una repl interactiva online done probar clojure sin instalar nada: try-clojure
  • Un pagina excelente para practicar clojure, con problemas de diferentes dificultad y la posibilidad de competir por escribir el codigo mas breve: 4clojure.
  • Una forma que se ha puesto de moda ultimamente es aprender los principios de un lenguaje con los koans, en esta pagina te guian como empezar con ellos con ruby, javascript y como no clojure.
  • Una pagina en las que se intenta centralizar todos los recursos utiles para aprender cojure, desde como instalarlo a listas de tutoriales, libros, etc. Imprescindible si quieres aprender el lenguaje: learn-clojure.com
  • Recursos en la web con documentacion y ejemplos acerca de Clojure: clojuredocs.org y clojure-examples.appspot.com
  • Una guia rapida de las funciones del nucleo de clojure.
  • Tutoriales acerca de como instalar y dar los primeros pasos en clojure: clojure-notes.rubylearning.org y si eres valiente, tienes un mac o linux y ganas de pegarte con las cosas how-to-setup-clojure-from-scratch, si tienes un ubuntu tambien hay un post especifico

domingo 21 de noviembre de 2010

Pruebo, luego programo.

He tenido la suerte de poder participar en el code retreat que se celebro el Sábado en Donosti. ¿Que es un code retreat? Pues es una reunión de programadores que ejercitan sus conocimientos y aprenden y mejoran con ese ejercicio y con su puesta en común entre todos. Una buena forma de aprendizaje a través de la practica y la colaboración. Pero no solo esto, otro de los objetivos es replantearse los propios conocimientos y practicas y para ello la dinámica estaba orientada:
  1. Los ejercicios se llevan a cabo en parejas, para potenciar la programación colaborativa.
  2. El problema a resolver o representar en código es el mismo para todos y cada intento de hacerlo duraba estrictamente 45 minutos (osease un pomodoro). En nuestro caso el problema a resolver fue el juego de la vida de Conway. Aunque el objetivo real no era resolver el problema por supuesto.
  3. Después de cada sesión había que borrar todo el código escrito antes de compartir entre todos la experiencia.
  4. Como principio general se estableció el desarrollo guiado por las pruebas.
  5. Aparte se pusieron restricciones sorpresa para evitar entrar de ninguna manera en una rutina.
Todo ello para conseguir una nueva visión de la programación y para romper los esquemas, el bagaje que muchas veces te hace ser mas rígido y te impide aprender y avanzar. Y para conseguirlo de forma radical es necesario medidas radicales.
Por supuesto contar con un maestro de ceremonias adecuado es casi imprescindible y Enrique lo hizo a la perfección compartiendo sus valiosos conocimientos de forma totalmente desinteresada. El acto no pudo haber existido sin el tiempo y el esfuerzo de los organizadores y de los patrocinadores. Desde aquí mi reconocimiento a su generosidad. Finalmente pero no menos importante también hay que reconocer el papel de todos los asistentes al acto ya que entre todos ayudamos un poco a que fuera un éxito.
Nunca había practicado ninguna de las técnicas que he descrito y solo tenia cierto conocimiento teórico de lo que había podido leer en blogs y artículos. Un conocimiento totalmente inútil y que intente olvidar para limpiar la mente de prejuicios.
El objetivo claro fue hacernos mejores programadores o sea que nuestro código fuera mas elegante, mas simple, mas fiable y mantenible. La prueba desde ese punto de vista pierde su valor como elemento externo al programa, muchas veces escrito a posteriori y se convierte en el medio para alejarnos del código real y mirarlo desde fuera incluso cuando aun esta en nuestra mente luchando por salir y apoderarse de nuestros dedos. La prueba nos aleja de el para mirarlo con ojos nuevos y sospechosos.

Sí, nuestro código siempre es sospechoso.

A la vez la prueba nos hace seguir pegados al objetivo y al propósito del código todavía no escrito y no nos separa totalmente de lo que es la programación ya que es en si misma código. Esa es su ventaja y su inconveniente ya que hay otras maneras de alejarnos del código: haciendo dibujos o simplemente no haciendo otra cosa que pensar en el. La desventaja de la prueba es que sigue siendo código en el que podemos regodearnos, dándole mas importancia de la debida y que puede acabar siendo tan imperfecto o mas que el código real que todavía no hemos escrito (y que puede acabar siendo empobrecido por la prueba y no al revés).

Sí, se corre el riesgo de escribir dos veces un código mal hecho. Nadie dijo que la tdd fuera mágica.

Aprendizajes y collejas varias

Para evitarlo Enrique nos daba collejas virtuales, tanto en mitad de los ejercicios como en las retrospectivas posteriores para sacarnos de nuestros raíles, viejos o los que íbamos creando en el momento. Borrar el código antes de hablar de el, tener que consensuar los nombres y el siguiente paso a seguir, cortar el ejercicio justo cuando la cosa empezaba a calentarse. Todo orientado a alejarnos del código y volverlo a mirar con otros ojos.
La prueba bien hecha no se deja arrastrar por el código todavía no escrito que pulula en nuestra cabeza, lo domina y lo encierra y le obliga a seguirla:
  • Le debe obligar a ser mas simple: para ello Enrique nos obligo a escribir todo el código necesario dentro de la prueba y solo darle entidad propia en cuanto detectaramos repeticiones reales en las pruebas. En otro momento nos obligo a que las funciones o métodos no fueran de mas de cuatro lineas.
  • Debe usar los nombres mas "adecuados" (totalmente autodescriptivos,no ambiguos, lo mas cortos posible) a la lógica de negocio y al problema que va a resolver. En cada retrospectiva Enrique incidía y cuestionaba los bautizos que hacíamos.
  • Le debe dar justo la responsabilidad que necesita para hacer bien su trabajo sin dependencias innecesarias y lo mas alejadas de la implementaciones concretas. El mismo hecho de hacer la prueba te conduce a ello ya que probar un fragmento de código es mas difícil y costoso cuanto mas dependencias externas tienes que manejar en la prueba (uso de mocks e inicializaciones costosas). Enrique nos prevenía de la implementación concreta que luchaba por salir de nuestros dedos y que amenazaba con encerrar y anclar nuestro diseño antes de tiempo.
  • La prueba debe contagiar al código de su carácter declarativo, para lo que obviamente debe ser ella misma lo mas declarativa posible. Enrique nos prohibió usar números en las pruebas para intentar hacernos ver que la mejor forma de que la prueba nos dijera lo que quería es darle un nombre que lo expresara de forma totalmente descriptiva. No lo consiguió y tuvo que borrar nuestro código (otra colleja) y señalarnos con el dedo (y aun asi nos costo asumirlo). Cuando vimos: public class TestCelulaCuandoEstaViva { public void muereCuandoTieneMenosDeDosVecinos () {assert(true)}} el código lleno de ifs, operadores de comparación y números que habíamos guardado celosamente en nuestras cabezas y que guiaba secretamente nuestras pruebas murió un poco mas. Tell, dont ask, my friend.
  • Debemos poder hacer las pruebas tanto desde arriba hacia abajo como al revés y elegir el camino adecuado en cada momento (o combinarlos, primero hacia abajo y luego de vuelta como nos mostró Enrique). La elección debe ser cuidadosa y debe tener en cuenta el dominio, las herramientas, el lenguaje con el contamos, etc.
Un tesoro nos llevamos de Donosti, seguro que hubo mas enseñanzas que se me han olvidado o que ni siquiera capte pero estoy contento. Pero, espera un momento, yo ya conocía todos esos preciosos principios y buenas practicas en teoría e incluso intento con mayor o menor éxito aplicarlos en mi código. Geniales programadores los llevan a la practica todos los días sin hacer un test. Se alejaran del código y como paso previo pensaran en el teniendo en cuenta todas estos principios y algunos mas. Incluso estoy seguro que el primer código que piense gente como Enrique ya sera cristalino (al menos comparado con el mio).
Como otra opción para los programadores de a pie queda el hacer prototipos del código y repensarlo y reescribirlo las veces que haga falta. Equivocarse una y otra vez y aprender de los errores.

Entonces, ¿para que las pruebas?

Buena pregunta ya que, en mi opinión, no hay que aceptar las metodologías sin mas ya que también son sospechosas (igual que lo es el código). Igual me perdí algo en el code retreat que eliminara todo mi escepticismo. Dejare a mis comentaristas la oportunidad de contestar aunque puedo adelantar que en primer lugar aparece el valor original de la prueba que hasta ahora habíamos obviado: monitorizar el código de forma automática y aumentar nuestra confianza en el, describir y documentar los casos de uso y la lógica de negocio. El hacer la prueba primero tiene la consecuencia obvia de que seguro que la haces. Por otro lado te obliga de forma metódica a pensar en el código antes de escribirlo (cosa que no suele hacerse siempre a no ser que tengas una gran disciplina) Seguro que se os ocurren mas ventajas.

Clojure y el TDD

Por supuesto fui con mis paréntesis a la sesión con un poco de miedo que provocaran urticaria y dispuesto a amoldarme a cualquier lenguaje sobre todo desconocido para mi como oportunidad para aprender. Debí haber previsto que todos venían dispuestos a aprender sin prejuicios y no les importo o incluso estuvieron encantados (¡cuando @jmbeas empezó a recordar sus tiempos de emacs y common lisp tuve que seguirle yo a el!) de usar clojure para los ejercicios, @kinisoftware también acepto sin problema hacerlo en clojure a la vez que me guiaba por mis primeros balbuceos en los tests. Eso tal vez sesgo un poco el objetivo principal pero creo que pude mostrar unas pinceladas del lenguaje sin olvidarnos del todo de el.
Hay gente que cuestiona la pertinencia de la TDD en un lenguaje funcional como Clojure, pero dentro de la comunidad también hay opiniones contrarias y al menos el testing tiene su lugar ya en el mismo núcleo del lenguaje. Aparte hay varias librerías magnificas para ponerlo en practica. Personalmente creo que aunque no tiene el mismo significado o importancia en un lenguaje orientado a objetos y dinámico (en el que casi es una locura programar sin hacer tdd) que en uno funcional y que promociona la inmutabilidad y evita el estado, si que cumple su papel. No hay mejor referencia que los posts que ha escrito @unclebob sobre clojure y tdd o los de @marick.
Fue curioso como la orientación de objetos fue, en mi opinión, mas un estorbo que una ayuda para las sesiones. Los objetos (todos con mayúsculas Célula, Tablero, Juego,Dios) que modelaban el problema, sus relaciones y sus responsabilidades rondaban la cabeza de los asistentes y creo que tiraban de los tests anclandolos y no dejandolos fluir. En mi caso era las estructura de datos simples la que me rondaban y las funciones para transformarlas (vectores, mapas) pero creo que esa diferencia iba a mi favor pues con elementos tan simples y genéricos (que hay mas abstracto,generico, componible y simple que una funcion o un mapa) no necesitas crear abstracciones a posteriori ni mocks para aislar los tests del diseño e implementación concreto. ¡¡¡Corregirme si me equivoco!!!

martes 28 de septiembre de 2010

Scala vs y Clojure (Round 1)

Un lenguaje escalable: tanta complejidad como necesites.


Una ventaja de que por la península vayamos retrasados en esto de lo tecnológico es que sea mas difícil que un post como este pueda iniciar un guerra. Pero por si acaso quiero aclarar que aunque mi lenguaje preferido en la intimidad sea Clojure, también aprecio Scala, al menos en lo que ha acercado a la JVM elementos de lenguajes como Haskell y la familia de los ML.
Ambos lenguajes comparten ciertos aspectos pero difieren lo suficiente como para no ser excluyentes dentro de los nuevos lenguajes funcionales que corren sobre la JVM. El mismo creador de Scala, Martin Odersky defendía esta postura en esta entrevista: para que "gane" uno de ellos no tiene porque perder el otro.
Y la diferencia fundamental esta en las razones que ambos surgieran y en su misma filosofía, antes incluso de las diferencias técnica. Esa diferencia se traduce también en sus respectivas comunidades, en lo que te exige como programador y aprendiz y en lo te puede dar.
En general estoy convencido que un lenguaje no es absolutamente mejor a cualquier otro sino mejor o mas adecuado a un contexto determinado, incluido java o cobol.

Scala desde sus orígenes ha querido ser "un mejor java", algo así como un java++. Y aunque ultimamente no es la imagen mas popular, efectivamente lo es. Eso supone que desde su creación se ha caracterizado por tener una sintaxis similar a la de java y unas construcciones parecidas pero mejoradas en cuanto a ser orientado a objetos de una forma mas consistente y expresiva que java. Incluso ha integrado en el lenguaje varios de los patrones de diseño que ya se han establecido casi como norma en java, de una forma sencilla y expresiva.

Como ejemplo podemos echarle un vistazo a este código que declara una típica clase de una aplicación empresarial:



No tiene nada de mala pinta para un programador en java que se acerca al lenguaje.

Por otro lado desde el origen Scala ha tomado como ejemplos lenguajes populares en el ámbito universitario de la "ciencia computacional": Ocaml y Haskell. Tal vez tenga algo que ver que tanto Odersky como Scala están fuertemente asociados a la Escuela Politécnica de Lausanne. Estos lenguajes (incluido el mismo Scala) están en la vanguardia de la investigación en los lenguajes de programación lo que ahora mismo es equivalente a decir que profundizan en el paradigma funcional unido a sistemas de tipado basados en su inferencia. Abstracciones como las Monadas o los Funtores son poderosas y atractivas intelectualmente, incluso tienen su aplicación real aunque tal vez no sea evidente a primera vista. Sin embargo su uso en Scala requiere cierta formación teórica y un conocimiento mas profundo de su sintaxis. Un código es Scala que hace un uso intensivo de estos elementos puede parecer tal que así:



Viendo este código el mismo programador que antes asentía pondrá los ojos en blanco.

Scala es un híbrido y su filosofía es darle todas las opciones posibles al programador aunque suponga hacer el lenguaje mas grande y complejo. Puedes programar orientado a objetos de forma imperativa o funcional, usando datos inmutables o no, usar la evaluación perezosa o no. Es por esto que lo han comparado a c++ ya que parece que también este lenguaje nació un poco con esa filosofía. Odersky tuvo que defender a su lenguaje de esa fama de complejo pero sin negar que lo fuera, sino defendiendo esa complejidad interna y la "escabilidad" del lenguaje, ya que cada uno puede usar Scala a su manera en cada momento y solo usar la parte mas compleja cuando se necesite construcciones mas poderosas. También es verdad que el mismo hecho de aglutinar paradigmas y opciones tan diferentes y el ser "escalable" y flexible añade su propia dosis de complejidad en el computo global sea el uso que sea el que le quieras dar.

El problema de muchos programadores provenientes de java con Scala me parece que es que llegan enganchados al cebo de su sintaxis y semántica básica parecida y su ligera curva de aprendizaje inicial pero cuando quieren dar un paso mas allá y usar las partes mas avanzadas el nivel de exigencia y de dificultad aumenta de forma exponencial, al menos en una segunda fase. Después de todo si te has cambiado de lenguaje es porque se quería usar algo diferente. Hay que ser pacientes y usar poco a poco esas características si no se tiene experiencia anterior o un solida base teórica en lenguajes funcionales tipados. Esa frustación aparece como una de las causas que Scala no se haya hecho mas popular, y se haya agravada por el hecho de que los miembros mas influyentes de su comunidad escriban sobre el lenguaje desde el punto de vista de su parte funcional y sobre el uso mas sofisticado de su sistema de tipado y que establecen casi de facto que ese uso del lenguaje es el estándar.

Según mi forma de ver Scala puede ser un lenguaje apropiado para entornos de programadores java con ganas de aprender pero que quieren empezar con una sintaxis y unas construcciones cercanas y añadir progresivamente elementos mas poderosos cogidos del paradigma funcional. También es una buena opción si se quiere un lenguaje que te permite usar un sistema de tipado estático sofisticado y que gracias a su inferencia te ahorra el tener que escribir muchas de las anotaciones de tipos. La gente que no se encuentren cómodos con los lenguajes dinámicos y echen de menos la velocidad de ejecución que te proporcionan los tipos (Scala es al menos tan rápido como java puro) o la posibilidad de validar/documentar tu programa de forma logica con los tipos, valoraran mas el sistema de tipado de Scala. Mas todavía si tenemos en cuenta que este sistema es mas flexible, rico y breve en código que el de java.

Clojure parte desde un punto de vista totalmente diferente que veremos en la entrada siguiente del blog, pero podemos adelantar que aunque la sintaxis sea totalmente ajena a los lenguajes derivados de Algol (c,c++,java,Scala,etc,etc) y suponga para muchos un handicap inicial casi insalvable en realidad es mucho mas sencillo que Scala, tanto en la sintaxis como en la semántica al centrarse y casi obligar a adoptar desde un principio a las característica funcionales casi puras: inmutabilidad de los datos, separación entre estos y los algoritmos que los tratan, evaluación perezosa, separación entre el valor (inmutable) de un dato y las referencias que nos permiten modificarlo en el tiempo de forma segura en lo que respecta a la concurrencia, etc. La curva de aprendizaje es inversa a la de Scala, muy empinada al principio pero mas leve en cuanto se ha comprendido los principios fundamentales del paradigma funcional y se ha acostumbrado uno a los benditos paréntesis. Pero no hay marcha atras una vez que entras en los paréntesis, mmm, ¿o tal vez sí?

((TO BE CONTINUED))

lunes 14 de junio de 2010

Clojure por las nubes (Google AppEngine)

Uno de los campos donde se mas se mueven los programadores en Clojure es en el de la programación web en la "nube". En concreto la alternativa que ofrece google es bastante popular desde que soporta aplicaciones en java y cualquier lenguaje que pueda ser compilado para la jvm.

La ventaja esta clara, no hay que preocuparse acerca de los recursos de computación necesarios para que tu aplicación funcione, ni anchos de banda, ni cpu o espacio en disco. Puedes desplegar una aplicación de forma gratuita y pagar justo por el espacio de disco, memoria ram y ciclos de cpu que uses a medida que tu aplicación crece. Vamos como si fuera la factura de la luz.

También tiene su lado malo, la aplicación tiene que ajustarse a las limitaciones sobre las librerias estandar de java y usar las librerias propias de la AppEngine para operaciones básicas asi como la base de datos que ellos te proporcionan. Ademas el kit de desarrollo necesario para desarrollar tu aplicacion no tiene el codigo abierto. Hay que modificar o programar la aplicación para hacerla dependiente de la plataforma.

En todo caso es una opción bastante atractiva para aplicaciones de prueba e investigación.


Entre las referencias que pululan por la red la mas atractiva es las de una empresa autogestionada por un grupo de "hackers" amantes del lisp, que han usado Clojure para programar y Google AppEngine para publicar una interesante aplicacion de gestion de "tareas pendientes" orientado a programadores, con ciertos elementos de inteligencia artificial para ayudar a priorizar las tareas y estimar los tiempos que tardaremos en completarlas. La aplicación esta en http://the-deadline.appspot.com

En este video la banda nos explica las razones de usar Clojure y GAE:



En su blog ha varios posts que no hay que perderse sobre aspectos técnicos del desarrollo con Clojure y GAE. El que escribe también ha subido el "hello world" de la GAE, un libro de visitas básico (aqui el codigo) que combina un servlet en java cogido de la aplicación de demostración que viene con el kit de desarrollo y un "servlet" en clojure usando Compojure como framework web y otras librerias de ayuda para el desarrollo en la GAE.

Asi que si quieres programar en Clojure y ver tu aplicacion por las nubes de forma relativamente sencilla esta puede ser tu opción.

martes 23 de marzo de 2010

Introducción a Clojure II: inmutabilidad.

"Ningun hombre puede cruzar el mismo río dos veces."
- Heraclito
En la primera entrada introductoria hablabamos de uno de los obstaculos o reparos que alguien acostumbrado a la programación imperativa de lenguajes herederos del c tiene al acercarse a Clojure: la sintaxis prefija y llena de parentesis. Sin embargo hay otra dificultad a la hora de acercarse a Clojure: la inmutabilidad de los datos y todo lo que ello supone, que no es sino otra forma radicalmente diferente de entender la programación. En Clojure, por defecto, no se puede cambiar el valor asociado a un "simbolo" y se delimita muy claramente las formas en que se puede mutar el valor asociado a una referencia.
Por ejemplo algo tan comun en java como:

int i=0;
while (i<10) i="">
es imposible hacerlo en un lenguaje funcional tal cual ya que no existen las "variables" como se entienden en un lenguaje imperativo (ni tampoco los bucles en el sentido imperativo pero es otra historia). Las variables en Clojure son mas bien como símbolos ligados a valores constantes o a funciones.
En clojure podemos definir un símbolo e inicializarlo con un valor que ya no puede cambiar, en lo que sería mas cercano a una constante en java.
;clojure
(def i 10)
// java
final int i=10;
No parece muy util un lenguaje en que no se puedan transformar los datos y en un lenguaje puramente funcional la unica manera de realizar esa transformación es precisamente mediante funciones. Pero las funciones no modifican los valores pasados por parámetros ni las variables globales, sino que siempre producen valores nuevos. En Clojure no se transforma, solo se crea y se destruye.
Esto suena a herejía, a derroche de memoria y lentitud. Y realmente si que lo es pero actualmente la memoria es barata y los procesadores rapidos y en los lenguajes funcionales modernas ese "crear" nuevos valores esta optimizado hasta conseguir un rendimiento bastante razonable.
Otra comparación de código (aunque la comparación no es exacta ni mucho menos, es simplemente para hacerse la idea).
(defn add [x y] (+ x y))

Integer add (final Integer x,final Integer y) {
   return new Integer (x.intValue()+y.intValue());
}

;o con una lista que tal vez se vea mejor
(def add3 [col] (cons 3 col))

Collection add3 (final Collection col) {
   ArrayList aux=new ArrayList (col);
   aux.add (new Integer(3));
   return aux;
}
Bueno esta muy bien pero por ahora solo parece una limitación respecto a java. En realidad sí que se pueden mutar valores en Clojure pero el punto de vista es el contrario: en Clojure por defecto todo es inmutable y asi nos ahorramos los "final", en java es al reves.
¿Que se consigue fomentando la inmutabilidad? Pues algo muy valioso: si no mutan el estado de nada exterior a ellas las funciones no tienen efectos laterales (o dicho de otra forma son transparentes referencialmente). El sabio consejo para cualquier lenguaje de limitar el uso de las variables globales aqui se lleva un paso mas adelante: por defecto no se pueden modificar las variables y por tanto no se producen cambios inesperados. Una función siempre devolvera el mismo valor si es llamada con los mismos parámetros independientemente de cuando, en que orden y como se llame y del estado del resto del universo. Si una función falla, falla siempre, y eso que parece una tonteria es el sueño de cualquiera que haya tenido que hacer debug o tests unitarios de una aplicación java o c++. Tampoco parece muy util el no poder producir efectos laterales ya que hay varias acciones que lo necesitan: la entrada y salida de datos, la generacion de numeros aleatorios, etc. En Clojure las funciones pueden producir esos efectos laterales pero de una forma mas delimitada y controlada (aunque sin llegar a la paranoia de Haskell).
Como conclusion podemos decir que se puede programar en Clojure de forma imperativa si se quiere (al fin y al cabo es un lenguaje multiparadigma) pero el lenguaje fomenta y facilita lo contrario.
Otra ventaja es en el campo de la concurrencia. El complejo análisis (y los horribles bugs que pueden aparecer) del comportamiento de una aplicación con multitud de procesos o hilos de ejecución simultaneos simplemente no es necesario. Los hilos no pueden modificar variables que afecten a los otros porque no pueden modificar las variables.
Hay otras ventajas de este modelo mas simple y matematico de enfocar la programación algunas de las cuales esta en este interesante post sobre la programación funcional.

Sin embargo no todos los objetivos de una aplicación en el mundo real se pueden conseguir (o al menos facilmente) solo con funciones. Los programadores trabajamos con modelos del mundo real que sufren (o disfrutan) modificaciones a lo largo de una línea temporal. Con Clojure tambien podemos hacerlo pero de forma controlada y automatizada en lo que respecta a la concurrencia. No hay que definir los "locks" manualmente ni analizar donde hay que sincronizar la ejecución de los hilos o procesos, al igual que en java ya no tenemos que preocuparnos de gestionar la memoria manualmente como en c. Pero todo esto da para todo un futuro post.

miércoles 24 de febrero de 2010

Ejemplo básico Clojure y IV: multiplicar números como listas.

Como las masas que siguen este blog son muy tímidas vamos a ponernos manos a la obra para conseguir que la ultima versión de nuestra función para multiplicar listas funcione correctamente. El error es muy básico pero vamos a seguir los pasos para detectarlo y arreglarlo. En Clojure no se sigue el proceso cíclico de codificar-compilar-debuguear-probar tan familiar para los que programamos en lenguajes compilados mas torpes como java. En Clojure (como en todos los lenguajes dinámicos y los estáticos mas modernos) tenemos siempre disponible el REPL (Read-Evaluation-Print-Loop) que es un entorno interactivo de programación, una consola donde podemos escribir código, que es evaluado por el interprete presentándose el resultado en la consola. De hecho ya la he expuesto en todos los fragmentos tal que así:

> (codigo a evaluar)
resultado


El REPL permite una programación dinámica e interactiva ya que tenemos una respuesta inmediata a nuestras pruebas e investigaciones. Vamos a usarla para diagnosticar el problema. Recordemos como era la función:

(defn multLists [xs ys]
(let [mults (reduce #(cons (multList %2 xs) %1) '() ys)
despl (apply multDespl mults)]
(listCarry (apply map + despl))))

Primero vamos a cambiar la función para tener los resultado intermedios y los almacenamos en una variable auxiliar:

> (defn multLists [xs ys]
(let [mults (reduce #(cons (multList %2 xs) %1) '() ys)
despl (apply multDespl mults)]
[mults despl (listCarry (apply map + despl))]))
#'mult-listas/multLists

> (def aux (multLists '(5 6 7 8) '(1 2 3))
#'mult-listas/aux

> (aux 0)
((1 7 0 3 4) (1 1 3 5 6) (5 6 7 8))

> (aux 1)
((1 7 0 3 4) (1 1 3 5 6 0) (5 6 7 8 0 0))


Hasta aquí la cosa esta bien, las multiplicaciones parciales son correctas y los desplazamientos a la izquierda también. Vamos a probar el siguiente paso que seria:

> (apply map + (aux 1))
(7 14 10 16 10)


Ooops, aquí esta el error, en lugar de sumar seis elementos ha sumado cinco, si nos fijamos ha sumado los n-esimos elementos de las listas y como la primera solo tiene cinco posiciones ha desechado los últimos elementos de las otras dos. Si comprobamos la documentación de la función map (el énfasis es mio):

> (doc map)
-------------------------
clojure.core/map
([f coll] [f c1 c2] [f c1 c2 c3] [f c1 c2 c3 & colls])
Returns a lazy sequence consisting of the result of applying f to the
set of first items of each coll, followed by applying f to the set
of second items in each coll,
until any one of the colls is
exhausted
. Any remaining items in other colls are ignored. Function
f should accept number-of-colls arguments.
nil


Para solucionarlo lo primero que se me ha ocurrido es igualar todas las listas añadiendo ceros por la izquierda. Para ello tendremos que calcular la longitud de la lista mas grande y añadir ceros a las demás hasta igualarla. Tal vez haya otra forma mas sencilla de hacerlo, no dudéis en comentar vuestras propuestas. Para igualar las listas necesitaremos otras dos funciones pero candidatas totales a ser reutilizadas mas adelante. Una para añadir n elementos a una lista por la izquierda:

(defn lpad [lista num elem] (concat (replicate num elem) lista))

La función es casi auto-descriptiva. Para saber la longitud de la lista mas grande nos haremos otra función (aunque ya esta hecha en la librería auxiliar oficial de Clojure:

(defn maxLength [& lst] (apply max (map count lst)))

En esta función vemos el símbolo & que hace que la función acepte cualquier numero de parámetros que están disponibles metidos en una lista identificada con el símbolo después de & (lst en este caso). Este símbolo puede estar al final de la lista de parámetros normales. La lista de listas se convierte en una lista de las longitudes de las misma con map count y luego usamos max para extraer el máximo entre ellos.

Bueno ya podemos completar la función para que la operación sea correcta:

(defn multLists [xs ys]
(let [mults (reduce #(cons (multList %2 xs) %1) '() ys)
despl (apply multDespl mults)
max (apply maxLength despl)
lpaded (map #(lpad %1 (- max (count %1)) 0) despl)]
(listCarry (apply map + lpaded))))

> (multLists '(5 6 7 8) '(1 2 3))
(6 9 8 3 9 4)
> (numList (multLists '(5 6 7 8) '(1 2 3))
698394


Ya hemos llegado al final del camino, espero que este sencillo ejemplo os haya picado la curiosidad por Clojure y sus posibilidades.

Teneis el codigo del ejemplo en github para que hagais con el lo que querais.