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.

1 comentario:

  1. La verdad es que te hubiese quedado redondo si explicases qué es cada uno de los elementos de:

    (defn insert [data]
    (sql/with-connection db
    (insert-rows :r [data])))

    Estoy empezando con esto y no soy especialmente ágil. Pero creo que con tus ejemplos, voy lanzado...

    ResponderEliminar