Getters y setters: No gracias!

noviembre 28, 2011 Deja un comentario

Según los principios básicos de la encapsulación es recomendable separar los elementos del contrato publico de una clase de su implementación interna. Esto elimina acoplamiento entre una clase y sus clientes, de manera que posibles cambios en la implementación de la clase no afecte a dichos clientes. Para los clientes de la clase, los cambios en la implementación son transparentes. Una de las buenas practicas mas habituales que ilustra este principio es el de la creación de getters y setters para acceder al estado [mutable] de una clase, que se mantiene inaccesible en atributos privados. La correspondiente mala practica seria exponer dichos atributos como miembros públicos.

Así, diríamos que los atributos privados son parte de la implementación interna y los getters y setters son parte del contrato publico. Si por ejemplo necesitaramos realizar alguna operacion antes de retornar, o modificar, el estado de la clase, podríamos hacerlo en dichos getters y setters. Si hubiéramos permitido acceso directo a los atributos, tendríamos un grave problema. No podríamos realizar estas operaciones sin romper código cliente.

En la practica, en Java, esta técnica tiene como consecuencia una verborrea de código tedioso de escribir que tiende a engordar las clases sin demasiado sentido. Es cierto que existen IDE’s que generan los getters y setters automaticamente, pero sigue siendo un engorro tener todas esas lineas que no son funcionalmente significativas.

Afortunadamente en Scala este problema esta bien resuelto. Veamos antes un ejemplo mínimo en Java:

public class Message {
	private String message;

	public void setMessage(String message) {
		this.message = message;
	}
	public String getMessage() {
		return message;
	}

	public void print() {
		System.out.println("The message is " + message);
	}
}

Nada especial. Ahora veamos lo mismo en Scala

class Message {
	var message:String = _

	def print() = {
		println("The message is " + message)
	}
}

Mucho mejor, la mitad de lineas. Pero, ¿donde están los setters y getters? Parecería que nos los hemos ahorrado, con lo que la comparación no seria justa. En realidad, lo que ocurre es que el compilador ha generado dichos setters y getters automáticamente, veamoslo en una sesión interactiva:


scala> class Message { var message:String = _ }
defined class Message

scala> val m = new Message
m: Message = Message@15daa9e

scala> m.message = "hola"
m.message: String = hola

scala> m.message
res0: String = hola

De nuevo parece que no existen dichos métodos, sino que da la impresión de estar accediendo directamente a un atributo publico de la clase, mandando a la papelera la encapsulación. Pero no es así, lo que estamos viendo es un ejemplo del Uniform Access Principle, de manera que para el cliente de la clase es indistinguible el acceso a los atributos directamente del acceso indirecto a través de métodos. En el ejemplo, el cliente de la clase esta usando métodos cuya sintaxis de invocación es igual al acceso directo al atributo.

Los métodos generados por el compilador no tienen ningún efecto mas allá del acceso y modificación, el equivalente a la implementación de getter y setter del ejemplo en Java. Si quisiéramos realizar alguna operación adicional, que es la razón de ser de los getters y setters, en Scala haríamos algo como esto:


class Message {
	private var msg:String = _

	def message_=(m: String) = {
		// codigo adicional
		msg = m
	}

	def message = {
		// codigo adicional
		msg
	}

	def print() = {
		println("The message is " + message)
	}
}

La modificación con respecto a la implementación anterior seria transparente para el cliente. Gracias al Uniform Access Principle, Scala respeta la encapsulación a la vez que reduce sustancialmente el tamaño del código, al no ser necesario escribir getters y setters por norma. Como curiosidad vemos que el método

def message_=(m: String) = { …

es invocado al usar una expresion como

val m = new Message

// invocacion del setter

m.message = «Hello»

En la siguiente entrega veremos como se produce un ahorro similar al declarar parámetros en el constructor principal de una clase, y como se traduce el código que hemos visto a bytecode en la clase resultante de la compilación.

Referencias

[1] http://www.dustinmartin.net/2009/10/getters-and-setters-in-scala/

Categorías: Sin categoría

El ADN de Scala

noviembre 25, 2011 Deja un comentario

Scala es un lenguaje de nueva generación y como tal puede aprovechar el conocimiento derivado de los errores y aciertos de lenguajes de programación anteriores. Scala incluye mecanismos de otros lenguajes que considera aciertos y evita incluir aquellos elementos que resultaron ser una mala elección. La lista de lenguajes y características correspondientes incluidas en Scala forman su particular ADN[1]. Veamos algunos elementos principales

  • La sintaxis «curly brace» de C,C++, Java
  • El modelo de objetos uniforme de Smalltalk
  • Los for-comprehension y views (inspirados en los type-classes) de Haskell
  • Los tipos abstractos inspirados en mecanismos similares de ML y OCaml
  • El tratamiento de propiedades (atributos con getters y setters) de Sather
  • El trasplante de características[1] de lenguajes funcionales a la JVM de Pizza
  • El estilo hibrido funcional/orientado objetos tambien visto en OCaml y Moby
[1] http://www.scala-lang.org/docu/files/ScalaOverview.pdf
[2] Funciónes de orden superior, programación genérica y pattern matching
Categorías: Fundamentos

Parámetros por nombre

noviembre 18, 2011 Deja un comentario

En Scala existe la posibilidad de pasar parámetros de tipo función con una sintaxis que parece corresponder al de pasar el resultado de evaluar dicha función, pero cuyo efecto es el de pasar la función en si, que luego sera evaluada dentro del método. A este mecanismo se le llama parámetros por nombre (by name parameters). Veámoslo


scala> def evaluar_normal(condicion: () => Boolean) = {
| if(condicion()) println("true")
| else println("false")
| }
evaluar_normal: (condicion: () => Boolean)Unit

scala> def evaluar(condicion: => Boolean) = {
| if(condicion) println("true")
| else println("false")
| }
evaluar: (condicion: => Boolean)Unit

scala> evaluar_normal(() => 5 > 3)
true

scala> evaluar(5 > 3)
true

Como vemos, en el método evaluar_normal utilizamos la sintaxis habitual para definir una función que es el parámetro del método. En este caso dicha función es simplemente la evaluación de 5 > 3, cuyo resultado es true. El método evaluar es una implementación alternativa que utiliza un parámetro por nombre. La sintaxis para definir este parámetro es

condicion: => Boolean

en vez de la habitual usada en el otro método

condicion: () => Boolean

Este mecanismo permite llamar al método evaluar de manera mas sencilla, sin tener que rodear la expresión 5 > 3 de una función. Simplemente se le pasa 5 > 3 a secas. La otra propiedad de los parámetros por nombre es la de que no son evaluados al hacer la llamada al método, sino solo al referenciarse desde dentro de este método. Podemos verlo aquí:


scala> def func() = {
| println("func")
| 5 > 3
| }
func: ()Boolean

scala> evaluar(func())
entrando
func
true

A simple vista parecería que la expresión 5 > 3 se evaluaría antes de la invocación de evaluar, pero como vemos, no es así.

Categorías: Sin categoría

Chuleta de Scala

noviembre 10, 2011 Deja un comentario

Uno de los recursos que encontramos en el reciente lanzado portal de documentación de Scala es este cheatsheet, o chuleta. No es muy extensa pero viene bien para recordar algunos de los casos de sintaxis menos intuitivos. ¿No viene nada sobre el uso del guion bajo? No hay problema, ya teníamos una chuleta exclusiva para eso!

Categorías: Sin categoría

La funcion implicitly

noviembre 7, 2011 Deja un comentario

En un post anterior hablábamos sobre el método to invocado sobre un entero para generar un Range. A pesar de que la clase Int no define un método to es posible hacer esta invocación en virtud de una conversión implícita de Int a RichInt, siendo esta ultima la clase que define el método en cuestión. Las conversiones implícitas son un mecanismo poderoso de Scala que permite, entre otras cosas, el uso/patrón pimp my library.

Sin embargo, el uso de conversiones implícitas puede hacer el código menos inteligible, al no estar siempre claro que conversiones entran en juego; de ahí su nombre, estas conversiones no son tan visibles como el código explicito que escribe el programador. Las conversiones implícitas pueden entrar en acción de manera aparentemente imprevisible, lejos del código donde fueron definidas. Una herramienta que podemos utilizar para aliviar este problema es la función de Scala implicitly.

scala> 1 to 5
res0: scala.collection.immutable.Range.Inclusive = Range(1, 2, 3, 4, 5)

scala> implicitly[Int => { def to(i: Int): Range }]
res1: Int => AnyRef{def to(i: Int): Range} =

scala> res1(1)
res2: AnyRef{def to(i: Int): Range} = 1

scala> res2.getClass
res3: java.lang.Class[_ <: AnyRef] = class scala.runtime.RichInt

scala> res2.to(5)
res4: Range = Range(1, 2, 3, 4, 5)

En este caso la función implicitly (invocada usando ‘[‘ y ‘]‘) acepta como parámetro una especificación de la conversión solicitada. Por tanto le pasamos

Int => { def to(i: Int): Range }

cuya traducción sería:

conversion de Int a una clase que defina un método to, que al pasarle un Int devuelva un Range

Como vemos en la sesión interactiva, esta invocación es exitosa al devolver una función que realiza esta conversión. Luego aplicamos esta conversión al literal 1 (RichInt), que nos devolverá un objeto de una clase que tendra el método deseado, to. Aqui es donde se clarifica la situación, puesto que podemos saber cual es la clase mediante la cual es posible invocar le método deseado. En este caso es efectivamente RichInt, cosa que sabíamos de antemano. Pero en general, esta técnica nos permite entender que esta pasando y que clases están interviniendo al entrar en acción una conversión implícita. Finalmente, vemos que el resultado de invocar to es el mismo que en la primera linea.

Referencias

http://stackoverflow.com/questions/3855595/scala-function-implicitly

Categorías: Sin categoría

val masUno = (_:Int) + 1

octubre 8, 2011 Deja un comentario

Mirando la chuleta de los usos del guion bajo me encontre con un uso que no conocia. Digamos que queremos definir una funcion anonima (que no un metodo) para sumar uno a un numero entero. Podriamos hacer esto

scala> val masUno = (x:Int) => x + 1
masUno: Int => Int = <function1>

scala> masUno(1)
res70: Int = 2

pero resulta que tambien existe esta sintaxis

scala> val masUno = (_:Int) + 1
masUno: Int => Int = <function1>

scala> masUno(1)
res71: Int = 2

curiosidades de Scala..

Categorías: Sin categoría

Producto Cartesiano (II)

septiembre 29, 2011 Deja un comentario

En una entrada anterior vimos como generar un producto cartesiano a traves de un for comprehension. Esta vez vamos a hacerlo usando las funciones map y flatmap, dos elementos basicos y recurrentes en la programacion funcional con colecciones. Recordemos la tecnica anterior

scala> for(i <- 1 to 6; j <- 3 to 5) yield (i, j)
res27: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (3,3) (3,4), (3,5), (4,3), (4,4), (4,5), (5,3), (5,4), (5,5), (6,3), (6,4), (6,5))

La funcion map crea una nueva coleccion resultante de aplicar a cada uno de los elementos de la coleccion original una funcion proporcionada. Repito, crea una nueva coleccion. En el tipico estilo funcional, la coleccion original no se transforma, sino que map retorna una coleccion nueva. Map requiere como argumento la funcion que hara la transformacion de los elementos.

Aqui creamos una lista resultante de multiplicar por dos los elementos de la lista original

scala> List(1,2,3,4,5) map (x => x*2)
res65: List[Int] = List(2, 4, 6, 8, 10)

Una formulacion equivalente

scala> List(1,2,3,4,5) map (_ * 2)
res66: List[Int] = List(2, 4, 6, 8, 10)

Usando las mismas listas que en la anterior entrada, el producto cartesiano usando map quedaria asi

scala> 1 to 6 map (i => 3 to 5 map ( j => (i,j)))
res67: scala.collection.immutable.IndexedSeq[scala.collection.immutable.IndexedSeq[(Int, Int)]] = Vector(Vector((1,3), (1,4), (1,5)), Vector((2,3), (2,4), (2,5)), Vector((3,3), (3,4), (3,5)), Vector((4,3), (4,4), (4,5)), Vector((5,3), (5,4), (5,5)), Vector((6,3), (6,4), (6,5)))

En este caso usamos dos maps anidados para lograr el mismo efecto que usar dos generadores (i <- 1 to 6; j <- 3 to 5) en un for-comprehension. Pero existe una pequeña diferencia en el resultado. Si nos fijamos bien, vemos que en este caso la lista es de listas de tuplas

IndexedSeq[IndexedSeq[(Int, Int)]].

Mientras que lo que realmente queremos, como vimos con el for-comprehension, es una lista de tuplas

IndexedSeq[(Int, Int)]].

El grado de anidamiento adicional viene al usar un map dentro de otro, ya que cada elemento del map exterior se ha convertido en una lista

.. map(i => 3 to 5 ..

La solucion a esto es usar flatMap que «aplana» la estructura, al extraer y concatenar los elementos de las listas internas. Lo vemos aqui

scala> 1 to 6 flatMap (i => 3 to 5 map ( j => (i,j)))
res64: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (3,3) (3,4), (3,5), (4,3), (4,4), (4,5), (5,3), (5,4), (5,5), (6,3), (6,4), (6,5))

y ahora el resultado si es identico.

for, map, flatMap y filter

La eleccion de las dos formas de generar un producto cartesiano no es casual, ni lo es la equivalencia de las dos implementaciones. Estos dos ejemplos sirven para ilustrar el hecho de que cualquier for-comprehension es expresable[1] en terminos de map, flatMap y filter. De hecho, el compilador de Scala realiza internamente la traduccion de un for comprehension a codigo usando estas tres funciones. Un for-comprehension es syntactic-sugar de map, flatMap y filter, con el objetivo de permitir escribir codigo de manera mas facil, intuitiva o entendible.

[1] http://www.scala-lang.org/docu/files/ScalaByExample.pdf – Capitulo 10.3

Categorías: Sin categoría

El dichoso guion bajo

septiembre 27, 2011 Deja un comentario

Una de las cosas que mas confunden al aprender Scala es el uso del guion bajo ‘_‘. Pues bien, en esta util presentacion tenemos resumidos todos los usos

http://www.slideshare.net/normation/scala-dreaded

¿Te los sabias todos?

Categorías: Sin categoría

Producto Cartesiano (I)

enero 25, 2011 Deja un comentario

Como generar un producto cartesiano de dos conjuntos en Scala? Imaginemos que son dos conjuntos de enteros consecutivos, que se pueden representar con la clase Range. Para generar dicho rangos podriamos usar el metodo apply del objeto Range, tal que

scala> val rango = Range(1, 6)
rango: scala.collection.immutable.Range with Range.ByOne = Range(1, 2, 3, 4, 5)

Como vemos, el rango es exclusivo en su extremo superior Range(1, 6) corresponde a 1, 2, 3, 4, 5. Otra forma mas vistosa de generar este rango es a traves del metodo to de RichInt.

scala> val rango = 1 to 5

rango: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(1, 2, 3, 4, 5)

a diferencia de usar Range(), este metodo es inclusivo; obtenemos el mismo rango usando 1 to 5. Si el metodo to esta definido en RichInt, como es que hemos podido invocarlo sobre 1, que es un Int? Al detectar el compilador la existencia (en el ambito de ejecuccion) de una conversion implicita entre Int y RichInt, este la aplica automaticamente, de manera que los detalles de la conversion son transparentes para el programador. Dicho de otra manera, el programador se beneficia de la implementacion de RichInt casi como si fuera parte de Int.

Una vez obtenidos dos rangos que representan los dos conjuntos solo nos queda obtener el Producto Cartesiano.

scala> val a = 1 to 6
a: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(1, 2, 3, 4, 5, 6)
scala> val b = 3 to 5
b: scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne = Range(3, 4, 5)
scala> for(i <- a; j <- b) yield (i, j)
res26: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (3,3), (3,4), (3,5), (4,3), (4,4), (4,5), (5,3), (5,4), (5,5), (6,3), (6,4), (6,5))

Esta for comprehension esta generando tuplas de dos elementos (que realmente son instancias de Tuple2) con la sintaxis (x, y). Este ejemplo de for comprehension equivaldria en Java a dos for loops anidados. El for comprehension es una estructura de control que generaliza el concepto de bucle o iteracion permitiendo expresiones de mucha potencia (asemejandose en su capacidad para seleccionar datos, sobre los que luego iterar, a las SELECT’s de SQL). Terminamos con una version en una linea del codigo anterior

scala> for(i <- 1 to 6; j <- 3 to 5) yield (i, j)
res27: scala.collection.immutable.IndexedSeq[(Int, Int)] = Vector((1,3), (1,4), (1,5), (2,3), (2,4), (2,5), (3,3), (3,4), (3,5), (4,3), (4,4), (4,5), (5,3), (5,4), (5,5), (6,3), (6,4), (6,5))
Categorías: Sin categoría

El equipo de Scala recibe beca del ERC

enero 11, 2011 Deja un comentario

El European Research Council ha concedido una beca de investigacion (research grant) de mas de 2.3 millones de euros a los investigadores del EPFL, responsables de Scala, con el objetivo de aportar soluciones al problema de la programacion paralela.

En años recientes los avances en capacidad de computacion han dejado de venir por el lado de CPU’s individuales mas rapidos para basarse en arquitecturas multi-nucleo, con el consiguiente incremento en la capacidad de computo agregada. Este cambio de tendencia requiere una adaptacion por parte de la industria de software para poder explotar esta nueva arquitectura a base de paralelismo.

La adaptacion a arquitecturas multi-nucleo es el reto mas grande al que se enfrenta la industria; la programacion paralelea es notoriamente dificil de hacer bien. Por ello van a ser necesarios avances sustanciales en distintos ambitos: tanto compiladores y lenguajes de programacion como tambien en la capacitacion de los programadores para extraer paralelismo especifico al dominio en cuestion.

Es sabido que los lenguajes funcionales tienen mucho que decir en este nuevo escenario. La  ausencia de efectos secundarios de estos lenguajes facilitan en gran medida la paralelizacion, al eliminarse todas las complicaciones derivadas de la necesidad de modificar el estado de manera concurrente. Por eso Scala, con su faceta funcional, es una buena basa para avanzar en este problema. Otra de las caracteristicas de Scala, la facilidad para construir lenguajes especificos al dominio (DSLs), sera una de las piezas de la linea de investigacion que seguira el EFPL, en colaboracion con investigadores de Stanford.

La evolucion y crecimiento de Scala parecen tener un futuro soleado.

Referencias
http://www.scala-lang.org/node/8579

 

Categorías: Sin categoría