viernes, 2 de julio de 2010

Clojure: Lo Básico

"Aprende un lenguaje de programacion nuevo".

Lo dice en el libro The Pragmatic Programmer y también es una de las 97 cosas que un programador debe saber.

Pues quiero aprender Clojure y otros lenguajes (Groovy, Scala, Ruby, etc.) asi que he decido que conforme vaya leyendo y practicando sobre estos lenguajes iré posteando en mi blog una seria de "notas" sobre mi avance. Asi pues, este post es sobre lo básico de Clojure.

Clojure es un lenguaje de programación que compila bytecode, es decir, corre bajo la JVM. Es un lenguaje que aplica el paradigma de la programación funcional.

Podemos bajar Clojure de aqui.

Para programar con Clojure necesitamos abrir el Clojure REPL (Read Evaluate Print Loop). Para abrir el REPL necesitamos correr el siguiente comando en la linea de comandos:


java -jar clojure-X.Y.Z.jar


Donde X.Y.Z es la versión que tengan instalada de Clojure. Creo que la versión actual es la 1.1.

Ok, al ejecutar el comando anterior se abre el REPL y nos muestra el prompt 'user=>' para empezar a tirar codigo.

Muy bien, si escribimos nuestro "Hola Mundo":


user=> (println "Hola Mundo!")


Como resultado tenemos:


Hola Mundo!
nil


Todo programa en Clojure se construye a partir de Formas. Una Forma en Clojure es una unidad de código que puede ser evaluada y regresar un valor. Hay cuatro tipos de formas:


  1. Literales: Son formas que se evaluan a si mismas, como: cadenas, números y caracteres.

  2. Simbolos: Son formas que se evaluan a un valor. Pueden verse como si fueran variables pero en Clojure se maneja distinto. Por ejemplo los nombres de las funciones son símbolos y algunos operadores son símbolos también.

  3. Formas Compuestas: Como su nombre lo dicen son formas que contienen otras formas. Estas formas usan parentesis (), <>, y {}. Las formas que usan <> evalúan a un vector y las que usan {} a un mapa y las que usan () a listas. En Clojure las listas son evaluadas como llamadas a función. El primer elemento en la lista es la llamada a una función, y el resto de elementos son los argumentos de esa función. Por ejemplo, cuando en otros lenguajes llamar a una funcion que recibe 2 argumentos 'a' y 'b' se escribe de esta forma: funcion(a, b), en Clojure es (funcion a b).

  4. Formas Especiales: Son un tipo particular de Formas Compuestas. Estas son los bloques mas básicos para construir un programa en Clojure, ya que son usadas para controlar el flujo de un programa, definir funciones nuevas, definir variables, entre otras cosas.



Su pueden crear archivos con el código Clojure que deseamos ejecutar. Por ejemplo si creamos un archivo holaMundo.clojure en la ruta que deseemos lo podemos ejecutar de la siguiente forma:


user=> (load-file "/holaMundo.clojure")


Otra forma de ejecutar el archivo holaMundo.clojure puede ser asi:


java -jar clojure-X.Y.Z.jar /holaMundo.clojure


Para definir "variables" se utiliza la Forma Especial "def".


(def nombre-variable valor-variable)


Donde "nombre-variable" es el nombre que le vamos a dar (o el símbolo al cual se va a ligar el valor) y "valor-variable" puede ser cualquier tipo de forma de Clojure (literal, símbolos, compuestas y especiales).

Clojure define reglas para nombrar a los símbolos:


  1. Se puede usar cualquier carácter alfanumérico y también ?,+,*,!,-, y _.

  2. No pueden empezar con números (como en Java).

  3. Pueden usar el carácter :, pero no al principio ni al fin del nombre del símbolo y tampoco se puede repetir.



Dadas estas reglas entonces los siguientes nombres de símbolos son correctos: variable, var123, *nombre*, nombre+apellido, esMujer?, etc.

Por convención, los nombres de símbolos en Clojure son con letras en minúsculas donde cada palabra es separada con guion medio '-'. También, los símbolos que representan constantes o parámetros globales deben empezar y terminar con '*'.

Namespaces



Cuando se definen variables (o símbolos ligados a un valor) con 'def' este nombre se mantiene dentro de un espacio de nombres o Namespace. Cuando escribimos programas y sobre todo programas grandes es muy probable que ciertas variables que definamos puedan llegar a tener problemas porque en alguna otra parte del programa existe otra variable que se llama igual, para evitar esto usamos los Namespaces.

Recuerdan el prompt 'user=>' que sale al inicar el REPL?, pues es el Namespace por default 'user'. Asi, una variable dentro de un cierto Namespace se puede hacer referencia a su valor usando el Namespace como parte del nombre de la siguiente forma: namespace/nombre.


user=> (def mi-variable 12)
#'user/mi-variable
user=> user/mi-variable
12
user=> (print mi-variable)
12nil
user=> (print user/mi-variable)
12nil
user=>


Lo anterior muestra como definimos una variable 'mi-variable' con el valor 12, y verán que se puede hacer referencia a esta usando el Namespace 'user' o no.

Para crear un nuevo Namespace usamos 'ns' de la siguiente forma:


user=>
user=> (def mi-variable 12)
#'user/mi-variable
user=> (ns mi-namespace)
nil
mi-namespace=> (def mi-variable 21)
#'mi-namespace/mi-variable
mi-namespace=> mi-variable
21
mi-namespace=> mi-namespace/mi-variable
21
mi-namespace=> user/mi-variable
12
mi-namespace=>


En lo anterior verán que 'ns' se utiliza para crear nuevos Namespaces y ademas se puede ver como se puede hacer referencia a variables en otros Namespaces.

If-else



Todo lenguaje de programación debe proveer estructuras condicionales para alterar el comportamiento de la ejecución de un programa dependiendo de ciertas situaciones.

En Clojure el famoso "if" se escribe asi:


user=> (if (= 1 1) "Son iguales" "No son iguales")
"Son iguales"
user=> (if (= 1 2) "Son iguales" "No son iguales")
"No son iguales"


Noten como el "if" y la comparación "=" realmente son llamadas a funciones que reciben parámetros. Es decir, "if" es una función que recibe en su primer parámetro la Forma que se evalúa a true o false y si es true devuelve lo que recibe en el segundo parámetro y si no lo que recibe en el tercer parámetro.

Existe otra forma del if y es "if-not" y se usa así:


user=> (if-not (= 1 2) "No son iguales" "Si son iguales")
"No son iguales"
user=> (if-not (= 1 1) "No son iguales" "Si son iguales")
"Si son iguales"


Bueno, le voy a parar aquí continuare en otro post con otras cosas básicas como funciones y ciclos.