Type classes en Scala

Después de unas cuantos meses con el blog ya hemos podido saber donde podemos usar traits, para qué sirven los implícitos o qué narices son eso de los tipos genéricos. Hoy vamos a utilizar un poco de todo para explicar las type classes.

Las type classes, a pesar del nombre, no provienen del paradigma orientado a objetos, si no de la programación funcional. Utilizando esta técnica vamos a poder agregar funcionalidad de forma ad-hoc a los tipos que deseemos. Tanto tipos primitivos del lenguaje como tipos que hayamos creado nosotros mismos.

¿Cómo se construyen type classes en Scala?

Para ilustrar la explicación vamos a usar un pequeño ejemplo.
En primer lugar, lo que tenemos que hacer es crear un trait con la funcionalidad que queremos añadir a los tipos. Como queremos que sea posible utilizarlo para varios tipos distintos, el trait estará parametrizado con el tipo genérico T.

trait Addable[T] {
  def +(a: T, b: T): T
}

Ahora que ya tenemos el trait definido, vamos a definir objetos implícitos que hereden de dicho trait. Tendremos que crear un objeto por cada tipo que queramos que tenga dicha funcionalidad. Además, en cada objeto tendremos que implementar como se deben sumar los elementos de ese tipo específico.

object Addables {
  implicit object AddableLikeInt extends Addable[Int] {
    def +(a: Int, b: Int): Int = a + b
  }

  implicit object AddableLikeString extends Addable[String] {
    def +(a: String, b: String): String = a + b
  }

  implicit object AddableLikeBoolean extends Addable[Boolean] {
    def +(a: Boolean, b: Boolean): Boolean = a && b
  }
}

Como podemos ver, en este caso estamos definiendo el método + para los tipos Int, String y Boolean. Ahora ya podemos sumar estos tipos.

Mmmm…¿y cómo se usan?

Ya tenemos las type classes definidas para tres tipos primitivos. Para darles uso vamos a utilizar un método plus que sume dos elementos del mismo tipo.

def plus[T](a1: T, a2: T)(implicit ev: Addable[T]): T = ev.+(a1, a2)

Para obligar a que el tipo de los elementos que queremos sumar sea Addable vamos a utilizar en la signatura del método un valor implícito de tipo Addable[T]. De esta forma, el método buscara en el scope si existe un objeto que extienda del tipo Addable[T] cuyo T sea el tipo de los valores que queremos sumar. A este valor implícito se le suele denominar evidencia.
Ahora vamos a probarlo:

import Addables._  

plus(true, false) //false

Como recordatorio, comentar que podemos hacer uso de syntactic sugar y usar los Context Bound para marcar las evidencias:

def plus[T:Addable](a1: T, a2: T)

¡Genial! Pero, ¿qué ocurre si queremos sumar algún tipo que no habíamos definido anteriormente? Pues que nos va a dar un error indicando que no se encuentra en el scope un implícito con el tipo definido:

plus(1.0, 2.0) // error: could not find implicit value for parameter ev: Addable[Double]

Solucionarlo es muy fácil. Basta con crear otro implicit object con el tipo deseado. En este caso con el tipo Double:

implicit object AddableLikeDouble extends Addable[Double] {
  def +(a: Double, b: Double): Double = a + b
}

plus(1.0, 2.0) //3.0

Ahora ya podemos sumar dos valores de tipo Double. Y lo mismo ocurre para tipos definidos por nosotros mismos:

case class Group(name: String, people: Set[String])

implicit object AddableLikeGroup extends Addable[Group] {
  def +(a: Group, b: Group): Group =
    Group(
      name = a.name + " & " + b.name,
      people = a.people | b.people
    )
}

Ahora ya puedo sumar Groups:

plus(Group("A", Set("Peter")), Group("B", Set("John, Chris"))) 
//Group("A & B", Set("Peter", "John", "Chris"))

Bonus

Como hemos podido ver, las type class nos permiten extender funcionalidad de una forma muy sencilla y elegante. Nos sirve tanto para trabajar con tipos primitivos, como con tipos propios. Además, controlando el scope de los objetos implícitos, tenemos el control sobre cuando queremos que un tipo específico extienda una determinada funcionalidad.

Esta técnica nos viene perfecto para meternos en profundidad con materia más compleja en futuros post. Lo que hemos visto hoy es el primer paso para entender los monoids, una estructura algebraica que podemos encontrar en librerías funcionales como Scalaz o Cats. Pero por el momento os dejo con la miel en los labios 🙂

Feliz verano!

Anuncios

2 thoughts on “Type classes en Scala

  1. […] Somos como las colecciones de soldaditos de plomo y las matrículas gratis en los gimnasios… ¡volvemos en Septiembre! Y volvemos con algo que parece muy heavy pero que seguro que después de leer unas pocas líneas será más sencillo. Hoy comenzamos con una serie de post en las que hablaremos de algunas abstracciones funcionales, muy relacionadas con el mundo de las matemáticas, con el objetivo de introducirnos poco a poco en el mundo de las librerías puramente funcionales de Scala, como pueden ser Scalaz y Cats. Para ello, vamos a apoyarnos en algunas características y técnicas que ya hemos tratado anteriormente, como por ejemplo, las type classes. […]

    Me gusta

  2. […] We are like the fascicles of tin soldier collections and the free enrollment at gyms … back in September! And with something that seems a little harsh but that surely after reading a few lines will be easier. Today we initiate a series of posts in which we’ll discuss some functional abstractions, that are very related to the world of mathematics, with the aim of being gradually introduced into the world of purely functional Scala libraries, such as as Scalaz or Cats. To do this, we will rely on some features and techniques that have already been discussed, such as type classes. […]

    Me gusta

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