Scalera tip: Uncle SAM

Today’s Scalera tip (and it’s been a while since last published tip) is about one of the Scala 2.12 version features that we fancy the most regarding the possibility of avoiding too much boilerplate.

Uncle SAM

Let’s suppose we’ve defined a type class that gathers certain amount of common implemented functionality like:

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

  
}

…in that case the remaining job would consist on defining the compare method.
For creating the different instances of Comparisson we could either create implicit vals or implicit objects (nothing new about type classes so far):

object Comparisson {

  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(_ - _)
      }
    }

}

Defining anonymous Comparisson instances (or extending from that trait in case of objects) was the only way of defining these instances so far.

With Scala’s 2.12 version a new concept known as SAM (Single Abstract Method) comes up. It basically allows defining an anonymous instance by providing a function equivalent to the only abstract method of the trait/abstract class.

When applying to the proposed example:

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(_ - _)

}

Cute, right? Just as a reminder, do have in mind that if we don’t explicitly type the expression, the compiler won’t understand that it has to do its magic and it will assume that what we’ve just implicitly defined is a mere function:

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

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

}

And we’re not telling this because it already happened to us…

Peace out!

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!

Curry, please…

One of the Scala’s rock starts that we cannot miss the chance to speak about is currying.

4252082-curry

What theory says

If we have a function (T,U) => V, currying it implies decomposing the function in a simpler one that allows building the result incrementally. In that case, we would get a function T => (U => V), what means that, from a T value we get a function whose only need is a U value for generating a V one. Messy? Let’s take a better look with next example.

Let’s suppose that we have a case class for modeling the student entity:

case class Student(
  name: String,
  age: Int,
  enrolled: Boolean)

We could have a method for instantiate a student: oh wait, like method apply, which has been automatically generated by creating the case class:

//Auto generated code below
object Student {

  def apply(
    name: String, 
    age: Int, 
    enrolled: Boolean): Student =
    new Student(name, age, enrolled)

}

By using such method, we can create a Student as shown below:

Student("john", 18, enrolled=true)

So easy so far. So let’s imagine next requirement:

In our student admission process, the candidate has to provide his/her personal data sequentially in a set of windows (At window A name must be provided. At window B so must the age. And finally, at window C, we would admit or not the candidate by setting the ‘enrolled’ attribute).

First approach: Classes are free!

We can define our windows as type aliases of transforming functions (so redundant…). I mean:

type WindowA = String => NotAStudientYet
type WindowB = (NotAStudentYet, Int) => AlmostAStudent
type WindowC = (AlmostAStudent, Boolean) => Student

case class NotAStudentYet(name: String)
case class AlmostAStudent(name: String, age: Int)

Take a look that windows are represented as functions.
So first window is a function that, given a name, it generates a «not-a-student-yet-like» object.
Second window takes as parameters a NotAStudientYet and the age of the subject, and it returns an «almost-a-student».
And the last one takes an «almost-a-student» and an admission or rejection parameter, which will finally allow generating a Student.

So for our purpose, with this first approach, we have created a couple of new classes that will be used as data-accumulators for, at the end, creating a Student.

The implementation should look like:

val windowA: WindowA = 
  (name) => 
    NotAStudentYet(name)

val windowB: WindowB = 
  (notStudent, age) => 
    AlmostStudent(notStudent.name, age)

val windowC: WindowC = 
  (almost, enrolled) => 
    Student(almost.name, almost.age, enrolled)

…sincerely, there’s no way to think that for doing such a thing, we have define additional classes. Let’s try another way.

Second approach: functions, functions everywhere …

Let’s have a try to defining functions that return another functions (higher order functions):

type WindowA = String => WindowB
type WindowB = Int => WindowC
type WindowC = Boolean => Student

val windowA: WindowA = 
  (name: String) => {
    val windowB: WindowB =
      (age: Int) => {
        val windowC: WindowC =
          (enrolled: Boolean) =>
            Student(name, age, enrolled)
        windowC
      }
    windowB
  }

By using a bunch of little functions, we’re setting values to all parameters that will build our Student. It’s pretty easier if we try to read the code from the most inside function to the most outside one (first windowC, then windowB and finally windowA). For invoking our function it’s enough with executing:

val student = windowA("john")(18)(true)

Third approach: U sure there’s nothing existing for this?

Of course it is. Inside the Function companion object in Scala, you can find curried method, which purpose is to separate a function that takes N parameters, in N concatenated functions, as we were previously discussing and with the last example as well.

For applying this wonder to the exposed example, it’s as easy as writing:

val f = (Sudent.apply _).curried
//f: String => (Int => (Boolean => Student))

f("john")(18)(true)
//Student("john", 18, true)

The reverse function called uncurried can also be found at the same Function companion object, so that N concatenated functions, for example, Int => (String => (Boolean => Double))) are converted to a single function that takes N different parameters: (Int, String, Boolean) => Double:

val myApply = Function.uncurried(f)
//myApply: (String, Int, Boolean) => Student

myApply("john",18,true)
//Student("john",18,true)

Easy peasy.
Peace out! 🙂

Curry, por favor…

Uno de los aportes de Scala de los que no podemos dejar pasar la ocasión de hablar acerca de ellos es el currying.

4252082-curry

La teoría

Si tenemos una función (T,U) => V, currificar la función supone descomponer la función en otra más sencilla que permite construir el resultado de manera incremental. En este caso pasaríamos a tener una función T => (U => V), es decir, a partir de un T obtenemos una función que solo necesita un U para generar un V. ¿Lioso? Veámoslo mejor con el siguiente ejemplo.

Supongamos que tenemos una case class que modela un estudiante:

case class Student(
  name: String,
  age: Int,
  enrolled: Boolean)

Podríamos tener adicionalmente un método que nos instanciara un estudiante como, por ejemplo, el método apply que se ha generado automáticamente para la case class:

//Auto generated code below
object Student {

  def apply(
    name: String, 
    age: Int, 
    enrolled: Boolean): Student =
    new Student(name, age, enrolled)

}

Utilizando dicho método, podemos construir un estudiante como sigue:

Student("john", 18, enrolled=true)

Hasta aquí fácil. Ahora supongamos la siguiente situación:

En nuestro proceso de admisión de alumnos, el candidato tiene que pasar por una serie de ventanillas para aportar su documentación poco a poco (en la ventanilla A indicaría el nombre, en la ventanilla B indicaría la edad; y en la ventanilla C le daríamos el visto bueno, o no, para formar parte de la escuela).

Primera aproximación: Hacer clases es gratis

Podemos definir nuestras ventanillas como alias de funciones transformadoras. Es decir:

type WindowA = String => NotAStudientYet
type WindowB = (NotAStudentYet, Int) => AlmostAStudent
type WindowC = (AlmostAStudent, Boolean) => Student

case class NotAStudentYet(name: String)
case class AlmostAStudent(name: String, age: Int)

Fijaros que, por una parte, las ventanillas se representan mediante funciones.
La primera ventanilla es una función que, a partir de un nombre, genera algo «que aún no es estudiante».
La segunda ventanilla, teniendo algo «que aún no es estudiante» y recibiendo una edad, devuelve algo que «casi es un estudiante».
Y la última ventanilla recibe algo «que casi es un estudiante» y una aprobación de admisión (aprobada o denegada) y genera un estudiante.

Para ello, en esta primera aproximación, hemos generado dos case classes nuevas, que van a servir de acumuladores, para finalmente crear un estudiante.

La implementación sería algo del estilo:

val windowA: WindowA = 
  (name) => 
    NotAStudentYet(name)

val windowB: WindowB = 
  (notStudent, age) => 
    AlmostStudent(notStudent.name, age)

val windowC: WindowC = 
  (almost, enrolled) => 
    Student(almost.name, almost.age, enrolled)

…sinceramente, no es posible que para hacer tal cosa tengamos que definirnos dos clases adicionales. Optemos por dar otro enfoque.

Segunda aproximación: Funciones, funciones everywhere …

Probemos a definir funciones que devuelvan otras funciones (funciones de orden superior):

type WindowA = String => WindowB
type WindowB = Int => WindowC
type WindowC = Boolean => Student

val windowA: WindowA = 
  (name: String) => {
    val windowB: WindowB =
      (age: Int) => {
        val windowC: WindowC =
          (enrolled: Boolean) =>
            Student(name, age, enrolled)
        windowC
      }
    windowB
  }

Fijaros que a partir de pequeñas funciones, vamos dando valores a los parámetros que construirán nuestro estudiante. Es más fácil si intentamos leerlo desde la función más interior a la mas exterior(primero windowC, después windowB y finalmente windowA). Para invocar nuestra función basta con ejecutar:

val student = windowA("john")(18)(true)

Tercera aproximación: ¿Seguro que no existe nada que haga esto?

Por supuesto que lo hay. Dentro del companion de Function en Scala, se encuentra el método curried, cuyo cometido es descomponer una función que recibe N argumentos en N funciones concatenadas, como veíamos al principio del post, y en el último ejemplo.

Para aplicar esta maravilla al ejemplo expuesto bastaría con escribir:

val f = (Sudent.apply _).curried
//f: String => (Int => (Boolean => Student))

f("john")(18)(true)
//Student("john", 18, true)

Existe además la función inversa uncurried, que dadas N funciones encadenadas, por ejemplo, Int => (String => (Boolean => Double))) devuelve una única función que recibe N argumentos: (Int, String, Boolean) => Double:

val myApply = Function.uncurried(f)
//myApply: (String, Int, Boolean) => Student

myApply("john",18,true)
//Student("john",18,true)

Fácil, sencillo y para toda la familia.
Agur de limón 🙂