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

scalaz.ContravariantCoyoneda.scala Maven / Gradle / Ivy

The newest version!
package scalaz

/** Decomposition of `fi.contramap(k)` into its components, as it is
  * frequently convenient to apply `k` separately from sorting or
  * whatever process with `fi`, even when `B` is unknown, which is
  * very common.
  *
  * This is isomorphic to `F` as long as `F` itself is a contravariant
  * functor.  The homomorphism from `F[A]` to
  * `ContravariantCoyoneda[F,A]` exists even when `F` is not a
  * contravariant functor.
  *
  * See `ContravariantCoyonedaUsage.scala` in the scalaz source tree
  * for an interesting usage demonstration.
  *
  * As `ContravariantCoyoneda(o)(identity).unlift` = `o`, further
  * factoring can occur as follows, for free:
  *
  * {{{
  * ContravariantCoyoneda(o contramap g)(f).unlift =
  *   ContravariantCoyoneda(o)(g compose f).unlift
  * }}}
  *
  * @see [[https://hackage.haskell.org/package/kan-extensions-4.0.1/docs/Data-Functor-Contravariant-Coyoneda.html]]
  */
sealed abstract class ContravariantCoyoneda[F[_], A] {
  /** The pivot between `fi` and `k`, usually existential. */
  type I

  /** The underlying value. */
  val fi: F[I]

  /** The transformer function, to be lifted into `F` by `run`. */
  val k: A => I

  import ContravariantCoyoneda.{Aux, apply}

  /** Converts to `F[A]` given that `F` is a contravariant. */
  final def run(implicit F: Contravariant[F]): F[A] =
    F.contramap(fi)(k)

  /** Alias for `run`. */
  @inline final def unlift(implicit F: Contravariant[F]): F[A] = run

  /** Simple function composition. Allows map fusion without touching
    * the underlying `F`.
    */
  final def contramap[B](f: B => A): Aux[F, B, I] = apply(fi)(k compose f)

  /** Natural transformation. */
  final def trans[G[_]](f: F ~> G): Aux[G, A, I] = apply(f(fi))(k)
}

sealed abstract class ContravariantCoyonedaInstances {
  /** `ContravariantCoyoneda[F,_]` is a contravariant functor for any
    * `F`.
    */
  implicit def contravariantCoyonedaContravariant[F[_]]: Contravariant[ContravariantCoyoneda[F, *]] =
    new Contravariant[ContravariantCoyoneda[F, *]] {
      def contramap[A, B](fa: ContravariantCoyoneda[F, A])(f: B => A): ContravariantCoyoneda[F, B] =
        fa contramap f
    }
}

object ContravariantCoyoneda extends ContravariantCoyonedaInstances {
  /** Lift the `I` type member to a parameter.  It is usually more
    * convenient to use `Aux` than a structural type.
    */
  type Aux[F[_], A, B] = ContravariantCoyoneda[F, A] {type I = B}

  /** See `by` method. */
  final class By[F[_]](private val dummy: Boolean = true) extends AnyVal {
    @inline def apply[A, B](k: A => B)(implicit F: F[B]): Aux[F, A, B] =
      ContravariantCoyoneda(F)(k)
  }

  /** Partial application of type parameters to `apply`.  It is often
    * more convenient to invoke `ContravariantCoyoneda.by[F]{x: X =>
    * ...}` then `ContravariantCoyoneda[...](...){x: X => ...}`.
    */
  @inline def by[F[_]]: By[F] = new By[F]

  /** Like `lift(fa).contramap(_k)`. */
  def apply[F[_], A, B](fa: F[B])(_k: A => B): Aux[F, A, B] =
    new ContravariantCoyoneda[F, A]{
      type I = B
      val k = _k
      val fi = fa
    }

  /** `F[A]` converts to `ContravariantCoyoneda[F,A]` for any `F`. */
  def lift[F[_], A](fa: F[A]): ContravariantCoyoneda[F, A] =
    apply(fa)(identity[A])

  import Isomorphism._

  def iso[F[_]: Contravariant]: ContravariantCoyoneda[F, *] <~> F =
    new IsoFunctorTemplate[ContravariantCoyoneda[F, *], F] {
      def from_[A](fa: F[A]) = lift(fa)
      def to_[A](fa: ContravariantCoyoneda[F, A]) = fa.run
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy