/core/src/main/scala/scalaz/Reducer.scala

http://github.com/scalaz/scalaz · Scala · 309 lines · 200 code · 75 blank · 34 comment · 4 complexity · 4725b74500cf4539fba1579819d1310e MD5 · raw file

  1. package scalaz
  2. import scala.annotation.tailrec
  3. import scalaz.Free.{Trampoline, return_, suspend}
  4. import scalaz.Maybe.Just
  5. import scalaz.Tags.Conjunction
  6. /**
  7. * A `Reducer[C,M]` is a [[scalaz.Semigroup]]`[M]` that maps
  8. * values of type `C` through `unit` to values of type `M`. A `C-Reducer` may also
  9. * supply operations which tack on another `C` to an existing `Semigroup` `M` on the left
  10. * or right. These specialized reductions may be more efficient in some scenarios
  11. * and are used when appropriate by a [[scalaz.Generator]]. The names `cons` and `snoc` work
  12. * by analogy to the synonymous operations in the list semigroup.
  13. *
  14. * Minimal definition: `unit` or `snoc`
  15. *
  16. * Based on the Reducer Haskell library by Edward Kmett
  17. * (https://hackage.haskell.org/package/reducers).
  18. */
  19. trait Reducer[C, M] {
  20. implicit def semigroup: Semigroup[M]
  21. def unit(c: C): M
  22. /** Faster `append(m, unit(c))`. */
  23. def snoc(m: M, c: C): M
  24. /** Faster `append(unit(c), m)`. */
  25. def cons(c: C, m: M): M
  26. def append(a1: M, a2: => M): M =
  27. semigroup.append(a1, a2)
  28. /** Distribute `C`s to `M` and `N`. */
  29. def compose[N](r: Reducer[C, N]): Reducer[C, (M, N)] = {
  30. implicit val m = Reducer.this.semigroup
  31. implicit val n = r.semigroup
  32. new Reducer[C, (M, N)] {
  33. import std.tuple._
  34. val semigroup = Semigroup[(M, N)]
  35. override def unit(x: C) = (Reducer.this.unit(x), r.unit(x))
  36. override def snoc(p: (M, N), x: C) = (Reducer.this.snoc(p._1, x), r.snoc(p._2, x))
  37. override def cons(x: C, p: (M, N)) = (Reducer.this.cons(x, p._1), r.cons(x, p._2))
  38. }
  39. }
  40. def unfoldlOpt[B](seed: B)(f: B => Maybe[(B, C)]): Maybe[M] =
  41. defaultUnfoldlOpt(seed)(f)
  42. @inline private def defaultUnfoldlOpt[B](seed: B)(f: B => Maybe[(B, C)]): Maybe[M] = {
  43. @tailrec
  44. def rec(seed: B, acc: M): M = f(seed) match {
  45. case Just((b, c)) => rec(b, cons(c, acc))
  46. case _ => acc
  47. }
  48. f(seed) map { case (b, c) => rec(b, unit(c)) }
  49. }
  50. def unfoldl[B](seed: B)(f: B => Maybe[(B, C)])(implicit M: Monoid[M]): M =
  51. unfoldlOpt(seed)(f) getOrElse M.zero
  52. def unfoldrOpt[B](seed: B)(f: B => Maybe[(C, B)]): Maybe[M] =
  53. defaultUnfoldrOpt(seed)(f)
  54. @inline private def defaultUnfoldrOpt[B](seed: B)(f: B => Maybe[(C, B)]): Maybe[M] = {
  55. @tailrec
  56. def rec(acc: M, seed: B): M = f(seed) match {
  57. case Just((c, b)) => rec(snoc(acc, c), b)
  58. case _ => acc
  59. }
  60. f(seed) map { case (c, b) => rec(unit(c), b) }
  61. }
  62. def unfoldr[B](seed: B)(f: B => Maybe[(C, B)])(implicit M: Monoid[M]): M =
  63. unfoldrOpt(seed)(f) getOrElse M.zero
  64. trait ReducerLaw {
  65. def consCorrectness(c: C, m: M)(implicit E: Equal[M]): Boolean =
  66. E.equal(cons(c, m), append(unit(c), m))
  67. def snocCorrectness(m: M, c: C)(implicit E: Equal[M]): Boolean =
  68. E.equal(snoc(m, c), append(m, unit(c)))
  69. def unfoldlOptConsistency[B](seed: B, f: B => Maybe[(B, C)])(implicit E: Equal[M]): Boolean = {
  70. val g: ((Int, B)) => Maybe[((Int, B), C)] = { case (i, b) =>
  71. if(i > 0) f(b) map { case (b, c) => ((i-1, b), c) }
  72. else Maybe.empty
  73. }
  74. val limit = 4 // to prevent infinite unfolds
  75. Equal[Maybe[M]].equal(unfoldlOpt((limit, seed))(g), defaultUnfoldlOpt((limit, seed))(g))
  76. }
  77. def unfoldrOptConsistency[B](seed: B, f: B => Maybe[(C, B)])(implicit E: Equal[M]): Boolean = {
  78. val g: ((Int, B)) => Maybe[(C, (Int, B))] = { case (i, b) =>
  79. if(i > 0) f(b) map { case (c, b) => (c, (i-1, b)) }
  80. else Maybe.empty
  81. }
  82. val limit = 4 // to prevent infinite unfolds
  83. Equal[Maybe[M]].equal(unfoldrOpt((limit, seed))(g), defaultUnfoldrOpt((limit, seed))(g))
  84. }
  85. }
  86. def reducerLaw = new ReducerLaw {}
  87. }
  88. sealed abstract class UnitReducer[C, M] extends Reducer[C, M] {
  89. implicit def semigroup: Semigroup[M]
  90. def unit(c: C): M
  91. def snoc(m: M, c: C): M = semigroup.append(m, unit(c))
  92. def cons(c: C, m: M): M = semigroup.append(unit(c), m)
  93. override def unfoldlOpt[B](seed: B)(f: B => Maybe[(B, C)]): Maybe[M] =
  94. semigroup.unfoldlSumOpt(seed)(f(_) map { case (b, c) => (b, unit(c)) })
  95. override def unfoldrOpt[B](seed: B)(f: B => Maybe[(C, B)]): Maybe[M] =
  96. semigroup.unfoldrSumOpt(seed)(f(_) map { case (c, b) => (unit(c), b) })
  97. }
  98. object UnitReducer {
  99. /** Minimal `Reducer` derived from a semigroup and `unit`. */
  100. def apply[C, M](u: C => M)(implicit M: Semigroup[M]): Reducer[C, M] = new UnitReducer[C, M] {
  101. val semigroup = M
  102. def unit(c: C) = u(c)
  103. }
  104. }
  105. object Reducer extends ReducerInstances {
  106. /** Reducer derived from `unit`, `cons`, and `snoc`. Permits more
  107. * sharing than `UnitReducer.apply`.
  108. */
  109. def apply[C, M](u: C => M, cs: (C, M) => M, sc: (M, C) => M)(implicit M: Semigroup[M]): Reducer[C, M] =
  110. reducer(u, cs, sc)
  111. }
  112. sealed abstract class ReducerInstances {
  113. /** Collect `C`s into a list, in order. */
  114. implicit def ListReducer[C]: Reducer[C, List[C]] = {
  115. import std.list._
  116. unitConsReducer(_ :: Nil, _ :: _)
  117. }
  118. def ReverseListReducer[C]: Reducer[C, List[C]] = {
  119. import std.list._
  120. reducer(_ :: Nil, (c, cs) => cs :+ c, (cs, c) => c :: cs)
  121. }
  122. /** Collect `C`s into an ilist, in order. */
  123. implicit def IListReducer[C]: Reducer[C, IList[C]] = {
  124. unitConsReducer(_ :: INil(), _ :: _)
  125. }
  126. /** Collect `C`s into an NonEmptyList, in order. */
  127. implicit def NonEmptyListReducer[C]: Reducer[C, NonEmptyList[C]] = {
  128. unitConsReducer(NonEmptyList.nel(_, INil()), _ <:: _)
  129. }
  130. def ReverseNonEmptyListReducer[C]: Reducer[C, NonEmptyList[C]] = {
  131. reducer(NonEmptyList.nel(_, INil()), (c, cs) => NonEmptyList.nel(cs.head, cs.tail :+ c), (cs, c) => c <:: cs)
  132. }
  133. /** Collect `C`s into a stream, in order. */
  134. implicit def StreamReducer[C]: Reducer[C, Stream[C]] = {
  135. import std.stream._
  136. import Stream._
  137. unitLazyConsReducer(cons(_, empty): Stream[C], cons(_, _))
  138. }
  139. /** Ignore `C`s. */
  140. implicit def UnitReducer[C]: Reducer[C, Unit] = {
  141. import std.anyVal._
  142. unitReducer((_: C) => ())
  143. }
  144. /** Collect `C`s into a vector, in order. */
  145. implicit def VectorReducer[C]: Reducer[C, Vector[C]] = new Reducer[C, Vector[C]]{
  146. val semigroup: Semigroup[Vector[C]] = std.vector.vectorMonoid[C]
  147. def cons(c: C, m: Vector[C]) = c +: m
  148. def snoc(m: Vector[C], c: C) = m :+ c
  149. def unit(c: C) = Vector(c)
  150. }
  151. /** The "or" semigroup. */
  152. implicit val AnyReducer: Reducer[Boolean, Boolean] = {
  153. implicit val B = std.anyVal.booleanInstance.disjunction
  154. unitReducer(x => x)
  155. }
  156. import std.anyVal._
  157. /** The "and" semigroup. */
  158. implicit val AllReducer: Reducer[Boolean, Boolean @@ Conjunction] = unitReducer(b => Tag[Boolean, Conjunction](b))
  159. /** Accumulate endomorphisms. */
  160. implicit def EndoReducer[A]: Reducer[A => A, Endo[A]] = unitReducer(Endo(_))
  161. implicit def DualReducer[A: Semigroup]: Reducer[A, A @@ Tags.Dual] = unitReducer(Tags.Dual(_: A))(Dual.dualSemigroup[A])
  162. import Tags.{Multiplication, First, Last}
  163. implicit val IntProductReducer: Reducer[Int, Int @@ Multiplication] = unitReducer(i => Tag[Int, Multiplication](i))
  164. implicit val CharProductReducer: Reducer[Char, Char @@ Multiplication] = unitReducer(c => Tag[Char, Multiplication](c))
  165. implicit val ByteProductReducer: Reducer[Byte, Byte @@ Multiplication] = unitReducer(b => Tag[Byte, Multiplication](b))
  166. implicit val LongProductReducer: Reducer[Long, Long @@ Multiplication] = unitReducer(l => Tag[Long, Multiplication](l))
  167. implicit val ShortProductReducer: Reducer[Short, Short @@ Multiplication] = unitReducer(s => Tag[Short, Multiplication](s))
  168. implicit val BigIntProductReducer: Reducer[BigInt, BigInt @@ Multiplication] = {
  169. import std.math.bigInt._
  170. unitReducer(b => Tag[BigInt, Multiplication](b))
  171. }
  172. import std.option._
  173. implicit def FirstReducer[A]: Reducer[A, Option[A] @@ First] = unitReducer(a => Tag[Option[A], First](Some(a)))
  174. implicit def FirstOptionReducer[A]: Reducer[Option[A], Option[A] @@ First] = unitReducer(o => Tag[Option[A], First](o))
  175. implicit def LastReducer[A]: Reducer[A, Option[A] @@ Last] = unitReducer(a => Tag[Option[A], Last](Some(a)))
  176. implicit def LastOptionReducer[A]: Reducer[Option[A], Option[A] @@ Last] = unitReducer(o => Tag[Option[A], Last](o))
  177. /** Alias for [[scalaz.Reducer]]`.apply`. */
  178. def reducer[C, M](u: C => M, cs: (C, M) => M, sc: (M, C) => M)(implicit sm: Semigroup[M]): Reducer[C, M] =
  179. new Reducer[C, M] {
  180. val semigroup = sm
  181. def unit(c: C) = u(c)
  182. def snoc(m: M, c: C): M = sc(m, c)
  183. def cons(c: C, m: M): M = cs(c, m)
  184. }
  185. def foldReduce[F[_], A, B: Monoid](a: F[A])(implicit f: Foldable[F], r: Reducer[A, B]): B =
  186. f.foldMap(a)(r.unit)
  187. /** Alias for [[scalaz.UnitReducer]]`.apply`. */
  188. def unitReducer[C, M](u: C => M)(implicit mm: Semigroup[M]): Reducer[C, M] =
  189. new UnitReducer[C, M] {
  190. val semigroup = mm
  191. def unit(c: C) = u(c)
  192. }
  193. def unitConsReducer[C, M](u: C => M, cs: (C, M) => M)(implicit sm: Semigroup[M]): Reducer[C, M] = new Reducer[C, M] {
  194. val semigroup = sm
  195. def unit(c: C) = u(c)
  196. def snoc(m: M, c: C): M = sm.append(m, u(c))
  197. def cons(c: C, m: M): M = cs(c, m)
  198. override def unfoldr[B](seed: B)(f: B => Maybe[(C, B)])(implicit M: Monoid[M]): M = {
  199. import std.function._
  200. def go(s: B, f: B => Maybe[(C, B)]): Trampoline[M] = f(s) match {
  201. case Just((c, b)) => suspend(go(b, f)) map (m => cs(c, m))
  202. case _ => return_[Function0, M](M.zero)
  203. }
  204. go(seed, f).run
  205. }
  206. }
  207. def unitLazyConsReducer[C, M](u: C => M, cs: (C, => M) => M)(implicit sm: Semigroup[M]): Reducer[C, M] = new Reducer[C, M] {
  208. val semigroup = sm
  209. def unit(c: C) = u(c)
  210. def snoc(m: M, c: C): M = sm.append(m, u(c))
  211. def cons(c: C, m: M): M = cs(c, m)
  212. override def unfoldr[B](seed: B)(f: B => Maybe[(C, B)])(implicit M: Monoid[M]): M = {
  213. def unfold(s: B): M = f(s) match {
  214. case Just((a, r)) => cs(a, unfold(r))
  215. case _ => M.zero
  216. }
  217. unfold(seed)
  218. }
  219. }
  220. /** The reducer derived from any semigroup. Not implicit because it is
  221. * suboptimal for most reducer applications.
  222. */
  223. def identityReducer[M](implicit mm: Semigroup[M]): Reducer[M, M] =
  224. new Reducer[M, M] {
  225. def semigroup = mm
  226. def unit(c: M): M = c
  227. def cons(c: M, m: M): M = append(c, m)
  228. def snoc(m: M, c: M): M = append(m, c)
  229. override def unfoldlOpt[B](seed: B)(f: B => Maybe[(B, M)]): Maybe[M] =
  230. mm.unfoldlSumOpt(seed)(f)
  231. override def unfoldrOpt[B](seed: B)(f: B => Maybe[(M, B)]): Maybe[M] =
  232. mm.unfoldrSumOpt(seed)(f)
  233. }
  234. }