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 🙂