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

monocle.Lens.scala Maven / Gradle / Ivy

package monocle

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

/**
 * A [[PLens]] can be seen as a pair of functions:
 *  get: S      => A  i.e. from an S, we can extract an A
 *  set: (B, S) => T  i.e. if we replace an A by a B in a S, we obtain a T
 *
 * A [[PLens]] could also be defined as a weaker [[PIso]] where set requires
 * an additional parameter than reverseGet.
 *
 * [[PLens]] stands for Polymorphic Lens as it set and modify methods change
 * a type A to B and S to T.
 * [[Lens]] is a type alias for [[PLens]] restricted to monomoprhic updates:
 *
 * type Lens[S, A] = PLens[S, S, A, A]
 *
 * A [[PLens]] is also a valid [[Getter]], [[Fold]], [[POptional]],
 * [[PTraversal]] and [[PSetter]]
 *
 * Typically a [[PLens]] or [[Lens]] can be defined between a [[Product]]
 * (e.g. case class, tuple, HList) and one of it is component.
 *
 * @see LensLaws in monocle-law module
 *
 * @tparam S the source of a [[PLens]]
 * @tparam T the modified source of a [[PLens]]
 * @tparam A the target of a [[PLens]]
 * @tparam B the modified target of a [[PLens]]
 *
 * @param get get the target of a [[PLens]]
 * @param set set polymorphically the target of a [[PLens]] with a value
 */
abstract class PLens[S, T, A, B] private[monocle](val get: S => A, val set: B => S => T){ self =>

  /** modify polymorphically the target of a [[PLens]] using [[Functor]] function */
  def modifyF[F[_]: Functor](f: A => F[B])(s: S): F[T]

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

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

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

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

  /** compose a [[PLens]] 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 [[PLens]] 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 [[PLens]] with an [[POptional]] */
  @inline final def composeOptional[C, D](other: POptional[A, B, C, D]): POptional[S, T, C, D] =
    asOptional composeOptional other

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

  /** compose a [[PLens]] with a [[PLens]] */
  @inline final def composeLens[C, D](other: PLens[A, B, C, D]): PLens[S, T, C, D] =
    new PLens[S, T, C, D](other.get compose self.get, c => modify(other.set(c))){
      def modifyF[F[_] : Functor](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 [[PLens]] with an [[PIso]] */
  @inline final def composeIso[C, D](other: PIso[A, B, C, D]): PLens[S, T, C, D] =
    composeLens(other.asLens)

  /********************************************/
  /** 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]): POptional[S, T, C, D] =
    composePrism(other)

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

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

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

  /** view a [[PLens]] as a [[Fold]] */
  @inline final def asFold: Fold[S, A] =
    new Fold[S, A] {
      def foldMap[M: Monoid](f: A => M)(s: S): M =
        f(get(s))
    }

  /** view a [[PLens]] as a [[Getter]] */
  @inline final def asGetter: Getter[S, A] =
    new Getter(get)

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

  /** view a [[PLens]] 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 [[PLens]] as an [[POptional]] */
  @inline final def asOptional: POptional[S, T, A, B] =
    POptional(\/.right compose get)(set)

}

object PLens {
  /**
   * create a [[PLens]] using a pair of functions: one to get the target, one to set the target.
   * @see macro module for methods generating [[PLens]] with less boiler plate
   */
  def apply[S, T, A, B](get: S => A)(set: B => S => T): PLens[S, T, A, B] =
    new PLens(get, set){
      def modifyF[F[_] : Functor](f: A => F[B])(s: S): F[T] =
        Functor[F].map(f(get(s)))(set(_)(s))

      def modify(f: A => B): S => T =
       s => set(f(get(s)))(s)
    }

}

object Lens {
  /** alias for [[PLens]] apply with a monomorphic set function */
  def apply[S, A](get: S => A)(set: A => S => S): Lens[S, A] =
    PLens(get)(set)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy