PageRenderTime 26ms CodeModel.GetById 12ms app.highlight 10ms RepoModel.GetById 2ms app.codeStats 0ms

/core/src/main/scala/scalaz/Contravariant.scala

http://github.com/scalaz/scalaz
Scala | 106 lines | 47 code | 19 blank | 40 comment | 0 complexity | 217c625233716dcac308a5112d00f0bf MD5 | raw file
  1package scalaz
  2
  3////
  4import scalaz.Liskov.<~<
  5/**
  6 * Contravariant functors.  For example, functions provide a
  7 * [[scalaz.Functor]] in their result type, but a
  8 * [[scalaz.Contravariant]] for each argument type.
  9 *
 10 * Note that the dual of a [[scalaz.Functor]] is just a [[scalaz.Functor]]
 11 * itself.
 12 *
 13 * Providing an instance of this is a useful alternative to marking a
 14 * type parameter with `-` in Scala.
 15 *
 16 * @see [[scalaz.Contravariant.ContravariantLaw]]
 17 */
 18////
 19trait Contravariant[F[_]] extends InvariantFunctor[F] { self =>
 20  ////
 21
 22  /** Transform `A`.
 23    *
 24    * @note `contramap(r)(identity)` = `r`
 25    */
 26  def contramap[A, B](r: F[A])(f: B => A): F[B]
 27
 28  // derived functions
 29
 30  def narrow[A, B](fa: F[A])(implicit ev: B <~< A): F[B] =
 31    contramap(fa)(ev.apply)
 32
 33  def xmap[A, B](fa: F[A], f: A => B, g: B => A): F[B] =
 34    contramap(fa)(g)
 35
 36  /** The composition of Contravariant F and G, `[x]F[G[x]]`, is
 37    * covariant.
 38    */
 39  def compose[G[_]](implicit G0: Contravariant[G]): Functor[λ[α => F[G[α]]]] =
 40    new Functor[λ[α => F[G[α]]]] {
 41      def map[A, B](fa: F[G[A]])(f: A => B) =
 42        self.contramap(fa)(gb => G0.contramap(gb)(f))
 43    }
 44
 45  /** The composition of Contravariant F and Functor G, `[x]F[G[x]]`,
 46    * is contravariant.
 47    */
 48  def icompose[G[_]](implicit G0: Functor[G]): Contravariant[λ[α => F[G[α]]]] =
 49    new Contravariant[λ[α => F[G[α]]]] {
 50      def contramap[A, B](fa: F[G[A]])(f: B => A) =
 51        self.contramap(fa)(G0.lift(f))
 52    }
 53
 54  /** The product of Contravariant `F` and `G`, `[x](F[x], G[x]])`, is
 55    * contravariant.
 56    */
 57  def product[G[_]](implicit G0: Contravariant[G]): Contravariant[λ[α => (F[α], G[α])]] =
 58    new Contravariant[λ[α => (F[α], G[α])]] {
 59      def contramap[A, B](fa: (F[A], G[A]))(f: B => A) =
 60        (self.contramap(fa._1)(f), G0.contramap(fa._2)(f))
 61    }
 62
 63  trait ContravariantLaw extends InvariantFunctorLaw {
 64    /** The identity function, lifted, is a no-op. */
 65    def identity[A](fa: F[A])(implicit FA: Equal[F[A]]): Boolean = FA.equal(contramap(fa)(x => x), fa)
 66
 67    /**
 68     * A series of contramaps may be freely rewritten as a single
 69     * contramap on a composed function.
 70     */
 71    def composite[A, B, C](fa: F[A], f1: B => A, f2: C => B)(implicit FC: Equal[F[C]]): Boolean = FC.equal(contramap(contramap(fa)(f1))(f2), contramap(fa)(f1 compose f2))
 72  }
 73  def contravariantLaw = new ContravariantLaw {}
 74
 75  ////
 76  val contravariantSyntax: scalaz.syntax.ContravariantSyntax[F] =
 77    new scalaz.syntax.ContravariantSyntax[F] { def F = Contravariant.this }
 78}
 79
 80object Contravariant {
 81  @inline def apply[F[_]](implicit F: Contravariant[F]): Contravariant[F] = F
 82
 83  import Isomorphism._
 84
 85  def fromIso[F[_], G[_]](D: F <~> G)(implicit E: Contravariant[G]): Contravariant[F] =
 86    new IsomorphismContravariant[F, G] {
 87      override def G: Contravariant[G] = E
 88      override def iso: F <~> G = D
 89    }
 90
 91  ////
 92
 93  ////
 94}
 95
 96trait IsomorphismContravariant[F[_], G[_]] extends Contravariant[F] with IsomorphismInvariantFunctor[F, G]{
 97  implicit def G: Contravariant[G]
 98  ////
 99  import Isomorphism._
100
101  def iso: F <~> G
102
103  override def contramap[A, B](r: F[A])(f: B => A): F[B] =
104    iso.from(G.contramap(iso.to(r))(f))
105  ////
106}