/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}
```