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

proptics.Grate.scala Maven / Gradle / Ivy

package proptics

import scala.Function.const

import cats.{Applicative, Distributive, Functor}

import proptics.internal.{Indexed, Tagged, Zipping}
import proptics.profunctor.Costar._
import proptics.profunctor.{Closed, Costar}
import proptics.rank2types.Rank2TypeGrateLike
import proptics.syntax.costar._
import proptics.syntax.function._

/** A [[Grate_]] allows a generalized zipWith operation.
  *
  * @tparam S
  *   the source of a [[Grate_]]
  * @tparam T
  *   the modified source of a [[Grate_]]
  * @tparam A
  *   the focus of a [[Grate_]]
  * @tparam B
  *   the modified focus of a [[Grate_]]
  */
abstract class Grate_[S, T, A, B] extends Serializable { self =>
  private[proptics] def apply[P[_, _]](pab: P[A, B])(implicit ev: Closed[P]): P[S, T]

  /** view the modified source of a [[Grate_]] */
  final def review(b: B): T = self(Tagged[A, B](b)).runTag

  /** set the modified focus of a [[Grate_]] */
  final def set(b: B): S => T = over(const(b))

  /** modify the focus type of a [[Grate_]] using a function, resulting in a change of type to the full structure */
  final def over(f: A => B): S => T = self(f)

  /** zip two sources of a [[Grate_]] together provided a binary operation which modify the focus of a [[Grate_]] */
  final def zipWith(s1: S, s2: S)(f: (A, A) => B): T = self(Zipping(f.curried)).runZipping(s1)(s2)

  /** modify an effectful focus of a [[Grate_]] to the type of the modified focus, resulting in a change of type to the full structure */
  final def cotraverse[F[_]: Applicative](fs: F[S])(f: F[A] => B): T = self(Costar(f)).runCostar(fs)

  /** synonym for [[cotraverse]], flipped */
  final def zipWithF[F[_]: Applicative](f: F[A] => B)(fs: F[S]): T = cotraverse(fs)(f)

  /** compose this [[Grate_]] with an [[Iso_]], having this [[Grate_]] applied first */
  final def andThen[C, D](other: Iso_[A, B, C, D]): Grate_[S, T, C, D] = new Grate_[S, T, C, D] {
    override def apply[P[_, _]](pab: P[C, D])(implicit ev: Closed[P]): P[S, T] = self(other(pab))
  }

  /** compose this [[Grate_]] with an [[Iso_]], having this [[Grate_]] applied last */
  final def compose[C, D](other: Iso_[C, D, S, T]): Grate_[C, D, A, B] = new Grate_[C, D, A, B] {
    override private[proptics] def apply[P[_, _]](pab: P[A, B])(implicit ev: Closed[P]): P[C, D] =
      other(self(pab))
  }

  /** compose this [[Grate_]] with an [[AnIso_]], having this [[Grate_]] applied first */
  final def andThen[C, D](other: AnIso_[A, B, C, D]): Grate_[S, T, C, D] = new Grate_[S, T, C, D] {
    override def apply[P[_, _]](pab: P[C, D])(implicit ev: Closed[P]): P[S, T] =
      self(ev.dimap(pab)(other.view)(other.review))
  }

  /** compose this [[Grate_]] with an [[AnIso_]], having this [[Grate_]] applied last */
  final def compose[C, D](other: AnIso_[C, D, S, T]): Grate_[C, D, A, B] = new Grate_[C, D, A, B] {
    override private[proptics] def apply[P[_, _]](pab: P[A, B])(implicit ev: Closed[P]): P[C, D] =
      ev.dimap(self(pab))(other.view)(other.review)
  }

  /** compose this [[Grate_]] with a [[Setter_]], having this [[Grate_]] applied first */
  final def andThen[C, D](other: Setter_[A, B, C, D]): Setter_[S, T, C, D] = new Setter_[S, T, C, D] {
    override private[proptics] def apply(pab: C => D): S => T = self(other(pab))
  }

  /** compose this [[Grate_]] with a [[Setter_]], having this [[Grate_]] applied last */
  final def compose[C, D](other: Setter_[C, D, S, T]): Setter_[C, D, A, B] = new Setter_[C, D, A, B] {
    override private[proptics] def apply(pab: A => B): C => D = other.over(self.over(pab))
  }

  /** compose this [[Grate_]] with a [[Grate_]], having this [[Grate_]] applied first */
  final def andThen[C, D](other: Grate_[A, B, C, D]): Grate_[S, T, C, D] = new Grate_[S, T, C, D] {
    override def apply[P[_, _]](pab: P[C, D])(implicit ev: Closed[P]): P[S, T] = self(other(pab))
  }

  /** compose this [[Grate_]] with a [[Grate_]], having this [[Grate_]] applied last */
  final def compose[C, D](other: Grate_[C, D, S, T]): Grate_[C, D, A, B] = new Grate_[C, D, A, B] {
    override private[proptics] def apply[P[_, _]](pab: P[A, B])(implicit ev: Closed[P]): P[C, D] = other(self(pab))
  }

  /** compose this [[Grate_]] with a [[Review_]], having this [[Grate_]] applied first */
  final def andThen[C, D](other: Review_[A, B, C, D]): Review_[S, T, C, D] = new Review_[S, T, C, D] {
    override private[proptics] def apply(tagged: Tagged[C, D]): Tagged[S, T] = self(other(tagged))
  }

  /** compose this [[Grate_]] with a [[Review_]], having this [[Grate_]] applied last */
  final def compose[C, D](other: Review_[C, D, S, T]): Review_[C, D, A, B] = new Review_[C, D, A, B] {
    override private[proptics] def apply(tagged: Tagged[A, B]): Tagged[C, D] =
      Tagged(other.review(self.review(tagged.runTag)))
  }

  /** compose this [[Grate_]] with an [[IndexedSetter_]], having this [[Grate_]] applied first */
  final def andThen[I, C, D](other: IndexedSetter_[I, A, B, C, D]): IndexedSetter_[I, S, T, C, D] = new IndexedSetter_[I, S, T, C, D] {
    override private[proptics] def apply(indexed: Indexed[* => *, I, C, D]): S => T =
      self.over(other.over(indexed.runIndex))
  }

  /** compose this [[Grate_]] with an [[IndexedSetter_]], having this [[Grate_]] applied last */
  final def compose[I, C, D](other: IndexedSetter_[I, C, D, S, T]): IndexedSetter_[I, C, D, A, B] = new IndexedSetter_[I, C, D, A, B] {
    override private[proptics] def apply(indexed: Indexed[* => *, I, A, B]): C => D =
      other.over { case (s, i) => self.over(a => indexed.runIndex((a, i)))(s) }
  }
}

object Grate_ {
  /** create a polymorphic [[Grate_]] from Rank2TypeGrateLike encoding */
  private[proptics] def apply[S, T, A, B](f: Rank2TypeGrateLike[S, T, A, B]): Grate_[S, T, A, B] = new Grate_[S, T, A, B] {
    override def apply[P[_, _]](pab: P[A, B])(implicit ev: Closed[P]): P[S, T] = f(pab)
  }

  /** create a polymorphic [[Grate_]] from a nested continuation function */
  final def apply[S, T, A, B](grate: ((S => A) => B) => T): Grate_[S, T, A, B] = Grate_(new Rank2TypeGrateLike[S, T, A, B] {
    override def apply[P[_, _]](pab: P[A, B])(implicit ev: Closed[P]): P[S, T] =
      ev.dimap[(S => A) => A, (S => A) => B, S, T](ev.closed(pab))(_.&)(grate)
  })

  /** create a polymorphic [[Grate_]] from a [[cats.Distributive]] */
  final def fromDistributive[F[_], A, B](implicit ev: Distributive[F]): Grate_[F[A], F[B], A, B] = {
    def cotraverse[G[_]: Functor](f: G[A] => B)(gfa: G[F[A]]): F[B] =
      ev.map(ev.cosequence(gfa))(f)

    Grate_[F[A], F[B], A, B](cotraverse(_: (F[A] => A) => B)(identity[F[A]])(Functor[F[A] => *]))
  }

  /** polymorphic identity of a [[Grate_]] */
  final def id[S, T]: Grate_[S, T, S, T] = Grate_[S, T, S, T]((s2s: (S => S) => T) => s2s(identity[S]))
}

object Grate {
  /** create a monomorphic [[Grate]] from a nested continuation function */
  final def apply[S, A](to: ((S => A) => A) => S): Grate[S, A] = Grate_(to)

  /** create a monomorphic [[Grate]] from a [[cats.Distributive]] */
  final def fromDistributive[F[_]: Distributive, A]: Grate[F[A], A] = Grate_.fromDistributive

  /** monomorphic identity of a [[Grate]] */
  final def id[S]: Grate[S, S] = Grate_.id[S, S]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy