/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

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