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

monocle.Prism.scala Maven / Gradle / Ivy

package monocle

import scalaz.{Applicative, Maybe, Monoid, \/}


/**
 * A [[PPrism]] can be seen as a pair of functions:
 *  getOrModify: S => T \/ A
 *  reverseGet : B => T
 *
 * A [[PPrism]] could also be defined as a weaker [[PIso]] where get can fail.
 *
 * Typically a [[PPrism]] or [[Prism]] encodes the relation between a Sum or
 * CoProduct type (e.g. sealed trait) and one of it is element.
 *
 * [[PPrism]] stands for Polymorphic Prism as it set and modify methods change
 * a type A to B and S to T.
 * [[Prism]] is a type alias for [[PPrism]] where the type of target cannot be modified:
 *
 * type Prism[S, A] = PPrism[S, S, A, A]
 *
 * A [[PPrism]] is also a valid  [[Fold]], [[POptional]], [[PTraversal]] and [[PSetter]]
 *
 * @see PrismLaws in monocle-law module
 *
 * @tparam S the source of a [[PPrism]]
 * @tparam T the modified source of a [[PPrism]]
 * @tparam A the target of a [[PPrism]]
 * @tparam B the modified target of a [[PPrism]]
 *
 * @param getOrModify get the target of a [[PPrism]] or modify the source in case there is no target
 * @param reverseGet get the modified source of a [[PIso]]
 */
abstract class PPrism[S, T, A, B] private[monocle](val getOrModify: S => T \/ A, val reverseGet: B => T){ self =>

  /** get the target of a [[PPrism]] or nothing if there is no target */
  def getMaybe(s: S): Maybe[A]

  /** modify polymorphically the target of a [[PPrism]] with an [[Applicative]] function */
  def modifyF[F[_]: Applicative](f: A => F[B])(s: S): F[T]

  /** modify polymorphically the target of a [[PPrism]] with a function */
  def modify(f: A => B): S => T

  /**
   * modify polymorphically the target of a [[PPrism]] with a function.
   * return empty if the [[PPrism]] is not matching
   */
  @inline final def modifyMaybe(f: A => B): S => Maybe[T] =
    s => getMaybe(s).map(_ => modify(f)(s))

  /** set polymorphically the target of a [[PPrism]] with a value */
  @inline final def set(b: B): S => T =
    modify(_ => b)

  /**
   * set polymorphically the target of a [[PPrism]] with a value.
   * return empty if the [[PPrism]] is not matching
   */
  @inline final def setMaybe(b: B): S => Maybe[T] =
    modifyMaybe(_ => b)

  /** check if a [[PPrism]] has a target */
  @inline final def isMatching(s: S): Boolean =
    getMaybe(s).isJust

  /** create a [[Getter]] from the modified target to the modified source of a [[PPrism]] */
  @inline final def re: Getter[B, T] =
    Getter(reverseGet)

  /************************************************************/
  /** Compose methods between a [[PPrism]] and another Optics */
  /************************************************************/

  /** compose a [[PPrism]] with a [[Fold]] */
  @inline final def composeFold[C](other: Fold[A, C]): Fold[S, C] =
    asFold composeFold other

  /** compose a [[PPrism]] with a [[Getter]] */
  @inline final def composeGetter[C](other: Getter[A, C]): Fold[S, C] =
    asFold composeGetter other

  /** compose a [[PPrism]] with a [[PSetter]] */
  @inline final def composeSetter[C, D](other: PSetter[A, B, C, D]): PSetter[S, T, C, D] =
    asSetter composeSetter other

  /** compose a [[PPrism]] with a [[PTraversal]] */
  @inline final def composeTraversal[C, D](other: PTraversal[A, B, C, D]): PTraversal[S, T, C, D] =
    asTraversal composeTraversal other

  /** compose a [[PPrism]] with a [[POptional]] */
  @inline final def composeOptional[C, D](other: POptional[A, B, C, D]): POptional[S, T, C, D] =
    asOptional composeOptional other

  /** compose a [[PPrism]] with a [[PLens]] */
  @inline final def composeLens[C, D](other: PLens[A, B, C, D]): POptional[S, T, C, D] =
    asOptional composeOptional other.asOptional

  /** compose a [[PPrism]] with a [[PPrism]] */
  @inline final def composePrism[C, D](other: PPrism[A, B, C, D]): PPrism[S, T, C, D] =
    new PPrism[S, T, C, D](
      s => getOrModify(s).flatMap(a => other.getOrModify(a).bimap(set(_)(s), identity)),
      reverseGet compose other.reverseGet
    ){
      def getMaybe(s: S): Maybe[C] =
        self.getMaybe(s) flatMap other.getMaybe

      def modifyF[F[_] : Applicative](f: C => F[D])(s: S): F[T] =
        self.modifyF(other.modifyF(f))(s)

      def modify(f: C => D): S => T =
        self.modify(other.modify(f))
    }

  /** compose a [[PPrism]] with a [[PIso]] */
  @inline final def composeIso[C, D](other: PIso[A, B, C, D]): PPrism[S, T, C, D] =
    composePrism(other.asPrism)

  /********************************************/
  /** Experimental aliases of compose methods */
  /********************************************/

  /** alias to composeTraversal */
  @inline final def ^|->>[C, D](other: PTraversal[A, B, C, D]): PTraversal[S, T, C, D] =
    composeTraversal(other)

  /** alias to composeOptional */
  @inline final def ^|-?[C, D](other: POptional[A, B, C, D]): POptional[S, T, C, D] =
    composeOptional(other)

  /** alias to composePrism */
  @inline final def ^<-?[C, D](other: PPrism[A, B, C, D]): PPrism[S, T, C, D] =
    composePrism(other)

  /** alias to composeLens */
  @inline final def ^|->[C, D](other: PLens[A, B, C, D]): POptional[S, T, C, D] =
    composeLens(other)

  /** alias to composeIso */
  @inline final def ^<->[C, D](other: PIso[A, B, C, D]): PPrism[S, T, C, D] =
    composeIso(other)

  /******************************************************************/
  /** Transformation methods to view a [[PPrism]] as another Optics */
  /******************************************************************/

  /** view a [[PPrism]] as a [[Fold]] */
  @inline final def asFold: Fold[S, A] = new Fold[S, A]{
    def foldMap[M: Monoid](f: A => M)(s: S): M =
      getMaybe(s) map f getOrElse Monoid[M].zero
  }

  /** view a [[PPrism]] as a [[Setter]] */
  @inline final def asSetter: PSetter[S, T, A, B] =
    new PSetter(modify)

  /** view a [[PPrism]] as a [[PTraversal]] */
  @inline final def asTraversal: PTraversal[S, T, A, B] =
    new PTraversal[S, T, A, B] {
      def modifyF[F[_]: Applicative](f: A => F[B])(s: S): F[T] =
        self.modifyF(f)(s)
    }

  /** view a [[PPrism]] as a [[POptional]] */
  @inline final def asOptional: POptional[S, T, A, B] =
    POptional(getOrModify)(set)
}

object PPrism {
  /** create a [[PPrism]] using the canonical functions: getOrModify and reverseGet */
  def apply[S, T, A, B](getOrModify: S => T \/ A)(reverseGet: B => T): PPrism[S, T, A, B] =
    new PPrism(getOrModify, reverseGet){
      def getMaybe(s: S): Maybe[A] =
        getOrModify(s).toMaybe

      def modifyF[F[_] : Applicative](f: (A) => F[B])(s: S): F[T] =
        getOrModify(s).fold(
          t => Applicative[F].point(t),
          a => Applicative[F].map(f(a))(reverseGet)
        )

      def modify(f: (A) => B): S => T =
        getOrModify(_).fold(identity, reverseGet compose f)
    }
}

object Prism {
  /** alias for [[PPrism]] apply restricted to monomorphic update */
  def apply[S, A](_getMaybe: S => Maybe[A])(_reverseGet: A => S): Prism[S, A] =
    new PPrism[S, S, A, A](s => _getMaybe(s) \/> s, _reverseGet){
      def getMaybe(s: S): Maybe[A] =
        _getMaybe(s)

      def modifyF[F[_]: Applicative](f: A => F[A])(s: S): F[S] =
        _getMaybe(s).cata(
          a => Applicative[F].map(f(a))(_reverseGet),
          Applicative[F].point(s)
        )

      def modify(f: A => A): S => S =
        s => _getMaybe(s).cata(_reverseGet compose f, s)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy