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.

jueves, 18 de febrero de 2010

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

Ya tenemos nuestras dos funciones de conversión entre numero y listas y podemos pensar en realizar operaciones. En este caso tenemos que trabajar con dos listas y hacer la multiplicación tal como la haríamos con papel y lápiz. Es decir multiplicar todos los elementos de la lista por cada uno de los elementos de la otra, sumando el acarreo y sumar todos los productos desplazando una posición a la izquierda cada vez.

De la descripción de lo que queremos podemos deducir las funciones que nos harán falta. En primer lugar necesitaremos unas funciones que nos sumen listas de dígitos. Lo que es la simple suma de los elementos es bastante directa:

> (map + '(4 5 6) '(7 8 9))
'(11 13 15)

Map es otra función básica en cualquier lenguaje funcional y aplica una función a cada uno de los elementos de una lista devolviendo la lista resultante. En Clojure si hay varias listas (como en este caso) aplica la función a todos los n-esimos elementos de cada una de las listas. El ejemplo anterior se convertiría en algo así:

((+ 4 7) (+ 5 8) (+6 9))

La cuestión es que no nos vale ya que no queremos (11 13 15) que se nos convertirá en 111315. Hace falta, al igual que cuando sumamos a la antigua usanza dejar el dígito de la unidad y sumar el dígito de la decena al siguiente termino de la suma: (1 2 4 5). Como hemos empezado a describir la resolución "de abajo a arriba", o sea de las funciones mas simples a las complejas que las usan nos ocuparemos de la suma con acarreo. Para construir la nueva lista usaremos de nuevo reduce, para recorrer todos los elementos de la lista e ir construyendo la nueva lista pasando el acarreo y la lista de un elemento a otro.

(defn listCarry [xs]
 (let [[x,xs] (reduce carry [0 '()] (reverse xs))]
  (if (zero? x) xs (cons x xs))))

Usamos de elemento intermedio un vector de dos posiciones [acarreo '(lista a devolver)]. La función que coge ese dato y nos devuelve el siguiente es:

(defn carry [acc x]
 (let [s (+ x (acc 0)) [d r] (div s 10)]
 [d (cons r (acc 1))]))

> (carry [0 ()] 15)
[1 (5)]
> (carry [1 '(5)] 13)
[1 (4 5)]
> (carry [1 '(4 5)] 11)
[1 (2 4 5]

La función listCarry si el ultimo acarreo es cero nos devuelve la lista construida y si no se lo añade antes de devolverla.

> (listCarry '(11 13 15))
(1 2 4 5)

Ya podemos atacar la siguiente función que seria multiplicar una lista de dígitos por un solo elemento base de la que queremos. Simplemente aplicaremos una función anónima que multiplica por el elemento en cuestión a todos los elementos de la lista con map, y le pasaremos a la lista resultante la función listCarry:

(defn multList [m lst]
(listCarry (map #(* %1 m) lst)))

> (multList 2 '(5 6 7 8))
(1 1 3 5 6)

Bueno la primera implementación de la multiplicación entre listas esta muy cerca, parecería que solo hay que recorrer los elementos de un lista, multiplicar la otra por cada uno de ellos y sumar las listas resultantes. Sin embargo ya podemos prever que como mínimo tendremos que desplazar cada una de las multiplicaciones parciales una "posición" hacia la izquierda mas que la anterior. Al ser sumas ese desplazamiento podemos traducirlo añadiendo ceros por la derecha. Podríamos o bien añadir los ceros a la vez que vamos haciendo las multiplicaciones o añadir los que toquen en la lista resultante. Una función general independiente que añada ceros por la derecha podría ser utilizada mas adelante así que vamos a elegir esta ultima opción:

(defn multDespl [& lst]
 (map concat lst (iterate #(cons 0 %1) '())))

Esta funcion se aprovecha de otra poderosa característica de Clojure (compartida con Haskell): la evaluación "perezosa". Muchas funciones en Clojure nos devuelven una secuencia que no se evalúa en el mismo momento de ser llamada la función. La evaluación de un elemento de la secuencia se hace cuando es necesario y no antes. Eso permite que podamos escribir funciones que representen secuencias infinitas en potencia, con la seguridad de que solo se computara hasta el elemento que necesitemos. La ultima función usa la evaluación perezosa en

(iterate #(cons 0 %1) '())
La función iterate aplica una función a un valor inicial (una lista vacia en nuestro caso) y vuelve a aplicarla sobre el resultado anterior una y otra vez hasta el infinito. Obviamente un ordenador no puede manejar listas infinitas, así que tendremos que decirle hasta donde queremos usarla, por ejemplo con la función take que "toma" los elementos que queramos de una secuencia.

> (take 4 (iterate #(cons 0 %1) ())
((0) (0 0) (0 0 0) (0 0 0 0) (0 0 0 0 0))

Bien ya podemos intuir lo que hace la función de desplazar a la izquierda: une cada una de las listas que representan las multiplicaciones parciales a cada una de las listas con ceros de nuestro iterate:

> (multDespl '(1 2 3) '(1 2) '(1))
((1 2 3 0) (1 2 0 0) (1 0 0 0))

Bien, ya parece que tenemos nuestros ladrillos para construir la función principal:

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

La función define en un let dos resultados intermedios, en mults las multiplicaciones parciales resultantes de multiplicar la lista xs por cada uno de los elementos de ys. En despl rehacemos la lista con los desplazamientos hacia la izquierda. Finalmente en el cuerpo de la función sumamos todas las listas intermedias y le pasamos la función listCarry. Sin embargo si probamos la función:

> (multLists '(5 6 7 8) '(1 2 3))
(8 5 1 7 0)

¡Argh! Nuestro gozo en un pozo, la solución ni se acerca a la solución real (si la calculadora de Google no falla tendría que ser (6 9 8 3 9 4). Os doy unos días para averiguar por qué ...

miércoles, 17 de febrero de 2010

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

En el episodio anterior conseguimos la función que nos convierte un número en la lista de sus dígitos:

> (numList 12345)
(1 2 3 4 5)

Nos faltaría el proceso inverso para tener la cosa completa. En este caso partimos de una lista de elementos y tenemos que "reducirla" a uno solo. La implementación cruda se puede hacer recursivamente, extrayendo cada uno de los dígitos y haciendo la operación inversa a la función anterior, es decir, empezando por el último, multiplicar cada elemento por diez y sumarlo al siguiente:

(defn listNum [l]+
(let [x (last l) xs (butlast l)]
(if (nil? x) 0 (+ x (* 10 (listNum xs))))))

Como en cualquier aproximación recursiva primero establecemos el caso trivial que es cuando ya no haya mas elementos en la lista (o sea cuando sea "nil" o nulo el elemento extraido de la lista). Entonces devolveremos el valor básico 0. Luego construimos la llamada de "dentro a afuera", como si fuera una muñeca rusa. La recursión puede ser un poco confusa al principio hasta que la mente abandona los for, while y compañía. En este caso no podemos usar recur ya que la llamada recursiva no es la ultima expresión evaluada en la funcion.
La ejecución de (listNum '(1 2 3)) se podria rastrear poniendo todas las llamadas recursivas a la vez así:

(+ 3 (* 10 (+ 2 (* 10 (+ 1 (* 10 0))))))
(+ 3 (* 10 (+ 2 (* 10 (1)))))
(+ 3 (* 10 (+ 2 (10))))
(+ 3 (* 10 (12)))
(+ 3 120)
123

La operacion de recorrer una lista e ir creando un valor acumulativo aplicando una funcion a cada elemento de la lista es algo tan habitual que hay una función especializada en ello: reduce, que precisamente "reduce" una lista a un unico valor aplicando una función a cada uno de los elementos de la lista y al valor acumulado anterior. Esta función entra dentro de las llamadas de "orden superior" ya que o bien toman otras funciones como parametro, bien devuelven otra función o ambas cosas. La firma de reduce sería:

(reduce funcionAcumuladora valorInicial listaAProcesar)

Usandola nuestra función quedaría así:

(defn listNum [lst] (reduce #(+ %2 (* 10 %1)) 0 lst))

Aparece aqui algo muy interesante: una función anónima representada por #(...). Dentro de la misma podemos hacer referencia a los parametros en orden con %1,%2,etc.
La traducción de la función anónima a una con nombre sería:

(defn acumulador [p1 p2] (+ p2 (* 10 p1)))
(defn listNum [lst] (reduce acumulador 0 lst))

Bueno con nuestras funciones de conversión ya podemos empezar a pensar en como multiplicaremos dos números como listas de dígitos, pero sera en la siguiente entrada.

lunes, 15 de febrero de 2010

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

(Si queréis poner en practica el ejemplo de esta serie de posts aquí tenéis unas instrucciones precisas de como instalar y los primeros pasos con Clojure .)

Hace poco me examine de la asignatura de Programación Declarativa en la UNED que consiste en gran parte en programar en Haskell. Tal vez algun día me ocupe de este lenguaje, funcional como Clojure pero con una sintaxis mas barroca y todo un universo propio en el que se intenta mantener la pureza funcional y la referencia transparencial a base encerrar lo impuro en unas carceles llamadas monadas. Un lenguaje fascinante pero tal vez un paso mas alejado del mundo de los negocios que otros lenguajes funcionales modernos como clojure, scala o erlang.
Uno de los ejercicios que se proponían para practicar era el siguiente, extraido del manual que seguíamos en la asignatura: dado dos números representados como una lista de sus dígitos, declarar una función que devuelva en el mismo formato el resultado de multiplicarlos, así como las funciones para convertir un numero en una lista en ese formato y otra que haga la operación inversa. La ventaja de representar así los números es que puedes sobrepasar el límite establecido para los tipos numéricos enteros habituales.

Ejemplo de lo que queremos:

->(def x (numList 12345))
(1 2 3 4 5)

->(def y (numList 12))
(1 2)

->(mult x y)
(1 4 8 1 4 0)

->(listNum (mult x y))
148140

En esta primera entrada nos pondremos con las función auxiliar que nos convierten un número en una lista. Para conseguir nuestra lista de dígitos habrá que dividir el número por 10, acumular el resto en la lista y volver a realizar la operación con el cociente de la división anterior hasta que la división nos de 0:

12345/10 -> cociente:1234 restos:(5)
1234/10-> 123 (4 5)
123/10-> 12 (3 4 5)
12/10-> 1 (2 3 4 5)
1/10-> 0 (1 2 3 4 5)

La descripción de la función ya nos da la pista de que usar la recursión será la solución mas elegante y ajustada. Como vamos a usar el resto y el cociente de la división entera nos haremos una función auxiliar que nos devuelva ambos resultados en un vector:

(defn div [x y] [(quot x y) (rem x y)])

-> (div 5 3)
[1 2]

La primera aproximación usando la recursión tal cual será hacernos una función auxiliar iNumList que irá haciendo las divisiones y acumulando los restos y que nos devolverá la lista acumulada cuando la división sea igual a 0, y llamar a esa función con una lista vacia inicial desde la función principal:

(defn numList [num] (iNumList () num))

(defn iNumList [acc x]
(let [[cociente resto] (div x 10) lista (cons resto acc)]
(if (zero? cociente) lista (iNumList lista cociente))))

Veamos la función iNumList. En primer lugar se usa let. Habria mucho que decir de esta forma especial que forma parte del nucleo mismo de Clojure pero basicamente enlaza símbolos con expresiones o valores constantes dentro de un vector y los pone a disposición de las expresiones que siguen a ese vector. En cada uno de los enlaces estan también disponibles los símbolos anteriores. Let soporta el enlace basado en la estructura abstracta (o "destructuracion") de la expresion a enlazar. Consiste en que podemos usar la estructura del dato para realizar el enlace. Se ve mejor con un ejemplo:

-> (let [[x,y] [10,2] z (* x y)] (list x 'por y 'igual 'a z))
(10 por 2 igual a 20)

Gracias a la destructuración del vector [10,2] usando su misma estructura en los simbolos [x,y] nos hemos ahorrado el tener que hacer:

->(let [v [1,2] x (v 0) y (v 1) z (* x y)] (list x 'por y 'igual 'a z))
(10 por 2 igual a 20)

En java no existe la destructuracion y la traduccion al mismo del ultimo codigo mas o menos seria:

Object[] ejemploLet () {
int[] v=new int[] {10,2};
int x=v[0],y=v[1],z=x*y;
return new Object[] {new Integer (x),"por",new Integer (y),
"es",new Integer (z)};
}

Volviendo a la función vemos que gracias a let tenemos nuestro resto, cociente y la lista en la que estamos acumulando los restos. Para ir construyendo la lista se usa cons que inserta un elemento en la cabeza de una lista. Usando if hacemos la recursión en el caso de que no sea 0 el cociente llamando a la misma función con el nuevo cociente.

Sin embargo el uso de la recursión tiene un pequeño problema ya que la parte de la memoria que se usa para almacenar los valores de cada llamada (stack) es mas limitada que la memoria normal donde se almacenan los datos de un programa (heap). Para numeros muy, muy grandes la llamada provocaría el famoso java.lang.StackOverflowError. Para evitarlo en Clojure usa una forma especial de hacer la recursión optimizada , consiguiendo la efectividad de los lenguajes imperativos sin perder la elegancia de la recursión. Esta forma especial se consigue con recur y tiene una importante limitación: debe estar en la última expresión evaluada de la función. Asociandolo a otra forma especial, loop podemos hacer una recursión segura y evitarnos la función auxiliar incrustandola dentro de la principal:

(defn numList [x]
(loop [acc () sig x]
(let [[cociente resto] (div sig 10) lista (cons resto acc)]
(if (zero? cociente) lista (recur lista cociente)))))

Loop marca el punto sobre el que se hace la recursión y enlaza en cada nueva llamada los parametros en un let implicito, iniciandolo con los valores () y el número original en la primera llamada.

En siguientes posts seguiremos desarrollando este ejemplo introductorio.

jueves, 11 de febrero de 2010

Introducción a Clojure: "Lost In a Soup of Parenthesis"

"Merece la pena aprender Lisp por la intensa experiencia de iluminación que tendras cuando finalmente lo domines; esa experiencia te hara un mejor programador para el resto de tus dias, aunque nunca vayas a usarlo demasiado."
- Eric Raymond Como ser un hacker

Puede parecer exagerado y poco pragmático pero si pongo la cita es porque mi experiencia como aprendiz de un dialecto del Lisp coincide plenamente con lo que dice. Lisp parece un alien de otra galaxia comparado con la mayoria de los lenguajes mas populares de la actualidad, dominados en mayor o menor medida (cada vez menos) por la sintaxis del lenguaje c y por el estilo de programar imperativo.
Como lo primero que extraña o repele del lenguaje es su sintaxis, es decir su apariencia, me ha parecido oportuno profundizar en el por qué de los paréntesis y como, si los vuelves a mirar con un poco de atención, cobran sentido. No se tarda mucho, ayudado por algun editor que te vaya marcando los inicios y cierres de los mismos, en olvidarlos y entonces solo te queda disfrutar de las virtudes de esa extraña sintaxis.
"Si quieres engañar a tu jefe para conseguir que te deje programar en Lisp, le puedes contar que es XML"
-Paul Graham La venganza de los "nerdos"

Y no solo es un "engaño", xml ha reinventado la esencia del Lisp de que el "codigo es igual a los datos" (code as data), lo que hace que el codigo en Lisp sea tan maleable y extensible, a la vez que minimalista, que cualquier capacida de metaprogramación de los lenguajes actuales palidezca al lado de el.
Esta comparación con el xml ha sido desarrollada para introducir a los no habituados al Lisp en uno de los libros que existen sobre el lenguaje y también, aplicado al Lisp en general en esta pagina.
Voy a intentar trasladar esa misma ruta del xml al Lisp para aquellos que sienten curiosidad de por que puñetas se le ocurrió a alguien diseñar un lenguaje de programación tan extraño.
Casi todo el mundo conoce XML, una forma estandar de representar datos que ha permitido una gran flexibilidad a la hora de integrar lenguajes y plataformas muy diferentes, sirviendo de lenguaje de definición de datos de tecnologias tan usadas como AJAX o los servicios web, asi como para representar la configuracion de casi todas las frameworks de java. Tambien ha sido usado como lenguaje de programación (por ejemplo XSLT) y es ahí donde empieza nuestro camino.

Empezaremos por este sencillo codigo en c:


Vamos a imaginar que tenemos un lenguaje escrito en xml y vamos a traducir la sintaxis de c al mismo (de hecho ya existe un libreria que te permite tranformar codigo java en xml y otra para c):


Bueno parece bastante razonable el resultado. Ahora podemos imaginar un poco lo que podríamos hacer con este xml antes de volver a transformarlo en código, compilarlo y ejecutarlo. Podríamos transformarlo de forma automática con xslt (por ejemplo en otro lenguaje), analizarlo en busca de mejoras, guardarlo o enviarlo a un servicio web que nos lo compilara. Podríamos buscar patrones sintácticos en el lenguaje que se repitieran una y otra vez y hacer transformaciones automáticas en función de etiquetas para ahorrarnos escribir código.
Dando un paso mas podriamos establecer una sintaxis uniforme para todos lenguajes en el que el xml representara el AST casi directamente. Estos lenguajes no necesitarian tranformar su codigo en un arbol sintáctico abstracto intermedio sino en XML, que es estandar y maleable de forma automatizada.
Si miramos con atencion el xml, ¿que diríamos?, ¿son datos o es código?, ¡Las dos cosas de forma intercambiable!
El XML esta muy bien pero tiene un pequeño defecto, rodea a lo que nos interesa de muchos carácteres lo que lo hace engorroso de escribir, de leer y enlentece su envío a través de cualquier canal de comunicación. Por eso se esta empezando a sustituir en las aplicaciones web por JSON, que es igual de legible pero mucho mas breve: en la programación casi siempre menos es mas.
Vamos a intentar cambiar el xml para hacerlo mas breve. Primero usaremos solo un '<' para iniciar las etiquetas xml y usaremos un simple '>' para representar los cierres de las etiquetas. El xml quedaria asi:


Bien, ahora vamos a establecer unas reglas para ahorrarnos mas caracteres en plan "convención sobre configuración": vamos a suponer que el que nos va a evaluar el definir una función es un tio listo y va a poder inferir el tipo que devuelve la función del retorno que esta dentro y que vamos a establecer obligatorio para una función. Vamos a establecer tambien que el primer simbolo despues de la marca de definir la función es su nombre ya que también es obligatorio. Los argumentos siempre iran justo despues del nombre y entre corchetes y su tipo también será inferido en tiempo de ejecución. La cosa quedaria así:


El "cuerpo" también vamos a darlo por supuesto y obligatorio para toda definición de función y dentro de la misma siempre el ultimo elemento será lo que se devuelve (si hay varios, en nuestro caso solo hay uno) así que nos lo podemos ahorrar. Al llamar a otra función lo primero sera siempre su nombre y lo que hay detras siempre sus argumentos en el mismo orden en el que estan definidos. Por ultimo acortaremos un poco los nombres: a definir-función lo llamaremos defn y para sumar usaremos el caracter '+'. La cosa
quedaría así:


Bueno parece que ya llegamos al final del camino. Personalmente '<' y '>' me parecen demasiado puntiagudos y ademas si queremos usarlos como "menos que" y "mayor que" igual hay problemas así que vamos a sustituirlo por... sí señor, por esos paréntesis tan denostados:


Hemos acortado bastante el código (tranformandolo en Clojure) y sin embargo seguimos teniendo las ventajas del xml. La definición de una función (y la llamada a la misma) es una lista que puede tener sublistas, es decir, una estructura de datos que podemos transformar, guardar, enviar a nuestro antojo antes de ser evaluada por el interprete. Más técnicamente podríamos decir que es una representación casi directa del arbol abstracto sintáctico misterioso que esta dentro de cualquier compilador y que o bien no se puede ver ni tocar o hay que hacerlo a traves de algun intermediario. En Clojure escribimos el arbol sintactico a la vez que el codigo y eso nos da el poder casi total sobre la sintaxis del lenguaje.
Bien pongamos un ejemplo de lo que se puede hacer gracias a los paréntesis. Asi es como se puede insertar una fila en una tabla con clojure:


Quienquiera que haya programado con jdbc habra acabado harto de repetir los bloques try-catch y de abrir y cerrar conexiones. El código de mas arriba en java sería:


Espero que al menos hayais visto el posible sentido de los paréntesis y os animeis a mirar de nuevo Clojure con un poco menos de extrañeza.