Bounds: A View over your Context…

¿No te ha pasado alguna vez, cuando estás con los amigos, que sientes la necesidad de expresarte mejor al parametrizar tus métodos y clases?

I-know-that-feel (1)

Context bounds

Los context bounds permiten definir restricciones sobre la herencia de los parameter types.
Por ejemplo, supongamos que tenemos la clásica herencia:

trait Animal
trait Cat extends Animal
trait Dog extends Animal

Si queremos definir un método que reciba como argumento cualquier animal podríamos definir el método como sigue:

def myMethod(animal: Animal): Unit =
  println(animal)

Pero usando parameter types, también podríamos hacerlo como sigue:

def myMethod[T<:Animal](animal: T): Unit =
  println(animal)

Con la notación <: forzamos a que el tipo T sea un subtipo de Animal.
Ahora bien, ¿qué aporta entonces la segunda notación frente a la primera? abramos un poco la mente…

post-28553-Steve-Jobs-mind-blown-gif-HD-T-pVbd

¿Qué ocurre si en vez de recibir un Animal nuestro método, recibiera un Set[Animal]?

Pues que no es lo mismo un Set[Animal] que un Set[_<:Animal], por lo que tendríamos que definir nuestro método siguiendo alguna de las siguientes opciones:

def myMethod[T<:Animal](animals: Set[T]): Unit =
  animals.foreach(println)

def myMethod(animals: Set[_<:Animal]): Unit =
  animals.foreach(println)

También podemos definir restricciones para que el tipo recibido sea un supertype de Dog, como por ejemplo:

def myType[T>:Dog](animals:Set[T]): Unit =
  animals.foreach(println)
myType(Set(new Animal{})) //ok!

View bounds

Si no necesitamos definir restricciones de herencia, pero queremos que exista un modo de convertir el parameter type a otra cosa, entonces lo que estamos buscando son los view bounds. Su notación es <%.
Por ejemplo:

def myMethod[T<%Double](doubleLikes: Set[T]): Unit=
  douleLikes.foreach(d => println(d + 2.0))

Lo que signfica es que nuestro método está parametrizado por un tipo T y que, en el scope actual, debe haber una función de conversión de T a Double. No es sino syntactic sugar para:

def myMethod[T](doubleLikes: Set[T])(implicit ev: T => Double): Unit =
  douleLikes.foreach(d => println(ev(d) + 2.0))

De manera que podríamos invocar nuestro método como sigue:

myMethod(Set(1,2,3))//ok
myMethod(Set(true,false))//fail!
<console>:9: error: No implicit view available from Boolean => Double.
              myMethod(Set(true,false,true))
               ^

Si no existe una vista para un tipo concreto, como en este caso para Boolean, nos podemos definir una:

implicit def toD(b: Boolean):Double = 
  if (b) 1.0 else 2.0
myMethod(Set(true,false))//ok

Y nada más por hoy. Fácil y sencillo 🙂

¡Agur de limón!

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s