PageRenderTime 44ms CodeModel.GetById 1ms app.highlight 37ms RepoModel.GetById 1ms app.codeStats 0ms

/core/src/main/scala/scalaz/Monoid.scala

http://github.com/scalaz/scalaz
Scala | 150 lines | 74 code | 24 blank | 52 comment | 4 complexity | 2bd2ac38bb80e2d44f0698dcef7a0939 MD5 | raw file
  1package scalaz
  2
  3////
  4/**
  5 * Provides an identity element (`zero`) to the binary `append`
  6 * operation in [[scalaz.Semigroup]], subject to the monoid laws.
  7 *
  8 * Example instances:
  9 *  - `Monoid[Int]`: `zero` and `append` are `0` and `Int#+` respectively
 10 *  - `Monoid[List[A]]`: `zero` and `append` are `Nil` and `List#++` respectively
 11 *
 12 * References:
 13 *  - [[http://mathworld.wolfram.com/Monoid.html]]
 14 *
 15 * @see [[scalaz.syntax.MonoidOps]]
 16 * @see [[scalaz.Monoid.MonoidLaw]]
 17 *
 18 */
 19////
 20trait Monoid[F] extends Semigroup[F] { self =>
 21  ////
 22  /** The identity element for `append`. */
 23  def zero: F
 24
 25  // derived functions
 26  /**
 27   * For `n = 0`, `zero`
 28   * For `n = 1`, `append(zero, value)`
 29   * For `n = 2`, `append(append(zero, value), value)`
 30   */
 31  def multiply(value: F, n: Int): F =
 32    if (n <= 0) zero else multiply1(value, n - 1)
 33
 34  /** Whether `a` == `zero`. */
 35  def isMZero(a: F)(implicit eq: Equal[F]): Boolean =
 36    eq.equal(a, zero)
 37
 38  final def ifEmpty[B](a: F)(t: => B)(f: => B)(implicit eq: Equal[F]): B =
 39    if (isMZero(a)) { t } else { f }
 40
 41  final def onNotEmpty[B](a: F)(v: => B)(implicit eq: Equal[F], mb: Monoid[B]): B =
 42    ifEmpty(a)(mb.zero)(v)
 43
 44  final def onEmpty[A,B](a: F)(v: => B)(implicit eq: Equal[F], mb: Monoid[B]): B =
 45    ifEmpty(a)(v)(mb.zero)
 46
 47  def unfoldlSum[S](seed: S)(f: S => Maybe[(S, F)]): F =
 48    unfoldlSumOpt(seed)(f) getOrElse zero
 49
 50  def unfoldrSum[S](seed: S)(f: S => Maybe[(F, S)]): F =
 51    unfoldrSumOpt(seed)(f) getOrElse zero
 52
 53  /** Every `Monoid` gives rise to a [[scalaz.Category]], for which
 54    * the type parameters are phantoms.
 55    *
 56    * @note `category.monoid` = `this`
 57    */
 58  final def category: Category[λ[(α, β) => F]] =
 59    new Category[λ[(α, β) => F]] with SemigroupCompose {
 60      def id[A] = zero
 61    }
 62
 63  /**
 64   * A monoidal applicative functor, that implements `point` and `ap`
 65   * with the operations `zero` and `append` respectively.  Note that
 66   * the type parameter `α` in `Applicative[λ[α => F]]` is
 67   * discarded; it is a phantom type.  As such, the functor cannot
 68   * support [[scalaz.Bind]].
 69   */
 70  final def applicative: Applicative[λ[α =>F]] =
 71    new Applicative[λ[α => F]] with SemigroupApply {
 72      def point[A](a: => A) = zero
 73    }
 74
 75  /**
 76   * Monoid instances must satisfy [[scalaz.Semigroup.SemigroupLaw]] and 2 additional laws:
 77   *
 78   *  - '''left identity''': `forall a. append(zero, a) == a`
 79   *  - '''right identity''' : `forall a. append(a, zero) == a`
 80   */
 81  trait MonoidLaw extends SemigroupLaw {
 82    def leftIdentity(a: F)(implicit F: Equal[F]): Boolean = F.equal(a, append(zero, a))
 83    def rightIdentity(a: F)(implicit F: Equal[F]): Boolean = F.equal(a, append(a, zero))
 84  }
 85  def monoidLaw = new MonoidLaw {}
 86
 87  ////
 88  val monoidSyntax: scalaz.syntax.MonoidSyntax[F] =
 89    new scalaz.syntax.MonoidSyntax[F] { def F = Monoid.this }
 90}
 91
 92object Monoid {
 93  @inline def apply[F](implicit F: Monoid[F]): Monoid[F] = F
 94
 95  import Isomorphism._
 96
 97  def fromIso[F, G](D: F <=> G)(implicit M: Monoid[G]): Monoid[F] =
 98    new IsomorphismMonoid[F, G] {
 99      override def G: Monoid[G] = M
100      override def iso: F <=> G = D
101    }
102
103  ////
104
105  /** Make an append and zero into an instance. */
106  def instance[A](f: (A, => A) => A, z: A): Monoid[A] =
107    new Monoid[A] {
108      def zero = z
109      def append(f1: A, f2: => A): A = f(f1,f2)
110    }
111
112  private trait ApplicativeMonoid[F[_], M] extends Monoid[F[M]] with Semigroup.ApplySemigroup[F, M] {
113    implicit def F: Applicative[F]
114    implicit def M: Monoid[M]
115    val zero = F.point(M.zero)
116  }
117
118  /**A monoid for sequencing Applicative effects. */
119  def liftMonoid[F[_], M](implicit F0: Applicative[F], M0: Monoid[M]): Monoid[F[M]] =
120    new ApplicativeMonoid[F, M] {
121      implicit def F: Applicative[F] = F0
122      implicit def M: Monoid[M] = M0
123    }
124
125  def liftPlusEmpty[A](implicit M0: Monoid[A]): PlusEmpty[λ[α => A]] =
126    new PlusEmpty[λ[α => A]] {
127      type A0[α] = A
128      def empty[A]: A0[A] = M0.zero
129      def plus[A](f1: A0[A], f2: => A0[A]): A0[A] = M0.append(f1, f2)
130    }
131
132  /** Monoid is an invariant functor. */
133  implicit val monoidInvariantFunctor: InvariantFunctor[Monoid] =
134    new InvariantFunctor[Monoid] {
135      def xmap[A, B](ma: Monoid[A], f: A => B, g: B => A): Monoid[B] = new Monoid[B] {
136        def zero: B = f(ma.zero)
137        def append(x: B, y: => B): B = f(ma.append(g(x), g(y)))
138      }
139    }
140
141  ////
142}
143
144trait IsomorphismMonoid[F, G] extends Monoid[F] with IsomorphismSemigroup[F, G]{
145  implicit def G: Monoid[G]
146  ////
147
148  def zero: F = iso.from(G.zero)
149  ////
150}