Lazy values

Just in case you lived in a hole for the last ten years and you didn’t know: Scala allows managing lazy values.

image

In Scala, we can define a value that won’t be evaluated until it is explicitly invoked. For example:

lazy val myLazyInt: Int = { println("hi"); 2 }

As you can see, using lazy notation, we’ve defined lazily an integer that stands for the literal 2 and also prints a ‘hi’ when it’s evaluated.
Apart from violating the biggest functional programming law (referential transparency) due to the insidious println, side effects, dead, destruction, blah blah …

anigif_enhanced-1822-1407333641-6

notice that, if we execute the code block, the previously mentioned ‘println’ is not executed. The block is not evaluated until any other expression makes use of our lazy integer value:

val result = myLazyInt + 3
//woa! somebody printed 'hi' and I have a brand new 5 inside 'result'

Once myLazyInt is evaluated, its value won’t be calculated again, no matter how many times it’s invoked. Therefore, the mysterious impression won’t salute us anymore:

lazy val myLazyInt: Int = { println("hi"); 2 }
myLazyInt
//"hi"
myLazyInt //nothing special happened now ...
myLazyInt //no matter how many times you invoke it...
myLazyInt //seriously, let it go...

Curious. The question that could come up is, if I define a lazy value and I pass it as a method parameter, what happens? Is it evaluated at the very same moment that the method is invoked? Maybe inside the method? That’ll depend on the way you define your method’s parameters.

Call by name vs. call by value

When defining a method, people usually define its parameter ‘by-value’, that means, that we expect the parameter to be already evaluated when it is passed to the method:

def myMethod(someInteger: Int): Int = {
  println("begin")
  val result = someInteger + 2
  println("end")
  result
}

If we invoke our method with any integer:

val n = 3
val result = myMethod(n)
//"begin"
//"end"
require(result == 5)

We just print both traces and it’s not big deal. Nothing new so far.
What happens if we now pass to the method our lazy value? In which exact moment will it print the salutation? Before or after the method traces?
Let’s try:

myMethod(myLazyInt)
//"hi"
//"begin"
//"end"

It printed it out before the method traces, which means that our lazy value was evaluated just before the method was invoked. Why does this happen? Because the way that Scala usually works needs the exact value of someInteger in order to be able to execute myMethod
It’s a pity if we want to keep myLazyInt lazy until the very last moment. How do we fix that? We’ll pass the argument ‘by-name’, that is, indicating the way the value has to be resolved instead of explicitly passing the value:

def myMethod(someInteger: => Int): Int = {
  println("begin")
  val result = someInteger + 2
  println("end")
  result
}

This way (someInteger: => Int) we indicate that our method requires as parameter an expression that, in the end, returns an integer and not an integer itself. If we now execute the method passing our non-yet evaluated lazy value:

myMethod(myLazyInt)
//"begin"
//"hi"
//"end"

Voilà! We made it. The ‘hi’ trace is not printed until the exact value of our lazy guy is required inside the method.

Some other ways to express laziness

Another way to express a lazy evaluation, which could be extremely useful, is the Function0 type:

trait Function0[+R]{
  def apply(): R
}

It’s just a function that requires zero parameters and return an only output type. It’s expressed as follows:

val f: () => Int =
  () => 2
f.apply() //2

And that’s pretty much everything…Once understood in rough outlines how laziness works in Scala, let’s move on to more interesting questions. A Lazy value, does it represent something stateful?
The answer (or more extra questions) will be available in the following post.

Peace out!

Anuncios

Valores perezosos

Por si hubieras estado en un agujero durante los últimos 10 años y no lo supieras, Scala permite gestionar valores de evaluación perezosa.

image

En Scala, podemos definir un valor que no será evaluado hasta que se le llame de manera explícita. Por ejemplo:

lazy val myLazyInt: Int = { println("hi"); 2 }

Como podéis ver, usando la notación lazy hemos definido de manera perezosa un entero que vale 2 y que imprime un ‘hola’ cuando se evalúa.
Aparte de haber violado la gran ley de la programación funcional (transparencia referencial) debido al infame println, side effects, muerte, destrucción, blah blah …

anigif_enhanced-1822-1407333641-6

fijaros que si ejecutamos el fragmento de código, dicho println no se ejecuta.
No es sino hasta que otra expresión hace uso de nuestro entero perezoso, que no se ejecuta el bloque:

val result = myLazyInt + 3
//woa! somebody printed 'hi' and I have a brand new 5 inside 'result'

Una vez calculado myLazyInt, su valor no volverá a calcularse independientemente de cuantas veces se invoque. Es decir, ya no volverá a aparecer una misteriosa impresión que nos saluda:

lazy val myLazyInt: Int = { println("hi"); 2 }
myLazyInt
//"hi"
myLazyInt //nothing special happened now ...
myLazyInt //no matter how many times you invoke it...
myLazyInt //seriously, let it go...

Curioso. La cuestión es, si yo defino un valor perezoso y lo paso a un método como argumento, ¿qué ocurre? ¿Se evalúa en el momento en que se invoca la función?¿Quizás dentro del cuerpo de la función? Eso dependerá de cómo definas los argumentos de tu método.

Call by name vs. call by value

Al definir un método, por lo general, definimos sus argumentos ‘by-value’, es decir, esperamos que el argumento ya se encuentre evaluado al pasarse al método:

def myMethod(someInteger: Int): Int = {
  println("begin")
  val result = someInteger + 2
  println("end")
  result
}

Si invocamos nuestro método con un número entero cualquiera:

val n = 3
val result = myMethod(n)
//"begin"
//"end"
require(result == 5)

Imprimimos nuestras dos trazas y ya está. Hasta aquí nada nuevo.
¿Qué ocurre ahora si le pasamos nuestro valor perezoso?¿En qué momento imprimirá “hi”?¿Antes o después de las trazas del método?
Probemos:

myMethod(myLazyInt)
//"hi"
//"begin"
//"end"

Lo imprimió antes, es decir, nuestro valor perezoso se evaluó antes de invocarse el método. ¿Esto por qué ocurre? Porque Scala, para poder ejecutar myMethod, necesita saber el valor de someInteger.
Es un fastidio si queremos mantener la evaluación de myLazyInt perezosa hasta el final. ¿Cómo lo solucionamos? Pasando el argumento ‘by-name’, es decir, indicando cómo se resolverá en el futuro el valor, pero sin pasar el valor de manera explícita:

def myMethod(someInteger: => Int): Int = {
  println("begin")
  val result = someInteger + 2
  println("end")
  result
}

De esta forma (someInteger: => Int) indicamos que le vamos a nuestro método como argumento una expresión que devolverá un entero (que no un entero). Si ahora ejecutamos el método pasándole nuestro valor perezoso no-evaluado:

myMethod(myLazyInt)
//"begin"
//"hi"
//"end"

Voilà! No es hasta el último momento en que se requiere el valor dentro del método, que no se evalúa nuestro entero perezoso.

Otras formas de expresar laziness

Otra forma que nos puede resultar muy útil para denotar que una expresión se evalúa de manera perezosa, es el tipo Function0:

trait Function0[+R]{
  def apply(): R
}

Se trata de una función que recibe 0 argumentos y devuelve un tipo de salida. Normalmente se suele notar como sigue:

val f: () => Int =
  () => 2
f.apply() //2

No hay mucho más misterio…Una vez comprendido a grandes rasgos el funcionamiento de la evaluación perezosa en Scala, pasemos a cuestiones más interesantes…¿Un Lazy es algo con estado?
La respuesta (o más preguntas) en el próximo post.

¡Agur de limón!