Primeros pasos con Scala

Primeros pasos con Scala

Periódicamente tengo una crisis y necesito tocar algo de código para sentir de primera mano algo de tecnología. El verano es una época propicia para ello.

La plataforma Java evoluciona rápidamente y cada día hay más opciones de lenguaje a la hora de programar para ella.

Personalmente creo va a costar algún tiempo que nuestros clientes se animen a integrar otros lenguajes en sus frameworks de desarrollo ya que incluso muchas organizaciones están a medio paso de instruir a su gente y formalizar sus arquitecturas.

Normalmente hay que ser pacientes y amortizar los costes de formación en las organizaciones y no creo que quieran ni deban asumir riesgos innecesariamente. Su negocio habitualmente no suele ser la tecnología sino que la usan como medio.

Otra cosa distinta es que las empresas que proporcionamos servicios y que tendremos que estar al día de los avances y modas y, desde luego, no cerrarnos al cambio. Poco a poco debemos ir viendo donde encajan las piezas, cuando son las tecnologías estables, maduras y confiables y cuando transferirlas.

Me he decidido por cacharrear con Scala porque me resulta más atractivo aprender los detalles de un lenguaje con características funcionales, que puede complementar a Java, que otro posible lenguaje que simplemente presuma de hacernos más fácil programar lo mismo (obviamente con su sutilezas y valor que no dudo que cada uno aporte).

Ya os adelanto que no considero Scala como un lenguaje sencillo pero es que las cosas potentes y versátiles tampoco suelen ser sencillas. Os invito a que leáis un poquito sobre programación funcional porque, de otro modo, algunas cosas que veréis que se puede hacer con las funciones parecerán algo enrevesado y sin sentido: http://es.wikipedia.org/wiki/Programación_funcional

Estoy desarrollando en Mac aunque a todos los efectos creo que da igual el entorno.

Entorno

Voy a partir del eclipse Helios porque aunque ya hay una versión más moderna (Indigo) porque el plugin me ha dado algún problema de instalación en esta última. Para lo que voy a hacer tampoco es algo relevante.

Instalaremos el pluggin de ScalaIDE para Eclipse de http://www.scala-ide.org/.

Scala IDE

Para instalarlo solo nos tenemos que ir al menú de ayuda de nuestro eclipse y pulsar install new software:

Install new software

Deberemos añadir el trayecto donde se encuentran los plugins: http://download.scala-ide.org/update-current:

Scala URL

Ahora solo tenemos que crear un proyecto de tipo Scala:

New Project

Y luego un objeto Scala:

New Object

Y ya tenemos es esqueleto que para nuestra aplicación.

Solo voy a comentar algunos detalles del lenguaje que ilustraré con alguna captura de pantalla. Serán variaciones de los ejemplos que vienen en la propia documentación o el libro programming Scala de Dean Wampler y Alex Payne (que está disponible gratuitamente vía Web), tratando de simplificarlos. Algunos, aunque los veáis tan cortitos u obvios, me han llevado un rato porque todavía hay poca documentación y se me hace todavía un poco críptica.

Como en lo que más he trabajado es en Java y C++ haré alguna referencia a cosas que me recuerden. Al final, todo se parece en cierta medida a otra cosa.

Una clase se define con class aunque también puede ser definida con object comportándose como un singleton y por lo tanto sus funciones son estáticas.

Un aplicación empieza en un main como en Java:

Object

Los paquetes se importan con import y para hacer referencia a un grupo de clases en vez de usar * usaremos ‘_’ ya que las funciones también son objetos en Scala y el * es un operador que se puede sobre-escribir.

La importación del paquete java.lang es implícita, por lo que no lo tenemos que importar.

No hay diferencia entre tipos nativos y objetos, todo es un objeto en Scala. Utilizamos var para declarar los objetos variables y val para solo lectura (por lo tanto tienen que ser inicializados al ser declarados). Ojo que es el mismo concepto que en C++. Una cosa es que el puntero sea invariable a que apunte a una zona de memoria invariable. Es decir, si declaro como val un array, esto significa que con esa variable no puedo apuntar a otro array, no que no pueda cambiar el contenido de los elementos del array 😉

El valor de retorno por defecto de una función es la última expresión ejecutada en su cuerpo:

Object2

El constructor principal es el propio cuerpo de la clase. Para acceder a los miembros podemos omitir el operador punto:

Object3

Las funciones tienen argumentos por defecto. Así no hay que sobrecargarlas (redefinirlas) innecesariamente delegando una sobre otra (como teníamos que hacer en Java para simular el mismo comportamiento):

Object4

Se pueden pasar los nombres de los parámetros y así, sabemos cual es el parámetro por defecto que queremos usar e incluso no vernos forzados por el orden de su declaración. Tiene sentido:

Se pueden retornar más de un valor a la vez con el concepto de tupla (para evitar utilizar objetos forzados que no forman parte del modelo real .. aunque ojo con los modelos anémicos). Hay reconocimiento de patrones, que es algo diferente al instanceof y es una característica importante de los lenguajes funcionales.

Se puede crear funciones anidadas con ocultación de ámbito. Es decir, crear una función en el cuerpo de otra. Esto es muy útil y es recurrentemente utilizado con funciones anónimas. Las funciones se pueden pasar como parámetros (me recuerda a punteros a funciones):

Estas funciones parciales puedes combinarse para proporcionar combinados. Podemos ver que hay una función test que se ejecutará con unas sentencia booleana y, a partir de este parámetro, se ejecutará el código de una función parcial u otro. Revisadlo bien porque tiene su importancia y complejidad.

Object6

Podemos hasta hacerlo más divertido condicionando la utilización de funciones parciales por el resultado de una expresión u otra función, como en el siguiente ejemplo. Fijaos que podríamos decir que es una forma de polimorfismo encubierta. El mismo código “test = traza orElse elige(hora)” ejecuta en base a una variable dos porciones distintas de código, correspondiente a dos funciones parciales.

Esto puede ser muy potente pero creo que puede hacer los programas muy difícil de seguir para gente con un pensamiento tradicional donde siempre que invoca a la misma función, con los mismos valores, espera obtener el mismo resultado.

Object7

Existe otro concepto de funciones Currying que consisten en convertir funciones que tienen varios parámetros en encadenamiento de funciones con un solo parámetro.

Las funciones pueden declarar atributos implícitos. Esto puede despistar ya que es como si fuera un parámetro por defecto pero parcialmente, desde que se le da el valor hasta final del bloque actual.

Algo sorprendente es la capacidad de llamar a funciones por nombre (y no por valor). Esto consiste en que invocamos una función con una expresión y, en vez de ser evaluada como pasaría con cualquier parámetro, la propia expresión se evalua como código dentro de la función invocada.

Mirad detenidamente el ejemplo porque el resultado obtenido puede desconcertar un poco:

Object8

Los bucles sobre listas son sencillos y potentes:

Podemos actuar sobre todos los elementos de una lista de un modo sencillo. Con el uso de map, iteramos sobre todos los elementos. El carácter ‘_’ nos vale como sustituto del elemento a iterar:

Object10

Trabajar con mapas es igual de sencillo que trabajar con listas. La única diferencia es que el valor sobre el que iteramos es una tupla (que ya hemos visto anteriormente):

Object11

Se pueden utilizar closures. Definir funciones que utilizan variables definidas en otros lugares. En cierto modo me recuerda al ligado de variables de JavaFx http://www.adictosaltrabajo.com/tutoriales/tutoriales.php?pagina=javafx. Los closures combinados con funciones parciales condicionadas nos darán muchísima potencia.

Object12

El método equals compara, al igual que ==, si los objetos son iguales. Eq comprueba si las dos referencias apuntan al mismo objeto.

El método copy clona un objeto.

Hay inferencia automática de tipos en la asignación. Es decir, la variable se convierte automáticamente al tipo del que es inicializada.

Existen los trails que son una especie de interfaces tipo java con implementación opcional. Muy útil cuando hay comportamientos similares a sobrecargar en distintas clases derivadas o que implementan un interfaz (en caso de java) y que normalmente se resuelven con una delegación o (malamente resuelto) copiando y pegando código:

Object13

No existe un valor de retorno NULL sino que es un objeto o None o Some que deriva de Option.

Existe el concepto de Actores basado en el modelo de Erlang. A través de mensajes podemos evitar problemas de concurrencia.

Existen muchas más cosas de las que no he hablado como los valores lazy (que se inicializan solo la primera vez que se usan), la implementación de DSLs, los tipos de datos, la ejecución tanto en plataforma .Net como java, las herramientas, etc. pero ya sería enrollarme demasiado para un primer vistazo.

Conclusión:

Francamente me ha sorprendido Scala y me ha hecho que me interese por la programación funcional. Ya sabes, cuando abres una puerta, normalmente te encuentras con otras que también te apetece abrir…

Como aspecto negativo (que podría ser positivo) es que creo que requiere dominar mucho el lenguaje y los conceptos subyacentes para sacarle partido y que, en muchos casos, depurar y mantener el código de otro puede resultar tremendamente complejo: pequeños cambios en la declaración de las funciones tienen significados y comportamientos muy distintos.

Esto no es para aficionados … por eso decía que tal vez sea también positivo.

Seguiré jugando un poco con Scala y espero animarme pronto a dar continuidad a este tutorial.

Recursos interesantes disponibles: