blog de ezamudio

Un caso interesante de Scala vs. Java

Hace mucho tiempo, implementé un codificador para Base 64 en Java. El algoritmo es relativamente simple: consiste en codificar 3 bytes como 4 caracteres de texto, tomados de un alfabeto de 64 posibles caracteres (de ahí el nombre). El alfabeto está compuesto de las letras A a la Z mayúsculas y luego minúsculas, los dígitos y los signos + y /. Existe además un símbolo especial = para indicar la ausencia de datos, cuando se codifica un bloque de menos de 3 bytes.

Cuando lo implementé en Java, lo primero que hice fue definir el alfabeto, como un arreglo de char Para ello, dentro de la clase que iba a contener los dos métodos (codificar y decodificar), hice esto:

Serialización de alto desempeño: Protocol Buffers

El 11 de octubre de 2011 liberé la versión 1.6.0 de jAlarms, y una de las características nuevas de esta versión es la posibilidad de poner un servicio de envío remoto de alarmas.

Una manera de lograr esto es con RMI, de lo cual ya hablaré posteriormente, pero una manera mucho más eficiente es con los Protocol Buffers de Google. Esta es una herramienta bastante buena para comunicación en red, de hecho es lo que usa Google para la comunicación entre sus procesos, porque ofrece las siguientes ventajas:

  • Alto desempeño para serializar y de-serializar objetos.
  • Portabilidad e interoperabilidad: permite comunicación entre aplicaciones hechas en distintos lenguajes y plataformas.
  • Ancho de banda/espacio: los mensajes serializados ocupan muy poco espacio en comparación con otros formatos como XML, JSON, o incluso serialización binaria nativa de varias plataformas.

Escribe menos código tedioso, con Lombok

Lombok es una biblioteca de software para Java, que contiene varias transformaciones aplicadas al código en tiempo de compilación, ayudándonos con varias de las tareas más tediosas de la programación en Java.

El Jar es además ejecutable, contiene un programa que detecta los IDE's instalados, como Eclipse (y los basados en Eclipse como STS), IDEA y me parece que NetBeans. Esto es necesario en el IDE para la compilación, pero en realidad si se usa un editor de texto regular para programar, o un sistema automatizado de construcción de proyectos, basta con agregar lombok.jar al classpath de compilación.

La versión más reciente es la 0.10.0 que trae algunas monerías adicionales a las que ya se incluían en versiones anteriores y además ya no requiere incluir el Jar en tiempo de ejecución; todas las transformaciones se realizan en tiempo de compilación.

También ya es compatible con Java 7.

Bueno pero, ¿qué son estas transformaciones? Vienen en forma de anotaciones, y creo que la manera más fácil de entenderlas es con ejemplos.

El poder de Option: Más allá del pattern matching

Una de las primeras cosas a las que le tomamos gusto cuando aprende Scala, sobre todo si venimos de Java, es al pattern matching, aunque una de las cosas que desconciertan un poco es la manera en que funcionan los mapas.

En Java, si tenemos un java.util.Map simplemente le pedimos el valor para una llave, y nos devuelve el valor, o null si no lo tiene. O si el mapa acepta nulos, entonces puede devolvernos null si es que tiene null guardado bajo la llave que indicamos. ¿Cómo podemos diferenciar entre el caso en que el mapa no tiene la llave, o si tiene almacenado null bajo esa llave? Podemos verificar usando containsKey.

Entonces, tenemos los siguientes casos:

if (map.get("X") != null) {
  /* obtuvimos un valor */
} else if (map.containsKey("X")) {
  /* Tenemos null almacenado bajo la llave
} else {
  /* No existe esa llave en el mapa */

}

Concurrencia sin dolor en Java puro, parte 3

En el post anterior ya vimos una alternativa muy buena para manejo de concurrencia en Java: el modelo de STM.

En esta tercera y última parte, veremos el modelo de actores. Esto es algo que tampoco es nuevo, viene de otros lenguajes; en la JVM, Scala es un lenguaje que incluye este modelo de concurrencia. Pero gracias a una biblioteca llamada Akka, la cual fue escrita en Scala, podemos usar el modelo en Java.

Visto de una manera muy simple, un actor es un objeto que responde (o reacciona) a mensajes que se le envían, uno a la vez. Los mensajes se le pueden enviar de manera asíncrona, un mecanismo conocido como fire-and-forget, por lo que puede haber varios hilos distintos enviando mensajes al mismo actor y dichos mensajes serán encolados para que los vaya atendiendo uno por uno.

Concurrencia sin dolor en Java puro, parte 2

En el post anterior, ya vimos lo difícil que es manejar concurrencia en Java, cuando usamos únicamente las herramientas que nos ofrece el JDK. Algunos llaman a este modelo synchronize and suffer.

En esta segunda parte, veremos una alternativa, implementada desde hace mucho tiempo en Lisp, y recientemente en Clojure, un lenguaje alterno para la JVM basado en Lisp. Este modelo de llama memoria transaccional, o STM, Software Transactional Memory.

Clojure

Clojure es un lenguaje alternativo para la JVM, basado en Lisp. En Clojure no hay variables, todo es inmutable. Sí, otra vez: en Clojure no hay variables. O si de plano no pueden concebir algo así, entonces pueden verlo de esta forma: en Clojure, todas las variables son inmutables. Es decir, una vez que asignan un valor a una variable, no se puede volver a modificar. Cuando dicen a=1, ya no pueden posteriormente cambiar el valor de a (por lo tanto, si ya no pueden variar, no se pueden llamar variables... se llaman valores).

Concurrencia sin dolor en Java puro, parte 1

El miércoles 7 de septiembre tuvimos el honor de recibir una plática del Dr. Venkat Subramaniam, en una sala facilitada por SynergyJ y la comunidad SpringHispano.

El tema de la plática fue cómo manejar concurrencia en Java y estuvo muy, muy interesante.

Todos sabemos que el JDK ofrece varias facilidades para manejo de concurrencia, pero son de muy bajo nivel: la palabra clave synchronized y los Locks, principalmente. Pero aquí se nos mostró cómo muy rápidamente se pueden complicar las cosas, primero por la cantidad de código que hay que escribir, segundo porque hay que conocer muy bien el modelo de memoria de la JVM y la manera en que maneja los Threads, y tercero porque hay muchas cosas que pueden salir mal si ponemos un synchronized en el lugar equivocado, o si olvidamos ponerlo, etc. No sólo deadlocks, sino también livelocks: cuando un hilo se queda esperando a que otro hilo libere un recurso para poder continuar. Esto realmente va en contra de la concurrencia; es un desperdicio tener hilos esperando indefinidamente a que otros hilos liberen los recursos que requieren.

Lenguaje para scripting: Scala o Groovy?

Ultimamente que he estado descubriendo varias de las bondades de Scala, me puse a pensar si también sería una buena opción para hacer scripting. Por "scripting" me refiero simplemente a hacer programas sencillos que se ejecutan una vez en la vida, o de manera muy esporádica, o como tarea periódica no interactiva.

Hay ocasiones en que los scripts de bash no son suficientes. El ejemplo típico es cuando te piden leer un archivo de texto en cierto formato, comparar los datos con lo que tengas en una base de datos, hacer un archivo de resultados y enviarlo por correo a ciertas personas. Sobre todo si ya tienes varias partes de esta lógica implementadas en un sistema que tienes hecho en Java.

Hacer scripting en Java es extremadamente tedioso. Para empezar, estrictamente lo que se dice scripting pues no se puede hacer. Obviamente puedes hacer una clase con un main y desde ahí invocar el código que necesitas, procesar datos, etc etc pero pues hay que compilarlo y luego ejecutarlo y para eso hay que ponerle un classpath probablemente bastante largo y es una lata. Simplemente el tener que estar compilando ya te lleva a que uses alguna herramienta para llevarlo como proyecto y, sin importar que uses Gradle/Ant/Maven/Ivy, ya se complicó demasiado el asunto.

Integración entre lenguajes JVM

En 2007 hice una biblioteca de software llamada j8583 y desde entonces la utilizo, actualizándola de vez en cuando con algunas optimizaciones o características nuevas.

Una de estas características nuevas se la agregué cuando comencé a utilizar j8583 en Groovy. Resulta que Groovy tiene azúcar sintáctica para realizar algunas operaciones, por ejemplo:

arreglo.add(elemento); //Java
arreglo << elemento //Groovy

outputStream.write(datos); //Java
outputStream << datos //Groovy

arreglo.get(5); //Java
arreglo[5] //Groovy

//Java
mapa.get("llave");
mapa.put("llave", valor);

//Groovy
mapa['llave']
mapa["llave"] = valor

HolaMundo en Scala III: Mensajes síncronos, patrones

Sigo estudiando algo de Scala, de manera muy empírica aún, leyendo varias referencias y fuentes de información de todo tipo, y sigo aprendiendo bastante.

He seguido orientándome más a la parte de multiprocesamiento, que a la parte de programación funcional. Sigo jugando con el ejemplito del servidor holamundo; ahora le puse manejo de excepciones, condiciones de salida y he estado viendo algo del pattern matching que parece ser una parte importante de Scala, para varias cosas distintas (desde usarse en vez de un simple cast que haríamos en Java, hasta manejo de excepciones, y por supuesto los mensajes entre actores).

Condiciones de salida

Primero que nada, ya le agregué una condición de salida a la clase Servidor, para que dependiendo de algún mensaje recibido por un socket, se pueda terminar con el ciclo que estaba previamente definido como infinito. Para ello agregué un nuevo método con la intención de que lo invoque un actor desde otro proceso, y el debido manejo de una excepción que esta secuencia de salida genera. Con esto ya podremos apreciar algo del manejo de excepciones, que es ligeramente distinto a Java. Queda así al final:

Distribuir contenido