Una propiedad útil de Eval es que sus métodos map y flatMap son trampolines. Esto significa que podemos anidar llamadas a map y flatMap arbitrariamente sin consumir llamadas de la pila. Llamamos a esta propiedad "seguridad de pila". Por ejemplo, considere esta función para calcular factoriales:
def factorial(n: BigInt): BigInt = if(n == 1) n else n * factorial(n - 1)
Es relativamente fácil que la pila desborde:
factorial(50000)
// java.lang.StackOverflowError
//
...
Podemos reescribir el método usando Eval para hacerlo seguro:
def factorial(n: BigInt): Eval[BigInt] = if(n == 1) {
Eval.now(n)
} else {
factorial(n - 1).map(_ * n)
}
factorial(50000).value
// java.lang.StackOverflowError
//
...
¡Ups! Eso no funcionó, ¡nuestra pila aún explotó! Esto se debe a que aún estamos realizando todas las llamadas recursivas a factorial antes de comenzar a trabajar con el método de mapa de Eval. Podemos solucionar esto usando Eval.defer, que toma una instancia existente de Eval y difiere su evaluación. El método diferido es un trampolín como map y flatMap, por lo que podemos usarlo como una forma rápida de hacer que una pila de operaciones existente sea segura:
def factorial(n: BigInt): Eval[BigInt] = if(n == 1) {
Eval.now(n)
} else {
Eval.defer(factorial(n - 1).map(_ * n))
}
factorial(50000).value
// res: A very big value