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! 🙂

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