Bounds: A View over your Context…*

Has it ever happened to you that you’re with your colleagues and you feel the need of expressing better parameterizing your methods and classes?

I-know-that-feel (1)

Context bounds

Context bounds allow defining constraints about parameter types inheritance.
For example, let’s suppose the classical inheritance:

trait Animal
trait Cat extends Animal
trait Dog extends Animal

If we want to define a method with an any-kind-of-animal argument, we could define that method as follows:

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

But, using parameter types, we could also do it this way:

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

With <: notation, we force the type T to be a subtype of Animal.
So now, what’s the benefit of using the second notation instead of using the first one? Let’s free our minds a little bit…

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

What happens if our method receives a Set[Animal] instead of an Animal?

Well, we have to admit, due to invariance, that a Set[Animal] is not the same as a Set[_<:Animal], and we would have to declare our method using one of the following options:

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

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

We can also define constraints so that the expressed type is a supertype of Dog, for example:

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

View bounds

If we don’t need to define inheritance constraints, but we want a way to convert the parameter type to something else, then what we’re looking for are view bounds. Their notation is <%.
For example:

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

It means that our method is parameterized by a type T and, in current scope, there must exist a conversion function from T to Double. It’s just syntactic sugar for:

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

This way, we could invoke our method like this:

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))
               ^

If there is no view for a concrete type, like in this case for Boolean, we can define one:

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

And that’s all folks! Easy peasy lemon squeezy 🙂

Peace out.

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