/core/src/main/scala/scalaz/InvariantFunctor.scala

http://github.com/scalaz/scalaz · Scala · 75 lines · 33 code · 16 blank · 26 comment · 0 complexity · 0bc56ea07c21a45a603afd0c9c00861c MD5 · raw file

  1. package scalaz
  2. ////
  3. /**
  4. * Unary type constructor that supports an `xmap` operation that converts an `F[A]` to an `F[B]` given
  5. * two functions, `A => B` and `B => A`.
  6. *
  7. * An invariant functor must satisfy two laws:
  8. * - identity - xmap(ma)(identity, identity) == ma
  9. * - composite - xmap(xmap(ma, f1, g1), f2, g2) == xmap(ma, f2 compose f1, g1, compose g2)
  10. *
  11. * Also known as an exponential functor.
  12. *
  13. * @see [[https://hackage.haskell.org/packages/archive/invariant/latest/doc/html/Data-Functor-Invariant.html]]
  14. * @see [[http://comonad.com/reader/2008/rotten-bananas/]]
  15. *
  16. * @see [[scalaz.InvariantFunctor.InvariantFunctorLaw]]
  17. */
  18. ////
  19. trait InvariantFunctor[F[_]] { self =>
  20. ////
  21. import BijectionT.Bijection
  22. import Isomorphism.<=>
  23. /** Converts `ma` to a value of type `F[B]` using the provided functions `f` and `g`. */
  24. def xmap[A, B](ma: F[A], f: A => B, g: B => A): F[B]
  25. /** Converts `ma` to a value of type `F[B]` using the provided bijection. */
  26. def xmapb[A, B](ma: F[A])(b: Bijection[A, B]): F[B] = xmap(ma, b.to, b.from)
  27. /** Converts `ma` to a value of type `F[B]` using the provided isomorphism. */
  28. def xmapi[A, B](ma: F[A])(iso: A <=> B): F[B] = xmap(ma, iso.to, iso.from)
  29. trait InvariantFunctorLaw {
  30. def invariantIdentity[A](fa: F[A])(implicit FA: Equal[F[A]]): Boolean =
  31. FA.equal(xmap[A, A](fa, x => x, x => x), fa)
  32. def invariantComposite[A, B, C](fa: F[A], f1: A => B, g1: B => A, f2: B => C, g2: C => B)(implicit FC: Equal[F[C]]): Boolean =
  33. FC.equal(xmap(xmap(fa, f1, g1), f2, g2), xmap(fa, f2 compose f1, g1 compose g2))
  34. }
  35. def invariantFunctorLaw = new InvariantFunctorLaw {}
  36. ////
  37. val invariantFunctorSyntax: scalaz.syntax.InvariantFunctorSyntax[F] =
  38. new scalaz.syntax.InvariantFunctorSyntax[F] { def F = InvariantFunctor.this }
  39. }
  40. object InvariantFunctor {
  41. @inline def apply[F[_]](implicit F: InvariantFunctor[F]): InvariantFunctor[F] = F
  42. import Isomorphism._
  43. def fromIso[F[_], G[_]](D: F <~> G)(implicit E: InvariantFunctor[G]): InvariantFunctor[F] =
  44. new IsomorphismInvariantFunctor[F, G] {
  45. override def G: InvariantFunctor[G] = E
  46. override def iso: F <~> G = D
  47. }
  48. ////
  49. ////
  50. }
  51. trait IsomorphismInvariantFunctor[F[_], G[_]] extends InvariantFunctor[F] {
  52. implicit def G: InvariantFunctor[G]
  53. ////
  54. import Isomorphism._
  55. def iso: F <~> G
  56. override def xmap[A, B](ma: F[A], f: A => B, g: B => A): F[B] =
  57. iso.from(G.xmap(iso.to(ma), f, g))
  58. ////
  59. }