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

scalaz.PLens.scala Maven / Gradle / Ivy

The newest version!
package scalaz

/**
 * Partial Lens Families, offering a purely functional means to access and retrieve
 * an optional field transitioning from type `B1` to type `B2` in a record that is
 * simultaneously transitioning from type `A1` to type `A2`.  [[scalaz.PLens]] is a
 * convenient alias for when `A1 === A2`, and `B1 === B2`.
 *
 * The term ''field'' should not be interpreted restrictively to mean a member of a class. For example, a partial lens
 * family can address the nth element of a `List`.
 *
 * @see [[scalaz.Lens]]
 *
 * @tparam A1 The initial type of the record
 * @tparam A2 The final type of the record
 * @tparam B1 The initial type of the optional field
 * @tparam B2 The final type of the optional field
 */
sealed abstract class PLensFamily[A1, A2, B1, B2] {
  def run(a: A1): Option[IndexedStore[B1, B2, A2]]

  def apply(a: A1): Option[IndexedStore[B1, B2, A2]] =
    run(a)

  import BijectionT._
  import PLensFamily._

  def kleisli: Kleisli[Option, A1, IndexedStore[B1, B2, A2]] =
    Kleisli[Option, A1, IndexedStore[B1, B2, A2]](apply)

  def xmapA[X1, X2](f: A2 => X2)(g: X1 => A1): PLensFamily[X1, X2, B1, B2] =
    plensFamily(x => apply(g(x)) map (_ map (f)))

  def xmapbA[X, A >: A2 <: A1](b: Bijection[A, X]): PLensFamily[X, X, B1, B2] =
    xmapA(b to _)(b from _)

  def xmapB[X1, X2](f: B1 => X1)(g: X2 => B2): PLensFamily[A1, A2, X1, X2] =
    plensFamily(a => apply(a) map (_.xmap(f)(g)))

  def xmapbB[X, B >: B1 <: B2](b: Bijection[B, X]): PLensFamily[A1, A2, X, X] =
    xmapB(b to _)(b from _)

  def get(a: A1): Option[B1] =
    run(a) map (_.pos)

  def getK: Kleisli[Option, A1, B1] =
    Kleisli[Option, A1, B1](get)

  /** If the Partial Lens is null, then return the given default value. */
  def getOr(a: A1, b: => B1): B1 =
    get(a) getOrElse b

  def getOrZ(a: A1)(implicit M: Monoid[B1]): B1 =
    getOr(a, M.zero)

  def set(a: A1, b: B2): Option[A2] =
    run(a) map (_.put(b))

  def setK(a: A1): Kleisli[Option, B2, A2] =
    Kleisli[Option, B2, A2](set(a, _))

  def setOr(a: A1, b: B2, d: => A2): A2 =
    set(a, b) getOrElse d

  def setOrZ(a: A1, b: B2)(implicit M: Monoid[A2]): A2 =
    setOr(a, b, M.zero)

  def trySet(a: A1): Option[B2 => A2] =
    run(a) map (c => c put _)

  def trySetK: Kleisli[Option, A1, B2 => A2] =
    Kleisli[Option, A1, B2 => A2](trySet)

  def trySetOr(a: A1, d: => B2 => A2): B2 => A2 =
    trySet(a) getOrElse d

  def trySetOrZ(a: A1)(implicit M: Monoid[A2]): B2 => A2 =
    trySetOr(a, _ => M.zero)

  /** If the Partial Lens is null, then return the target object, otherwise run the function on its projection. */
  def as(f: B1 => A1, a: A1): A1 =
    get(a) match {
      case None => a
      case Some(w) => f(w)
    }

  def is(a: A1): Boolean =
    run(a).isDefined

  def isNot(a: A1): Boolean =
    !is(a)

  def exists(p: B1 => Boolean, a: A1): Boolean =
    get(a) exists p

  def forall(p: B1 => Boolean, a: A1): Boolean =
    get(a) forall p

  def modg(f: B1 => B2, a: A1): Option[A2] =
    run(a).map(_ puts f)

  def =?>=(f: B1 => B2): A1 => Option[A2] =
    modg(f, _)

  /** Modify the potential value viewed through the partial lens */
  def mod[A >: A2 <: A1](f: B1 => B2, a: A): A =
    run(a) match {
      case None => a: A
      case Some(w) => (w puts f): A
    }

  def =>=[A >: A2 <: A1](f: B1 => B2): A => A =
    mod(f, _)

  def st: PState[A1, B1] =
    State(s => (s, get(s)))

  def %=[A >: A2 <: A1](f: B1 => B2): PState[A, B2] =
    State(a => run(a) match {
      case None => (a, None)
      case Some(w) => {
        val r = f(w.pos)
        (w put r, Some(r))
      }
    })

  def :=[A >: A2 <: A1](b: => B2): PState[A, B2] =
    %=(_ => b)

  def %==[A >: A2 <: A1](f: B1 => B2): State[A, Unit] =
    State(a =>
  (mod(f, a), ()))

  def %%=[A >: A2 <: A1, C](s: IndexedState[B1, B2, C]): PState[A, C] =
    State(a => run(a) match {
      case None => (a, None)
      case Some(w) => {
        val r = s.run(w.pos): (B2, C)
        (w put r._1, Some(r._2))
      }
    })

  def >-[A >: A2 <: A1, C](f: B1 => C): PState[A, C] =
    State(a => (a, get(a) map f))

  def >>-[A >: A2 <: A1, C](f: B1 => State[A, C]): PState[A, C] =
    StateT(a => get(a) match {
      case None => (a, None)
      case Some(w) =>
        f(w) apply a match {
          case (y, x) => (y, Some(x))
        }
    })

  def ->>-[A >: A2 <: A1, C](f: => State[A, C]): PState[A, C] =
    >>-(_ => f)

  /** Partial Lenses can be composed */
  def compose[C1, C2](that: PLensFamily[C1, C2, A1, A2]): PLensFamily[C1, C2, B1, B2] =
    plensFamily(c =>
      (that run c).flatMap (x => {
        val (ac, a) = x.run
        run(a) map (y => {
          val (ba, b) = y.run
          IndexedStore(ac compose ba, b)
        })
      }))

  /** alias for `compose` */
  def <=<[C1, C2](that: PLensFamily[C1, C2, A1, A2]): PLensFamily[C1, C2, B1, B2] = compose(that)

  def andThen[C1, C2](that: PLensFamily[B1, B2, C1, C2]): PLensFamily[A1, A2, C1, C2] =
    that compose this

  /** alias for `andThen` */
  def >=>[C1, C2](that: PLensFamily[B1, B2, C1, C2]): PLensFamily[A1, A2, C1, C2] = andThen(that)

  /** Two partial lenses that view a value of the same type can be joined */
  def sum[C1, C2](that: => PLensFamily[C1, C2, B1, B2]): PLensFamily[A1 \/ C1, A2 \/ C2, B1, B2] =
    plensFamily {
      case -\/(a) =>
        run(a) map (_ map (\/.left))
      case \/-(c) =>
        that run c map (_ map (\/.right))
    }

  /** Alias for `sum` */
  def |||[C1, C2](that: => PLensFamily[C1, C2, B1, B2]): PLensFamily[A1 \/ C1, A2 \/ C2, B1, B2] = sum(that)

  /** Two disjoint partial lenses can be paired */
  def product[C1, C2, D1, D2](that: PLensFamily[C1, C2, D1, D2]): PLensFamily[(A1, C1), (A2, C2), (B1, D1), (B2, D2)] =
    plensFamily {
      case (a, c) =>
        for {
          q <- run(a)
          r <- that run c
        } yield q *** r
    }

  /** alias for `product` */
  def ***[C1, C2, D1, D2](that: PLensFamily[C1, C2, D1, D2]): PLensFamily[(A1, C1), (A2, C2), (B1, D1), (B2, D2)] = product(that)

}

object PLensFamily extends PLensInstances with PLensFunctions {

  def apply[A1, A2, B1, B2](r: A1 => Option[IndexedStore[B1, B2, A2]]): PLensFamily[A1, A2, B1, B2] =
    plensFamily(r)
}

trait PLensFamilyFunctions extends PLensInstances {

  import BijectionT._

  def plensFamily[A1, A2, B1, B2](r: A1 => Option[IndexedStore[B1, B2, A2]]): PLensFamily[A1, A2, B1, B2] = new PLensFamily[A1, A2, B1, B2] {
    def run(a: A1): Option[IndexedStore[B1, B2, A2]] = r(a)
  }

  def plensFamilyf[A1, A2, B1, B2](r: PartialFunction[A1, IndexedStore[B1, B2, A2]]): PLensFamily[A1, A2, B1, B2] =
    plensFamily(r.lift)

  def plensFamilyg[A1, A2, B1, B2](set: A1 => Option[B2 => A2], get: A1 => Option[B1]): PLensFamily[A1, A2, B1, B2] =
    plensFamily(a => for {
      w <- set(a)
      x <- get(a)
    } yield IndexedStore(w, x))

  /** The identity partial lens family for a given pair of objects */
  def plensFamilyId[A1, A2]: PLensFamily[A1, A2, A1, A2] =
    LensFamily.lensFamilyId[A1, A2].partial

  /** A partial lens family that discards the choice of right or left from disjunction */
  def codiagPLensFamily[A1, A2]: PLensFamily[A1 \/ A1, A2 \/ A2, A1, A2] =
    plensFamilyId[A1, A2] ||| plensFamilyId[A1, A2]

  /** The always-null partial lens family */
  def nilFamily[A1, A2, B1, B2]: PLensFamily[A1, A2, B1, B2] =
    plensFamily(_ => None)

  def somePLensFamily[A1, A2]: PLensFamily[Option[A1], Option[A2], A1, A2] =
    plensFamily(_ map (z => IndexedStore(Some(_), z)))

  def leftPLensFamily[A1, A2, B]: PLensFamily[A1 \/ B, A2 \/ B, A1, A2] =
    plensFamily {
      case -\/(a) => Some(IndexedStore(\/.left, a))
      case \/-(_) => None
    }

  def rightPLensFamily[A, B1, B2]: PLensFamily[A \/ B1, A \/ B2, B1, B2] =
    plensFamily {
      case \/-(b) => Some(IndexedStore(\/.right, b))
      case -\/(_) => None
    }

  def tuple2PLensFamily[S1, S2, A, B](lens: PLensFamily[S1, S2, (A, B), (A, B)]):
  (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B]) =
    PLensFamilyUnzip[S1, S2].unzip(lens)

  def tuple3PLensFamily[S1, S2, A, B, C](lens: PLensFamily[S1, S2, (A, B, C), (A, B, C)]):
  (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B], PLensFamily[S1, S2, C, C]) =
    PLensFamilyUnzip[S1, S2].unzip3(lens.xmapbB(tuple3B))

  def tuple4PLensFamily[S1, S2, A, B, C, D](lens: PLensFamily[S1, S2, (A, B, C, D), (A, B, C, D)]):
  (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B], PLensFamily[S1, S2, C, C], PLensFamily[S1, S2, D, D]) =
    PLensFamilyUnzip[S1, S2].unzip4(lens.xmapbB(tuple4B))

  def tuple5PLensFamily[S1, S2, A, B, C, D, E](lens: PLensFamily[S1, S2, (A, B, C, D, E), (A, B, C, D, E)]):
  (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B], PLensFamily[S1, S2, C, C], PLensFamily[S1, S2, D, D], PLensFamily[S1, S2, E, E]) =
    PLensFamilyUnzip[S1, S2].unzip5(lens.xmapbB(tuple5B))

  def tuple6PLensFamily[S1, S2, A, B, C, D, E, H](lens: PLensFamily[S1, S2, (A, B, C, D, E, H), (A, B, C, D, E, H)]):
  (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B], PLensFamily[S1, S2, C, C], PLensFamily[S1, S2, D, D], PLensFamily[S1, S2, E, E], PLensFamily[S1, S2, H, H]) =
    PLensFamilyUnzip[S1, S2].unzip6(lens.xmapbB(tuple6B))

  def tuple7PLensFamily[S1, S2, A, B, C, D, E, H, I](lens: PLensFamily[S1, S2, (A, B, C, D, E, H, I), (A, B, C, D, E, H, I)]):
  (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B], PLensFamily[S1, S2, C, C], PLensFamily[S1, S2, D, D], PLensFamily[S1, S2, E, E], PLensFamily[S1, S2, H, H], PLensFamily[S1, S2, I, I]) =
    PLensFamilyUnzip[S1, S2].unzip7(lens.xmapbB(tuple7B))

  def eitherLensFamily[S1, S2, A, B](l: PLensFamily[S1, S2, A \/ B, A \/ B]): (PLensFamily[S1, S2, A, A], PLensFamily[S1, S2, B, B]) =
    (
    leftPLensFamily compose l
    , rightPLensFamily compose l
    )

  import LazyOption._

  def lazySomePLensFamily[A1, A2]: PLensFamily[LazyOption[A1], LazyOption[A2], A1, A2] =
    plensFamily(_.fold(z => Some(IndexedStore(lazySome(_), z)), None))

  import LazyEither._

  def lazyLeftPLensFamily[A1, A2, B]: PLensFamily[LazyEither[A1, B], LazyEither[A2, B], A1, A2] =
    plensFamily(_.fold(a => Some(IndexedStore(lazyLeft(_), a)), _ => None))

  def lazyRightPLensFamily[A, B1, B2]: PLensFamily[LazyEither[A, B1], LazyEither[A, B2], B1, B2] =
    plensFamily(_.fold(_ => None, b => Some(IndexedStore(lazyRight(_), b))))

  def factorPLensFamily[A1, A2, B1, B2, C1, C2]: PLensFamily[((A1, B1) \/ (A1, C1)), ((A2, B2) \/ (A2, C2)), (A1, B1 \/ C1), (A2, B2 \/ C2)] =
    ~LensFamily.factorLensFamily

  def distributePLensFamily[A1, A2, B1, B2, C1, C2]: PLensFamily[(A1, B1 \/ C1), (A2, B2 \/ C2), ((A1, B1) \/ (A1, C1)), ((A2, B2) \/ (A2, C2))] =
    ~LensFamily.distributeLensFamily
}

trait PLensFunctions extends PLensInstances with PLensFamilyFunctions {
  import BijectionT._

  def plens[A, B](r: A => Option[Store[B, A]]): PLens[A, B] = new PLens[A, B] {
    def run(a: A): Option[Store[B, A]] = r(a)
  }

  def plensf[A, B](r: PartialFunction[A, Store[B, A]]): PLens[A, B] =
    plens(r.lift)

  def plensg[A, B](set: A => Option[B => A], get: A => Option[B]): PLens[A, B] =
    plens(a => for {
      w <- set(a)
      x <- get(a)
    } yield Store(w, x))

  def plensgf[A, B](set: PartialFunction[A, B => A], get: PartialFunction[A, B]): PLens[A, B] =
    plensg(set.lift, get.lift)

  /** The identity partial lens for a given object */
  def plensId[A]: PLens[A, A] =
    LensFamily.lensId[A].partial

  /** The trivial partial lens that can retrieve Unit from anything */
  def trivialPLens[A]: PLens[A, Unit] =
    LensFamily.trivialLens[A].partial

  /** A lens that discards the choice of right or left from disjunction */
  def codiagPLens[A]: PLens[A \/ A, A] =
    plensId[A] ||| plensId[A]

  /** The always-null partial lens */
  def nil[A, B]: PLens[A, B] =
    plens(_ => None)

  def somePLens[A]: Option[A] @?> A =
    plens(_ map (z => Store(Some(_), z)))

  def leftPLens[A, B]: (A \/ B) @?> A =
    plens {
      case -\/(a) => Some(Store(\/.left, a))
      case \/-(_) => None
    }

  def rightPLens[A, B]: (A \/ B) @?> B =
    plens {
      case \/-(b) => Some(Store(\/.right, b))
      case -\/(_) => None
    }

  def tuple2PLens[S, A, B](lens: PLens[S, (A, B)]):
  (PLens[S, A], PLens[S, B]) =
    PLensFamilyUnzip[S, S].unzip(lens)

  def tuple3PLens[S, A, B, C](lens: PLens[S, (A, B, C)]):
  (PLens[S, A], PLens[S, B], PLens[S, C]) =
    PLensFamilyUnzip[S, S].unzip3(lens.xmapbB(tuple3B))

  def tuple4PLens[ S, A, B, C, D](lens: PLens[S, (A, B, C, D)]):
  (PLens[S, A], PLens[S, B], PLens[S, C], PLens[S, D]) =
    PLensFamilyUnzip[S, S].unzip4(lens.xmapbB(tuple4B))

  def tuple5PLens[S, A, B, C, D, E](lens: PLens[S, (A, B, C, D, E)]):
  (PLens[S, A], PLens[S, B], PLens[S, C], PLens[S, D], PLens[S, E]) =
    PLensFamilyUnzip[S, S].unzip5(lens.xmapbB(tuple5B))

  def tuple6PLens[S, A, B, C, D, E, H](lens: PLens[S, (A, B, C, D, E, H)]):
  (PLens[S, A], PLens[S, B], PLens[S, C], PLens[S, D], PLens[S, E], PLens[S, H]) =
    PLensFamilyUnzip[S, S].unzip6(lens.xmapbB(tuple6B))

  def tuple7PLens[S, A, B, C, D, E, H, I](lens: PLens[S, (A, B, C, D, E, H, I)]):
  (PLens[S, A], PLens[S, B], PLens[S, C], PLens[S, D], PLens[S, E], PLens[S, H], PLens[S, I]) =
    PLensFamilyUnzip[S, S].unzip7(lens.xmapbB(tuple7B))

  def eitherLens[S, A, B](l: S @?> (A \/ B)): (S @?> A, S @?> B) =
    (
    leftPLens compose l
    , rightPLens compose l
    )

  import LazyOption._

  def lazySomePLens[A]: LazyOption[A] @?> A =
    plens(_.fold(z => Some(Store(lazySome(_), z)), None))

  import LazyEither._

  def lazyLeftPLens[A, B]: LazyEither[A, B] @?> A =
    plens(_.fold(a => Some(Store(lazyLeft(_), a)), _ => None))

  def lazyRightPLens[A, B]: LazyEither[A, B] @?> B =
    plens(_.fold(_ => None, b => Some(Store(lazyRight(_), b))))

  def listHeadPLens[A]: List[A] @?> A =
    plens {
      case Nil => None
      case h :: t => Some(Store(_ :: t, h))
    }

  def listTailPLens[A]: List[A] @?> List[A] =
    plens {
      case Nil => None
      case h :: t => Some(Store(h :: _, t))
    }

  def listNthPLens[A](n: Int): List[A] @?> A =
    if(n < 0)
      nil
    else if(n == 0)
      listHeadPLens
    else
      listNthPLens(n - 1) compose listTailPLens

  def listLookupByPLens[K, V](p: K => Boolean): List[(K, V)] @?> V = {
    @annotation.tailrec
    def lookupr(t: (List[(K, V)], (K, V), List[(K, V)])): Option[(List[(K, V)], (K, V), List[(K, V)])] =
      t match {
        case (_, (k, _), _) if p(k) => Some(t)
        case (_, _     , Nil)       => None
        case (l, x     , r::rs)     => lookupr((x :: l, r, rs))
      }
    plens {
      case Nil => None
      case h :: t => lookupr((Nil, h, t)) map {
        case (l, (k, v), r) => Store(w => l reverse_::: (k, w) :: r, v)
      }
    }
  }

  def listLookupPLens[K: Equal, V](k: K): List[(K, V)] @?> V =
    listLookupByPLens(Equal[K].equal(k, _))

  def iListHeadPLens[A]: IList[A] @?> A =
    plens {
      case INil() => None
      case ICons(h, t) => Some(Store(_ :: t, h))
    }

  def iListTailPLens[A]: IList[A] @?> IList[A] =
    plens {
      case INil() => None
      case ICons(h, t) => Some(Store(h :: _, t))
    }

  def iListNthPLens[A](n: Int): IList[A] @?> A =
    if(n < 0)
      nil
    else if(n == 0)
      iListHeadPLens
    else
      iListNthPLens(n - 1) compose iListTailPLens

  def iListLookupByPLens[K, V](p: K => Boolean): IList[(K, V)] @?> V = {
    @annotation.tailrec
    def lookupr(t: (IList[(K, V)], (K, V), IList[(K, V)])): Option[(IList[(K, V)], (K, V), IList[(K, V)])] =
      t match {
        case (_, (k, _), _) if p(k)   => Some(t)
        case (_, _     , INil())      => None
        case (l, x     , ICons(r, rs)) => lookupr((x::l, r, rs))
      }
    plens {
      case INil() => None
      case ICons(h, t) => lookupr((IList.empty, h, t)) map {
        case (l, (k, v), r) => Store(w => l reverse_::: (k, w) :: r, v)
      }
    }
  }

  def iListLookupPLens[K: Equal, V](k: K): IList[(K, V)] @?> V =
    iListLookupByPLens(Equal[K].equal(k, _))

  def vectorHeadPLens[A]: Vector[A] @?> A =
    vectorNthPLens(0)

  def vectorNthPLens[A](n: Int): Vector[A] @?> A =
    plens(v =>
      v.lift(n) map (a => Store(x => v.patch(n, Vector(x), 1), a)))

  def vectorLastPLens[A]: Vector[A] @?> A =
    plens(v =>
      v.lastOption map (a => Store(x => v.patch(v.length - 1, Vector(x), 1), a)))

  import Stream._

  def streamHeadPLens[A]: Stream[A] @?> A =
    plens {
      case Empty => None
      case h #:: t => Some(Store(_ #:: t, h))
    }

  def streamTailPLens[A]: Stream[A] @?> Stream[A] =
    plens {
      case Empty => None
      case h #:: t => Some(Store(h #:: _, t))
    }

  def streamNthPLens[A](n: Int): Stream[A] @?> A =
    if(n < 0)
      nil
    else if(n == 0)
      streamHeadPLens
    else
      streamNthPLens(n - 1) compose streamTailPLens

  def streamLookupByPLens[K, V](p: K => Boolean): Stream[(K, V)] @?> V = {
    @annotation.tailrec
    def lookupr(t: (Stream[(K, V)], (K, V), Stream[(K, V)])): Option[(Stream[(K, V)], (K, V), Stream[(K, V)])] =
      t match {
        case (_, (k, _), _) if p(k)    => Some(t)
        case (_, _     , Stream.Empty) => None
        case (l, x     , r #:: rs)     => lookupr((x #:: l, r, rs))
      }
    plens {
      case Stream.Empty => None
      case h #:: t => lookupr((Stream.empty, h, t)) map {
        case (l, (k, v), r) => Store(w => l.reverse #::: (k, w) #:: r, v)
      }
    }
  }

  def streamLookupPLens[K: Equal, V](k: K): Stream[(K, V)] @?> V =
    streamLookupByPLens(Equal[K].equal(k, _))

  def ephemeralStreamHeadPLens[A]: EphemeralStream[A] @?> A =
    plens(s =>
      if(s.isEmpty)
        None
      else
        Some(Store(EphemeralStream.cons(_, s.tail()), s.head()))
    )

  def ephemeralStreamTailPLens[A]: EphemeralStream[A] @?> EphemeralStream[A] =
    plens(s =>
      if(s.isEmpty)
        None
      else
        Some(Store(EphemeralStream.cons(s.head(), _), s.tail()))
    )

  def ephemeralStreamNthPLens[A](n: Int): EphemeralStream[A] @?> A =
    if(n < 0)
      nil
    else if(n == 0)
      ephemeralStreamHeadPLens
    else
      ephemeralStreamNthPLens(n - 1) compose ephemeralStreamTailPLens

  def ephemeralStreamLookupByPLens[K, V](p: K => Boolean): EphemeralStream[(K, V)] @?> V = {
    import EphemeralStream.cons

    @annotation.tailrec
    def lookupr(t: (EphemeralStream[(K, V)], (K, V), EphemeralStream[(K, V)])): Option[(EphemeralStream[(K, V)], (K, V), EphemeralStream[(K, V)])] =
      t match {
        case (_, (k, _), _) if p(k)    => Some(t)
        case (l, x     , s) =>
            if(s.isEmpty)
              None
            else
              lookupr((cons(x, l), s.head(), s.tail()))
      }
    plens(s =>
      if(s.isEmpty)
        None
      else
        lookupr((EphemeralStream.emptyEphemeralStream, s.head(), s.tail())) map {
          case (l, (k, v), r) => Store(w => l.reverse ++ cons((k, w), r), v)
        }
    )
  }

  def ephemeralStreamLookupPLens[K: Equal, V](k: K): EphemeralStream[(K, V)] @?> V =
    ephemeralStreamLookupByPLens(Equal[K].equal(k, _))

  import LensFamily.mapVLens

  def mapVPLens[K, V](k: K): Map[K, V] @?> V =
    somePLens compose ~mapVLens[K, V](k)

  def factorPLens[A, B, C]: ((A, B) \/ (A, C)) @?> (A, B \/ C) =
    ~LensFamily.factorLens

  def distributePLens[A, B, C]: (A, B \/ C) @?> ((A, B) \/ (A, C)) =
    ~LensFamily.distributeLens
}

abstract class PLensInstances {

  import PLensFamily._

  implicit val plensCategory: PLensCategory = new PLensCategory {
  }

  /** Partial Lenses may be used implicitly as State monadic actions that get the potentially viewed portion of the state */
  implicit def PLensFamilyState[A, B](plens: PLensFamily[A, _, B, _]): PState[A, B] =
    plens.st

  implicit def PLensFamilyUnzip[S, R]: Unzip[λ[α => PLensFamily[S, R, α, α]]] =
    new Unzip[λ[α => PLensFamily[S, R, α, α]]] {
      def unzip[A, B](a: PLensFamily[S, R, (A, B), (A, B)]) =
        (
          plensFamily(x => a run x map (c => {
            val (p, q) = c.pos
            IndexedStore(a => c.put((a, q)): R, p)
          }))
        , plensFamily(x => a run x map (c => {
            val (p, q) = c.pos
            IndexedStore(a => c.put((p, a)): R, q)
          }))
        )
    }

  /** Allow the illusion of imperative updates to potential numbers viewed through a partial lens */
  case class NumericPLens[S, N: Numeric](lens: S @?> N, num: Numeric[N]) {
    def +=(that: N): PState[S, N] =
      lens %= (num.plus(_, that))

    def -=(that: N): PState[S, N] =
      lens %= (num.minus(_, that))

    def *=(that: N): PState[S, N] =
      lens %= (num.times(_, that))
  }

  implicit def numericPLens[S, N: Numeric](lens: S @?> N): NumericPLens[S, N] =
    NumericPLens[S, N](lens, implicitly[Numeric[N]])

  /** Allow the illusion of imperative updates to potential numbers viewed through a partial lens */
  case class FractionalPLens[S, F](lens: S @?> F, frac: Fractional[F]) {
    def /=(that: F): PState[S, F] =
      lens %= (frac.div(_, that))
  }

  implicit def fractionalPLens[S, F: Fractional](lens: S @?> F): FractionalPLens[S, F] =
    FractionalPLens[S, F](lens, implicitly[Fractional[F]])

  /** Allow the illusion of imperative updates to potential numbers viewed through a partial lens */
  case class IntegralPLens[S, I](lens: S @?> I, ig: Integral[I]) {
    def %=(that: I): PState[S, I] =
      lens %= (ig.quot(_, that))
  }

  implicit def integralPLens[S, I: Integral](lens: S @?> I): IntegralPLens[S, I] =
    IntegralPLens[S, I](lens, implicitly[Integral[I]])

}

private[scalaz] trait PLensCategory
  extends Choice[PLens]
  with Split[PLens] {

  def compose[A, B, C](bc: PLens[B, C], ab: PLens[A, B]): PLens[A, C] = ab >=> bc
  def id[A]: PLens[A, A] = PLensFamily.plensId

  def choice[A, B, C](f: => PLens[A, C], g: => PLens[B, C]): PLens[A \/ B, C] =
    PLensFamily.plens[A \/ B, C] {
      case -\/(a) =>
        f run a map (_ map (\/.left))
      case \/-(b) =>
        g run b map (_ map (\/.right))
    }

  def split[A, B, C, D](f: PLens[A, B], g: PLens[C, D]): PLens[(A,  C), (B, D)] =
    f *** g
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy