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

monocle.Fold.scala Maven / Gradle / Ivy

The newest version!
package monocle

import cats.{Eq, Foldable, Monoid}
import cats.arrow.Choice
import cats.instances.int._
import cats.instances.list._
import cats.syntax.either._
import monocle.function.{At, Each, FilterIndex, Index}
import monocle.internal.Monoids

/** A [[Fold]] can be seen as a [[Getter]] with many targets or
  * a weaker [[PTraversal]] which cannot modify its target.
  *
  * [[Fold]] is on the top of the Optic hierarchy which means that
  * [[Getter]], [[PTraversal]], [[POptional]], [[PLens]], [[PPrism]]
  * and [[PIso]] are valid [[Fold]]
  *
  * @tparam S the source of a [[Fold]]
  * @tparam A the target of a [[Fold]]
  */
abstract class Fold[S, A] extends Serializable { self =>

  /** map each target to a Monoid and combine the results
    * underlying representation of [[Fold]], all [[Fold]] methods are defined in terms of foldMap
    */
  def foldMap[M: Monoid](f: A => M)(s: S): M

  /** combine all targets using a target's Monoid */
  final def fold(s: S)(implicit ev: Monoid[A]): A =
    foldMap(identity)(s)

  /** get all the targets of a [[Fold]] */
  final def getAll(s: S): List[A] =
    foldMap(List(_))(s)

  /** find the first target matching the predicate */
  final def find(p: A => Boolean): S => Option[A] =
    foldMap(a => Some(a).filter(p))(_)(Monoids.firstOption)

  /** get the first target */
  final def headOption(s: S): Option[A] =
    foldMap(Option(_))(s)(Monoids.firstOption)

  /** get the last target */
  final def lastOption(s: S): Option[A] =
    foldMap(Option(_))(s)(Monoids.lastOption)

  /** check if at least one target satisfies the predicate */
  final def exist(p: A => Boolean): S => Boolean =
    foldMap(p(_))(_)(Monoids.any)

  /** check if all targets satisfy the predicate */
  final def all(p: A => Boolean): S => Boolean =
    foldMap(p(_))(_)(Monoids.all)

  /** calculate the number of targets */
  final def length(s: S): Int =
    foldMap(_ => 1)(s)

  /** check if there is no target */
  final def isEmpty(s: S): Boolean =
    foldMap(_ => false)(s)(Monoids.all)

  /** check if there is at least one target */
  final def nonEmpty(s: S): Boolean =
    !isEmpty(s)

  /** join two [[Fold]] with the same target */
  final def choice[S1](other: Fold[S1, A]): Fold[Either[S, S1], A] =
    new Fold[Either[S, S1], A] {
      def foldMap[M: Monoid](f: A => M)(s: Either[S, S1]): M =
        s.fold(self.foldMap(f), other.foldMap(f))
    }

  final def left[C]: Fold[Either[S, C], Either[A, C]] =
    new Fold[Either[S, C], Either[A, C]] {
      override def foldMap[M: Monoid](f: Either[A, C] => M)(s: Either[S, C]): M =
        s.fold(self.foldMap(a => f(Either.left(a))), c => f(Either.right(c)))
    }

  final def right[C]: Fold[Either[C, S], Either[C, A]] =
    new Fold[Either[C, S], Either[C, A]] {
      override def foldMap[M: Monoid](f: Either[C, A] => M)(s: Either[C, S]): M =
        s.fold(c => f(Either.left(c)), self.foldMap(a => f(Either.right(a))))
    }

  /** Compose with a function lifted into a Getter */
  def to[C](f: A => C): Fold[S, C] =
    andThen(Getter(f))

  def each[C](implicit evEach: Each[A, C]): Fold[S, C] =
    composeTraversal(evEach.each)

  /** Select all the elements which satisfies the predicate.
    * This combinator can break the fusion property see Optional.filter for more details.
    */
  def filter(predicate: A => Boolean): Fold[S, A] =
    self.andThen(Optional.filter(predicate))

  def filterIndex[I, A1](predicate: I => Boolean)(implicit ev: FilterIndex[A, I, A1]): Fold[S, A1] =
    self.andThen(ev.filterIndex(predicate))

  def some[A1](implicit ev1: A =:= Option[A1]): Fold[S, A1] =
    adapt[Option[A1]] composePrism (std.option.pSome)

  def withDefault[A1: Eq](defaultValue: A1)(implicit ev1: A =:= Option[A1]): Fold[S, A1] =
    adapt[Option[A1]] composeIso (std.option.withDefault(defaultValue))

  private def adapt[A1](implicit evA: A =:= A1): Fold[S, A1] =
    evA.substituteCo[Fold[S, *]](this)

  def at[I, A1](i: I)(implicit evAt: At[A, i.type, A1]): Fold[S, A1] =
    andThen(evAt.at(i))

  def index[I, A1](i: I)(implicit evIndex: Index[A, I, A1]): Fold[S, A1] =
    composeOptional(evIndex.index(i))

  /** compose a [[Fold]] with another [[Fold]] */
  final def andThen[B](other: Fold[A, B]): Fold[S, B] =
    new Fold[S, B] {
      def foldMap[M: Monoid](f: B => M)(s: S): M =
        self.foldMap(other.foldMap(f)(_))(s)
    }

  /** compose a [[Fold]] with a [[Getter]] */
  final def andThen[C](other: Getter[A, C]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PTraversal]] */
  final def andThen[B, C, D](other: PTraversal[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[POptional]] */
  final def andThen[B, C, D](other: POptional[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PPrism]] */
  final def andThen[B, C, D](other: PPrism[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PLens]] */
  final def andThen[B, C, D](other: PLens[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PIso]] */
  final def andThen[B, C, D](other: PIso[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[Fold]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composeFold[B](other: Fold[A, B]): Fold[S, B] =
    andThen(other)

  /** compose a [[Fold]] with a [[Getter]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composeGetter[C](other: Getter[A, C]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PTraversal]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composeTraversal[B, C, D](other: PTraversal[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[POptional]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composeOptional[B, C, D](other: POptional[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PPrism]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composePrism[B, C, D](other: PPrism[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PLens]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composeLens[B, C, D](other: PLens[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** compose a [[Fold]] with a [[PIso]] */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def composeIso[B, C, D](other: PIso[A, B, C, D]): Fold[S, C] =
    andThen(other.asFold)

  /** *****************************************
    */
  /** Experimental aliases of compose methods */
  /** *****************************************
    */
  /** alias to composeTraversal */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def ^|->>[B, C, D](other: PTraversal[A, B, C, D]): Fold[S, C] =
    andThen(other)

  /** alias to composeOptional */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def ^|-?[B, C, D](other: POptional[A, B, C, D]): Fold[S, C] =
    andThen(other)

  /** alias to composePrism */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def ^<-?[B, C, D](other: PPrism[A, B, C, D]): Fold[S, C] =
    andThen(other)

  /** alias to composeLens */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def ^|->[B, C, D](other: PLens[A, B, C, D]): Fold[S, C] =
    andThen(other)

  /** alias to composeIso */
  @deprecated("use andThen", since = "3.0.0-M1")
  final def ^<->[B, C, D](other: PIso[A, B, C, D]): Fold[S, C] =
    andThen(other)
}

object Fold extends FoldInstances {
  def id[A]: Fold[A, A] =
    Iso.id[A].asFold

  def codiagonal[A]: Fold[Either[A, A], A] =
    new Fold[Either[A, A], A] {
      def foldMap[M: Monoid](f: A => M)(s: Either[A, A]): M =
        s.fold(f, f)
    }

  def select[A](p: A => Boolean): Fold[A, A] =
    new Fold[A, A] {
      def foldMap[M: Monoid](f: A => M)(s: A): M =
        if (p(s)) f(s) else Monoid[M].empty
    }

  /** [[Fold]] that points to nothing */
  def void[S, A]: Fold[S, A] =
    Optional.void.asFold

  /** create a [[Fold]] from a Foldable */
  def fromFoldable[F[_]: Foldable, A]: Fold[F[A], A] =
    new Fold[F[A], A] {
      def foldMap[M: Monoid](f: A => M)(s: F[A]): M =
        Foldable[F].foldMap(s)(f)
    }
}

sealed abstract class FoldInstances {
  implicit val foldChoice: Choice[Fold] = new Choice[Fold] {
    def choice[A, B, C](f: Fold[A, C], g: Fold[B, C]): Fold[Either[A, B], C] =
      f choice g

    def id[A]: Fold[A, A] =
      Fold.id[A]

    def compose[A, B, C](f: Fold[B, C], g: Fold[A, B]): Fold[A, C] =
      g composeFold f
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy