/core/src/main/scala/scalaz/Monoid.scala
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}