PageRenderTime 39ms CodeModel.GetById 16ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

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