All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.specs2.fp.Foldable.scala Maven / Gradle / Ivy

The newest version!
package org.specs2.fp


/**
 * Inspired from the scalaz (https://github.com/scalaz/scalaz) project
 */
trait Foldable[F[_]]  {

  /** Map each element of the structure to a Monoid, and combine the results. */
  def foldMap[A,B](fa: F[A])(f: A => B)(implicit F: Monoid[B]): B

  /** Right-associative fold of a structure. */
  def foldRight[A, B](fa: F[A], z: => B)(f: (A, => B) => B): B

  /**Left-associative fold of a structure. */
  def foldLeft[A, B](fa: F[A], z: B)(f: (B, A) => B): B

  /**Right-associative, monadic fold of a structure. */
  def foldRightM[G[_], A, B](fa: F[A], z: => B)(f: (A, => B) => G[B])(implicit M: Monad[G]): G[B] =
    foldLeft[A, B => G[B]](fa, M.point(_))((b, a) => w => M.bind(f(a, w))(b))(z)

  /**Left-associative, monadic fold of a structure. */
  def foldLeftM[G[_], A, B](fa: F[A], z: B)(f: (B, A) => G[B])(implicit M: Monad[G]): G[B] =
    foldRight[A, B => G[B]](fa, M.point(_))((a, b) => w => M.bind(f(w, a))(b))(z)

  /** Specialization of foldRightM when `B` has a `Monoid`. */
  def foldMapM[G[_], A, B](fa: F[A])(f: A => G[B])(implicit B: Monoid[B], G: Monad[G]): G[B] =
    foldRightM[G, A, B](fa, B.zero)((a, b2) => G.map(f(a))(b1 => B.append(b1, b2)))

  /** Combine the elements of a structure using a monoid. */
  def fold[M: Monoid](t: F[M]): M = foldMap[M, M](t)(x => x)

  /** Strict traversal in an applicative functor `M` that ignores the result of `f`. */
  def traverse_[M[_], A, B](fa: F[A])(f: A => M[B])(implicit a: Applicative[M]): M[Unit] =
    foldLeft(fa, a.pure(()))((x, y) => a.ap(f(y))(a.map(x)(_ => _ => ())))

  /** Strict sequencing in an applicative functor `M` that ignores the value in `fa`. */
  def sequence_[M[_], A](fa: F[M[A]])(implicit a: Applicative[M]): M[Unit] =
    traverse_(fa)(x => x)

  def findLeft[A](fa: F[A])(f: A => Boolean): Option[A] =
    foldLeft[A, Option[A]](fa, None)((b, a) => b.orElse(if(f(a)) Some(a) else None))

  def findRight[A](fa: F[A])(f: A => Boolean): Option[A] =
    foldRight[A, Option[A]](fa, None)((a, b) => b.orElse(if(f(a)) Some(a) else None))

  /** Alias for `length`. */
  final def count[A](fa: F[A]): Int = length(fa)

  /** Deforested alias for `toStream(fa).size`. */
  def length[A](fa: F[A]): Int = foldLeft(fa, 0)((b, _) => b + 1)

  /**
   * @return the element at index `i` in a `Some`, or `None` if the given index falls outside of the range
   */
  def index[A](fa: F[A], i: Int): Option[A] =
    foldLeft[A, (Int, Option[A])](fa, (0, None)) {
      case ((idx, elem), curr) =>
        (idx + 1, elem orElse { if (idx == i) Some(curr) else None })
    }._2

  /**
   * @return the element at index `i`, or `default` if the given index falls outside of the range
   */
  def indexOr[A](fa: F[A], default: => A, i: Int): A =
    index(fa, i) getOrElse default

  def toList[A](fa: F[A]): List[A] = foldLeft(fa, scala.List[A]())((t, h) => h :: t).reverse
  def toVector[A](fa: F[A]): Vector[A] = foldLeft(fa, Vector[A]())(_ :+ _)
  def toSet[A](fa: F[A]): Set[A] = foldLeft(fa, Set[A]())(_ + _)
  def toStream[A](fa: F[A]): Stream[A] = foldRight[A, Stream[A]](fa, Stream.empty)(Stream.cons(_, _))

  /** Whether all `A`s in `fa` yield true from `p`. */
  def all[A](fa: F[A])(p: A => Boolean): Boolean = foldRight(fa, true)(p(_) && _)

  /** `all` with monadic traversal. */
  def allM[G[_], A](fa: F[A])(p: A => G[Boolean])(implicit G: Monad[G]): G[Boolean] =
    foldRight(fa, G.point(true))((a, b) => G.bind(p(a))(q => if(q) b else G.point(false)))

  /** Whether any `A`s in `fa` yield true from `p`. */
  def any[A](fa: F[A])(p: A => Boolean): Boolean = foldRight(fa, false)(p(_) || _)

  /** `any` with monadic traversal. */
  def anyM[G[_], A](fa: F[A])(p: A => G[Boolean])(implicit G: Monad[G]): G[Boolean] =
    foldRight(fa, G.point(false))((a, b) => G.bind(p(a))(q => if(q) G.point(true) else b))

  def sumr[A](fa: F[A])(implicit A: Monoid[A]): A =
    foldRight(fa, A.zero)(A.append)

  def suml[A](fa: F[A])(implicit A: Monoid[A]): A =
    foldLeft(fa, A.zero)(A.append(_, _))

  /** Deforested alias for `toStream(fa).isEmpty`. */
  def empty[A](fa: F[A]): Boolean = all(fa)(_ => false)

  /** Insert an `A` between every A, yielding the sum. */
  def intercalate[A](fa: F[A], a: A)(implicit A: Monoid[A]): A =
    foldRight(fa, None: Option[A]) {(l, oa) =>
      Some(A.append(l, oa map (A.append(a, _)) getOrElse A.zero))
    }.getOrElse(A.zero)

}

object Foldable {
  @inline def apply[F[_]](implicit F: Foldable[F]): Foldable[F] = F

  implicit def listInstance: Foldable[List] = new Foldable[List] {
    def foldMap[A,B](fa: List[A])(f: A => B)(implicit F: Monoid[B]): B =
      fa.foldLeft(F.zero) { (res, cur) => F.append(res, f(cur)) }

    def foldRight[A, B](fa: List[A], z: => B)(f: (A, => B) => B): B =
      fa.foldRight(z) { (cur, res) => f(cur, res) }

    def foldLeft[A, B](fa: List[A], z: B)(f: (B, A) => B): B =
      fa.foldLeft(z)(f)
  }

  implicit def vectorInstance: Foldable[Vector] = new Foldable[Vector] {
    def foldMap[A,B](fa: Vector[A])(f: A => B)(implicit F: Monoid[B]): B =
      fa.foldLeft(F.zero) { (res, cur) => F.append(res, f(cur)) }

    def foldRight[A, B](fa: Vector[A], z: => B)(f: (A, => B) => B): B =
      fa.foldRight(z) { (cur, res) => f(cur, res) }

    def foldLeft[A, B](fa: Vector[A], z: B)(f: (B, A) => B): B =
      fa.foldLeft(z)(f)
  }

  implicit def streamInstance: Foldable[Stream] = new Foldable[Stream] {
    def foldMap[A,B](fa: Stream[A])(f: A => B)(implicit F: Monoid[B]): B =
      fa.foldLeft(F.zero) { (res, cur) => F.append(res, f(cur)) }

    def foldRight[A, B](fa: Stream[A], z: => B)(f: (A, => B) => B): B =
      fa.foldRight(z) { (cur, res) => f(cur, res) }

    def foldLeft[A, B](fa: Stream[A], z: B)(f: (B, A) => B): B =
      fa.foldLeft(z)(f)
  }
}

trait FoldableSyntax {

  implicit class FoldableOps[F[_] : Foldable, A](fa: F[A]) {
    val foldable = Foldable.apply[F]

    def toList: List[A] =
      foldable.toList(fa)

    def traverse_[M[_], B](f: A => M[B])(implicit a: Applicative[M]): M[Unit] =
      foldable.traverse_(fa)(f)

    def foldMap[B : Monoid](f: A => B): B =
      foldable.foldMap(fa)(f)

    def sumr(implicit m: Monoid[A]): A =
      foldable.sumr(fa)

    def suml(implicit m: Monoid[A]): A =
      foldable.suml(fa)

    def foldLeft[B](z: B)(f: (B, A) => B): B =
      foldable.foldLeft(fa, z)(f)

    def foldLeftM[M[_] : Monad, B](z: B)(f: (B, A) => M[B]): M[B] =
      foldable.foldLeftM(fa, z)(f)

    def foldRightM[M[_] : Monad, B](z: B)(f: (A, =>B) => M[B]): M[B] =
      foldable.foldRightM(fa, z)(f)
  }

  implicit class FoldableMonoidOps[F[_] : Foldable, A : Monoid](fa: F[A]) {
    def sumAll: A =
      Foldable.apply[F].foldMap(fa)(identity)
  }
}






© 2015 - 2024 Weber Informatics LLC | Privacy Policy