Scalera tip: El tío SAM

En el tip de Scalera de hoy (y ha llovido ya un tanto desde el último que publicamos) hablaremos sobre una de las features de Scala 2.12 que nos ha parecido bastante interesante de cara a evitar escribir boilerplate

El tio SAM

Supongamos que hemos definido una type class que tiene bastante funcionalidad común del tipo:

trait Comparisson[T]{

  def compare(t1: T, t2: T): Int

  def greaterThan(t1: T, t2: T): Boolean =
    compare(t1, t2) > 0

  def greaterEqualThan(t1: T, t2: T): Boolean =
    compare(t1, t2) >= 0

  def equalTo(t1: T, t2: T): Boolean =
    compare(t1, t2) == 0

  def lowerThan(t1: T, t2: T): Boolean =
    compare(t1, t2) < 0

  def lowerEqualThan(t1: T, t2: T): Boolean =
    compare(t1, t2) <= 0

  
}

…en cuyo caso solo nos restaría definir el método compare.
Para crear las distintas instancias de Comparisson bien podemos crear implicit vals o implicit objects (hasta aquí nada nuevo sobre type classes en Scala):

object Comparission {

  implicit val intComparisson: Comparisson[Int] =
    new Comparisson[Int]{
      def compare(t1: Int, t2: Int): Int = t1 - t2
    }

  implicit val booleanComparisson: Comparisson[Boolean] =
    new Comparisson[Boolean] {
      def compare(t1: Boolean, t2: Boolean): Int = {
        List(t1, t2)
          .map(b => if (b) 1 else 0)
          .reduce(_ - _)
      }
    }

}

Definir instancias anónimas de Comparisson (o extender de dicho trait para el caso de objects) era la única forma de definir estas instancias hasta el momento.

Con la versión 2.12 de Scala surge el concepto de SAM (Single Abstract Method) que básicamente permite definir una instancia anónima aportando una función de orden superior equivalente al único método abstracto en el trait/abstract class.

Si aplicamos al caso anterior quedaría algo como:

object Comparisson {

  implicit val intComparisson: Comparisson[Int] =
    (t1: Int, t2: Int) => t1 - t2

  implicit val booleanComparisson: Comparisson[Boolean] =
    (t1: Boolean, t2: Boolean) =>
      List(t1, t2)
        .map(b => if (b) 1 else 0)
        .reduce(_ - _)

}

Cuqui, ¿no? Simplemente como recordatorio, tened en cuenta que si no anotamos el tipo de manera específica, el compilador no entiende que tiene que hacer su magia y asumira que lo que hemos definido de manera implícita es una función:

object Comparisson {
  
  implicit val intComparisson =
    (t1: Int, t2: Int) => t1 - t2

  //  The previous inferred type will be (Int, Int) => Int

}

Y no es que lo recordemos porque nos haya pasado a nosotros….

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

Google+ photo

Estás comentando usando tu cuenta de Google+. 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 )

w

Conectando a %s