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

monocle.Lens.scala Maven / Gradle / Ivy

The newest version!
package monocle

import cats.{Applicative, Eq, Functor, Monoid}
import cats.arrow.Choice
import cats.syntax.either._
import monocle.function.{At, Each, FilterIndex, Index}

/** 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 an `S`, we obtain a `T`
  *
  * A [[PLens]] could also be defined as a weaker [[PIso]] where replace requires
  * an additional parameter than reverseGet.
  *
  * [[PLens]] stands for Polymorphic Lens as it replace and modify methods change
  * a type `A` to `B` and `S` to `T`.
  * [[Lens]] is a type alias for [[PLens]] restricted to monomorphic 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 its component.
  *
  * @see [[monocle.law.LensLaws]]
  *
  * @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]]
  */
abstract class PLens[S, T, A, B] extends Serializable { self =>

  /** get the target of a [[PLens]] */
  def get(s: S): A

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

  /** alias to replace */
  @deprecated("use replace instead", since = "3.0.0-M1")
  def set(b: B): S => T = replace(b)

  /** 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

  /** find if the target satisfies the predicate */
  final def find(p: A => Boolean): S => Option[A] =
    s => Some(get(s)).filter(p)

  /** check if the target satisfies the predicate */
  final def exist(p: A => Boolean): S => Boolean =
    p compose get

  /** join two [[PLens]] with the same target */
  final def choice[S1, T1](other: PLens[S1, T1, A, B]): PLens[Either[S, S1], Either[T, T1], A, B] =
    PLens[Either[S, S1], Either[T, T1], A, B](_.fold(self.get, other.get))(b =>
      _.bimap(self.replace(b), other.replace(b))
    )

  /** pair two disjoint [[PLens]] */
  final def split[S1, T1, A1, B1](other: PLens[S1, T1, A1, B1]): PLens[(S, S1), (T, T1), (A, A1), (B, B1)] =
    PLens[(S, S1), (T, T1), (A, A1), (B, B1)] { case (s, s1) =>
      (self.get(s), other.get(s1))
    } {
      case (b, b1) => { case (s, s1) =>
        (self.replace(b)(s), other.replace(b1)(s1))
      }
    }

  final def first[C]: PLens[(S, C), (T, C), (A, C), (B, C)] =
    PLens[(S, C), (T, C), (A, C), (B, C)] { case (s, c) =>
      (get(s), c)
    } {
      case (b, c) => { case (s, _) =>
        (replace(b)(s), c)
      }
    }

  final def second[C]: PLens[(C, S), (C, T), (C, A), (C, B)] =
    PLens[(C, S), (C, T), (C, A), (C, B)] { case (c, s) =>
      (c, get(s))
    } {
      case (c, b) => { case (_, s) =>
        (c, replace(b)(s))
      }
    }

  def some[A1, B1](implicit ev1: A =:= Option[A1], ev2: B =:= Option[B1]): POptional[S, T, A1, B1] =
    adapt[Option[A1], Option[B1]] composePrism (std.option.pSome)

  private[monocle] def adapt[A1, B1](implicit evA: A =:= A1, evB: B =:= B1): PLens[S, T, A1, B1] =
    evB.substituteCo[PLens[S, T, A1, *]](evA.substituteCo[PLens[S, T, *, B]](this))

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

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

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

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

  /** compose a [[PLens]] with an [[POptional]] */
  final def andThen[C, D](other: POptional[A, B, C, D]): POptional[S, T, C, D] =
    asOptional.andThen(other)

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

  /** compose a [[PLens]] with a [[PLens]] */
  final def andThen[C, D](other: PLens[A, B, C, D]): PLens[S, T, C, D] =
    new PLens[S, T, C, D] {
      def get(s: S): C =
        other.get(self.get(s))

      def replace(d: D): S => T =
        self.modify(other.replace(d))

      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]] */
  final def andThen[C, D](other: PIso[A, B, C, D]): PLens[S, T, C, D] =
    andThen(other.asLens)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  /** *********************************************************************************************
    */
  /** Transformation methods to view a [[PLens]] as another Optics */
  /** *********************************************************************************************
    */
  /** view a [[PLens]] as a [[Fold]] */
  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]] */
  final def asGetter: Getter[S, A] =
    (s: S) => self.get(s)

  /** view a [[PLens]] as a [[PSetter]] */
  final def asSetter: PSetter[S, T, A, B] =
    new PSetter[S, T, A, B] {
      def modify(f: A => B): S => T =
        self.modify(f)

      def replace(b: B): S => T =
        self.replace(b)
    }

  /** view a [[PLens]] as a [[PTraversal]] */
  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]] */
  final def asOptional: POptional[S, T, A, B] =
    new POptional[S, T, A, B] {
      def getOrModify(s: S): Either[T, A] =
        Either.right(get(s))

      def replace(b: B): S => T =
        self.replace(b)

      def getOption(s: S): Option[A] =
        Some(self.get(s))

      def modify(f: A => B): S => T =
        self.modify(f)

      def modifyF[F[_]: Applicative](f: A => F[B])(s: S): F[T] =
        self.modifyF(f)(s)
    }
}

object PLens extends LensInstances {
  def id[S, T]: PLens[S, T, S, T] =
    PIso.id[S, T].asLens

  def codiagonal[S, T]: PLens[Either[S, S], Either[T, T], S, T] =
    PLens[Either[S, S], Either[T, T], S, T](
      _.fold(identity, identity)
    )(t => _.bimap(_ => t, _ => t))

  /** create a [[PLens]] using a pair of functions: one to get the target, one to replace 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[S, T, A, B] {
      def get(s: S): A =
        _get(s)

      def replace(b: B): S => T =
        _set(b)

      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)
    }

  implicit def lensSyntax[S, A](self: Lens[S, A]): LensSyntax[S, A] =
    new LensSyntax(self)
}

object Lens {
  def id[A]: Lens[A, A] =
    Iso.id[A].asLens

  def codiagonal[S]: Lens[Either[S, S], S] =
    PLens.codiagonal

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

sealed abstract class LensInstances {
  implicit val lensChoice: Choice[Lens] = new Choice[Lens] {
    def choice[A, B, C](f: Lens[A, C], g: Lens[B, C]): Lens[Either[A, B], C] =
      f choice g

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

    def compose[A, B, C](f: Lens[B, C], g: Lens[A, B]): Lens[A, C] =
      g.andThen(f)
  }
}

/** Extension methods for monomorphic Lens
  */
final case class LensSyntax[S, A](private val self: Lens[S, A]) extends AnyVal {
  def each[C](implicit evEach: Each[A, C]): Traversal[S, C] =
    self 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): Optional[S, A] =
    self.andThen(Optional.filter(predicate))

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

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

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

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




© 2015 - 2025 Weber Informatics LLC | Privacy Policy