Translate

martes, 27 de diciembre de 2022

Foldable y Traverse parte 3

 Foldable define foldRight de manera diferente a foldLeft, en términos de la mónada Eval:


def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]


El uso de Eval significa que el plegado siempre es seguro para la pila, incluso cuando la definición predeterminada de foldRight de la colección no lo es. Por ejemplo, la implementación predeterminada de foldRight para LazyList no es segura a nivel de pila. Cuanto más larga sea la lista perezosa, mayores serán los requisitos de pila para el pliegue. Una lista perezosa lo suficientemente grande provocará un StackOverflowError:


import cats.Eval

import cats.Foldable

def bigData = (1 to 100000).to(LazyList)

bigData.foldRight(0L)(_ + _)

// java.lang.StackOverflowError ...


El uso de Foldable nos obliga a usar operaciones seguras de pila, lo que corrige la excepción de desbordamiento:


import cats.instances.lazyList._ // for Foldable

val eval: Eval[Long] =

    Foldable[LazyList].

        foldRight(bigData, Eval.now(0L)) { (num, eval) =>

            eval.map(_ + num)

        }

eval.value

// res3: Long = 5000050000L


Foldable nos proporciona una gran cantidad de métodos útiles definidos en la parte superior de foldLeft. Muchos de estos son facsímiles de métodos familiares de la biblioteca estándar: find, exist, forall, toList, isEmpty, nonEmpty, etc.

Además de estos métodos familiares, Cats proporciona dos métodos que hacen uso de Monoids:

  • combineAll (y su alias fold) combina todos los elementos en la secuencia usando su Monoid;
  • foldMap mapea una función proporcionada por el usuario sobre la secuencia y combina los resultados usando un Monoid.

Por ejemplo, podemos usar combineAll para sumar sobre List[Int]:


import cats.instances.int._ // for Monoid

Foldable[List].combineAll(List(1, 2, 3))

// res8: Int = 6


Alternativamente, podemos usar foldMap para convertir cada Int en un String y concatenarlos:


import cats.instances.string._ // for Monoid

Foldable[List].foldMap(List(1, 2, 3))(_.toString)

// res9: String = "123"


Finalmente, podemos componer Foldables para admitir un recorrido profundo de secuencias anidadas:


import cats.instances.vector._ // for Monoid

val ints = List(Vector(1, 2, 3), Vector(4, 5, 6))

(Foldable[List] compose Foldable[Vector]).combineAll(ints)

// res11: Int = 21