Translate

jueves, 7 de julio de 2022

Mónadas en Cats parte 16

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