package com.zibaldone.cats
package ch_03

// foldRight in cats is stack-safe
import cats.{Eval, Foldable, Monoid}

object list:

  // ex. implement in terms of fold
  def map[A, B](list: List[A])(f: A => B): List[B]           = list.foldRight(Nil) { (a, b) => f(a) :: b }
  def flatMap[A, B](list: List[A])(f: A => List[B]): List[B] = list.foldLeft(Nil) { (b, a) => b ++ f(a) }
  def filter[A](list: List[A])(f: A => Boolean): List[A]     = list.foldRight(Nil) { (a, b) => if f(a) then a :: b else b }
  def combineAll[A: Monoid as monoid](list: List[A]): A      = list.foldLeft(monoid.empty)(monoid.combine)

def combineAll[F[_]: Foldable as ff, M[_]: Foldable as mf, A: Monoid](container: F[M[A]]): A =
  ff.compose[M](using mf).combineAll(container)

trait `foldable`[F[_]]:

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