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.