package com.zibaldone.cats
package ch_03
import cats.{Applicative, Monad}
import cats.syntax.flatMap.*
import cats.syntax.functor.*
import cats.syntax.applicative.*
import cats.syntax.apply.*
// ex. list traverse
def monadListTraverse[F[_]: Monad as monad, A, B](fa: List[A])(f: A => F[B]): F[List[B]] =
val pureF: F[List[B]] = List.empty[B].pure[F]
fa.foldLeft(pureF) { (listF, a) =>
for
list <- listF
fb <- f(a)
yield list :+ fb
}
// when F[_] == Vector[_] the lists are combinations because map is implemented in terms of flatMap
// behaves differently for cats.data.Validated
def applicativeListTraverse[F[_]: Applicative, A, B](fa: List[A])(f: A => F[B]): F[List[B]] =
val pureF: F[List[B]] = List.empty[B].pure[F]
fa.foldLeft(pureF) { (listF, a) => (listF, f(a)).mapN(_ :+ _) }
// ex. implement sequence
def listSequence[F[_]: Applicative as applicative, A](fa: List[F[A]]): F[List[A]] =
applicativeListTraverse(fa)(identity)
trait `traverse`[F[_]] extends `foldable`[F] with ch_01.`functor`[F]:
def traverse[M[_]: Applicative, A, B](fa: F[A])(f: A => M[B]): M[F[B]]
def sequence[M[_]: Applicative, A](fa: F[M[A]]): M[F[A]] = traverse(fa)(identity)
// ex. implement map
type Identity[A] = A // <-- BiMonad
def map[A, B](fa: F[A])(f: A => B): F[B] = traverse[Identity, A, B](fa)(f(_))