All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.Reducer.scala Maven / Gradle / Ivy

package scalaz

import scala.annotation.tailrec
import scalaz.Free.{Trampoline, return_, suspend}
import scalaz.Maybe.Just
import scalaz.Tags.Conjunction


/**
 * A `Reducer[C,M]` is a [[scalaz.Semigroup]]`[M]` that maps
 * values of type `C` through `unit` to values of type `M`. A `C-Reducer` may also
 * supply operations which tack on another `C` to an existing `Semigroup` `M` on the left
 * or right. These specialized reductions may be more efficient in some scenarios
 * and are used when appropriate by a [[scalaz.Generator]]. The names `cons` and `snoc` work
 * by analogy to the synonymous operations in the list semigroup.
 *
 * Minimal definition: `unit` or `snoc`
 *
 * Based on the Reducer Haskell library by Edward Kmett
 * (https://hackage.haskell.org/package/reducers).
 */
trait Reducer[C, M] {
  implicit def semigroup: Semigroup[M]

  def unit(c: C): M

  /** Faster `append(m, unit(c))`. */
  def snoc(m: M, c: C): M

  /** Faster `append(unit(c), m)`. */
  def cons(c: C, m: M): M

  def append(a1: M, a2: => M): M =
    semigroup.append(a1, a2)

  /** Distribute `C`s to `M` and `N`. */
  def compose[N](r: Reducer[C, N]): Reducer[C, (M, N)] = {
    new Reducer[C, (M, N)] {
      private[this] implicit val m: Semigroup[M] = Reducer.this.semigroup
      private[this] implicit val n: Semigroup[N] = r.semigroup
      import std.tuple._

      override val semigroup: Semigroup[(M, N)] = Semigroup[(M, N)]

      override def unit(x: C) = (Reducer.this.unit(x), r.unit(x))

      override def snoc(p: (M, N), x: C) = (Reducer.this.snoc(p._1, x), r.snoc(p._2, x))

      override def cons(x: C, p: (M, N)) = (Reducer.this.cons(x, p._1), r.cons(x, p._2))
    }
  }

  def unfoldlOpt[B](seed: B)(f: B => Maybe[(B, C)]): Maybe[M] =
    defaultUnfoldlOpt(seed)(f)

  @inline private def defaultUnfoldlOpt[B](seed: B)(f: B => Maybe[(B, C)]): Maybe[M] = {
    @tailrec
    def rec(seed: B, acc: M): M = f(seed) match {
      case Just((b, c)) => rec(b, cons(c, acc))
      case _ => acc
    }
    f(seed) map { case (b, c) => rec(b, unit(c)) }
  }

  def unfoldl[B](seed: B)(f: B => Maybe[(B, C)])(implicit M: Monoid[M]): M =
    unfoldlOpt(seed)(f) getOrElse M.zero

  def unfoldrOpt[B](seed: B)(f: B => Maybe[(C, B)]): Maybe[M] =
    defaultUnfoldrOpt(seed)(f)

  @inline private def defaultUnfoldrOpt[B](seed: B)(f: B => Maybe[(C, B)]): Maybe[M] = {
    @tailrec
    def rec(acc: M, seed: B): M = f(seed) match {
      case Just((c, b)) => rec(snoc(acc, c), b)
      case _ => acc
    }
    f(seed) map { case (c, b) => rec(unit(c), b) }
  }

  def unfoldr[B](seed: B)(f: B => Maybe[(C, B)])(implicit M: Monoid[M]): M =
    unfoldrOpt(seed)(f) getOrElse M.zero

  trait ReducerLaw {
    def consCorrectness(c: C, m: M)(implicit E: Equal[M]): Boolean =
      E.equal(cons(c, m), append(unit(c), m))

    def snocCorrectness(m: M, c: C)(implicit E: Equal[M]): Boolean =
      E.equal(snoc(m, c), append(m, unit(c)))

    def unfoldlOptConsistency[B](seed: B, f: B => Maybe[(B, C)])(implicit E: Equal[M]): Boolean = {
      val g: ((Int, B)) => Maybe[((Int, B), C)] = { case (i, b) =>
        if(i > 0) f(b) map { case (b, c) => ((i-1, b), c) }
        else Maybe.empty
      }
      val limit = 4 // to prevent infinite unfolds
      Equal[Maybe[M]].equal(unfoldlOpt((limit, seed))(g), defaultUnfoldlOpt((limit, seed))(g))
    }

    def unfoldrOptConsistency[B](seed: B, f: B => Maybe[(C, B)])(implicit E: Equal[M]): Boolean = {
      val g: ((Int, B)) => Maybe[(C, (Int, B))] = { case (i, b) =>
        if(i > 0) f(b) map { case (c, b) => (c, (i-1, b)) }
        else Maybe.empty
      }
      val limit = 4 // to prevent infinite unfolds
      Equal[Maybe[M]].equal(unfoldrOpt((limit, seed))(g), defaultUnfoldrOpt((limit, seed))(g))
    }
  }
  def reducerLaw: ReducerLaw = new ReducerLaw {}
}
sealed abstract class UnitReducer[C, M] extends Reducer[C, M] {
  implicit def semigroup: Semigroup[M]
  def unit(c: C): M

  def snoc(m: M, c: C): M = semigroup.append(m, unit(c))

  def cons(c: C, m: M): M = semigroup.append(unit(c), m)

  override def unfoldlOpt[B](seed: B)(f: B => Maybe[(B, C)]): Maybe[M] =
    semigroup.unfoldlSumOpt(seed)(f(_) map { case (b, c) => (b, unit(c)) })

  override def unfoldrOpt[B](seed: B)(f: B => Maybe[(C, B)]): Maybe[M] =
    semigroup.unfoldrSumOpt(seed)(f(_) map { case (c, b) => (unit(c), b) })
}

object UnitReducer {
  /** Minimal `Reducer` derived from a semigroup and `unit`. */
  def apply[C, M](u: C => M)(implicit M: Semigroup[M]): Reducer[C, M] = new UnitReducer[C, M] {
    val semigroup = M
    def unit(c: C) = u(c)
  }
}

object Reducer extends ReducerInstances {
  /** Reducer derived from `unit`, `cons`, and `snoc`.  Permits more
    * sharing than `UnitReducer.apply`.
    */
  def apply[C, M](u: C => M, cs: (C, M) => M, sc: (M, C) => M)(implicit M: Semigroup[M]): Reducer[C, M] =
    reducer(u, cs, sc)
}

sealed abstract class ReducerInstances {

  /** Collect `C`s into a list, in order. */
  implicit def ListReducer[C]: Reducer[C, List[C]] = {
    import std.list._
    unitConsReducer(_ :: Nil, _ :: _)
  }

  def ReverseListReducer[C]: Reducer[C, List[C]] = {
    import std.list._
    reducer(_ :: Nil, (c, cs) => cs :+ c, (cs, c) => c :: cs)
  }

  /** Collect `C`s into an ilist, in order. */
  implicit def IListReducer[C]: Reducer[C, IList[C]] = {
    unitConsReducer(_ :: INil(), _ :: _)
  }

  /** Collect `C`s into an NonEmptyList, in order. */
  implicit def NonEmptyListReducer[C]: Reducer[C, NonEmptyList[C]] = {
    unitConsReducer(NonEmptyList.nel(_, INil()),  _ <:: _)
  }

  def ReverseNonEmptyListReducer[C]: Reducer[C, NonEmptyList[C]] = {
    reducer(NonEmptyList.nel(_, INil()), (c, cs) => NonEmptyList.nel(cs.head, cs.tail :+ c), (cs, c) => c <:: cs)
  }

  /** Collect `C`s into a LazyList, in order. */
  implicit def LazyListReducer[C]: Reducer[C, LazyList[C]] = {
    import std.lazylist._
    unitLazyConsReducer(LazyList.cons(_, LazyList.empty), LazyList.cons(_, _))
  }

  /** Collect `C`s into a stream, in order. */
  implicit def StreamReducer[C]: Reducer[C, Stream[C]] = {
    import std.stream._
    import Stream._
    unitLazyConsReducer(cons(_, empty): Stream[C], cons(_, _))
  }

  /** Ignore `C`s. */
  implicit def UnitReducer[C]: Reducer[C, Unit] = {
    import std.anyVal._
    unitReducer((_: C) => ())
  }

  /** Collect `C`s into a vector, in order. */
  implicit def VectorReducer[C]: Reducer[C, Vector[C]] = new Reducer[C, Vector[C]]{
    val semigroup: Semigroup[Vector[C]] = std.vector.vectorMonoid[C]
    def cons(c: C, m: Vector[C]) = c +: m
    def snoc(m: Vector[C], c: C) = m :+ c
    def unit(c: C) = Vector(c)
  }

  /** The "or" semigroup. */
  implicit val AnyReducer: Reducer[Boolean, Boolean] = {
    implicit val B = std.anyVal.booleanInstance.disjunction
    unitReducer(x => x)
  }

  import std.anyVal._

  /** The "and" semigroup. */
  implicit val AllReducer: Reducer[Boolean, Boolean @@ Conjunction] = unitReducer(b => Tag[Boolean, Conjunction](b))

  /** Accumulate endomorphisms. */
  implicit def EndoReducer[A]: Reducer[A => A, Endo[A]] = unitReducer(Endo(_))

  implicit def DualReducer[A: Semigroup]: Reducer[A, A @@ Tags.Dual] = unitReducer(Tags.Dual(_: A))(Dual.dualSemigroup[A])

  import Tags.{Multiplication, First, Last}

  implicit val IntProductReducer: Reducer[Int, Int @@ Multiplication] = unitReducer(i => Tag[Int, Multiplication](i))

  implicit val CharProductReducer: Reducer[Char, Char @@ Multiplication] = unitReducer(c => Tag[Char, Multiplication](c))

  implicit val ByteProductReducer: Reducer[Byte, Byte @@ Multiplication] = unitReducer(b => Tag[Byte, Multiplication](b))

  implicit val LongProductReducer: Reducer[Long, Long @@ Multiplication] = unitReducer(l => Tag[Long, Multiplication](l))

  implicit val ShortProductReducer: Reducer[Short, Short @@ Multiplication] = unitReducer(s => Tag[Short, Multiplication](s))


  implicit val BigIntProductReducer: Reducer[BigInt, BigInt @@ Multiplication] = {
    import std.math.bigInt._
    unitReducer(b => Tag[BigInt, Multiplication](b))
  }

  import std.option._

  implicit def FirstReducer[A]: Reducer[A, Option[A] @@ First] = unitReducer(a => Tag[Option[A], First](Some(a)))

  implicit def FirstOptionReducer[A]: Reducer[Option[A], Option[A] @@ First] = unitReducer(o => Tag[Option[A], First](o))

  implicit def LastReducer[A]: Reducer[A, Option[A] @@ Last] = unitReducer(a => Tag[Option[A], Last](Some(a)))

  implicit def LastOptionReducer[A]: Reducer[Option[A], Option[A] @@ Last] = unitReducer(o => Tag[Option[A], Last](o))

  /** Alias for [[scalaz.Reducer]]`.apply`. */
  def reducer[C, M](u: C => M, cs: (C, M) => M, sc: (M, C) => M)(implicit sm: Semigroup[M]): Reducer[C, M] =
    new Reducer[C, M] {
      val semigroup = sm

      def unit(c: C) = u(c)

      def snoc(m: M, c: C): M = sc(m, c)

      def cons(c: C, m: M): M = cs(c, m)
    }

  def foldReduce[F[_], A, B: Monoid](a: F[A])(implicit f: Foldable[F], r: Reducer[A, B]): B =
    f.foldMap(a)(r.unit)

  /** Alias for [[scalaz.UnitReducer]]`.apply`. */
  def unitReducer[C, M](u: C => M)(implicit mm: Semigroup[M]): Reducer[C, M] =
    new UnitReducer[C, M] {
      val semigroup = mm
      def unit(c: C) = u(c)
    }

  def unitConsReducer[C, M](u: C => M, cs: (C, M) => M)(implicit sm: Semigroup[M]): Reducer[C, M] = new Reducer[C, M] {
    val semigroup = sm

    def unit(c: C) = u(c)

    def snoc(m: M, c: C): M = sm.append(m, u(c))

    def cons(c: C, m: M): M = cs(c, m)

    override def unfoldr[B](seed: B)(f: B => Maybe[(C, B)])(implicit M: Monoid[M]): M = {
      import  std.function._
      def go(s: B, f: B => Maybe[(C, B)]): Trampoline[M] = f(s) match {
        case Just((c, b)) => suspend(go(b, f)) map (m => cs(c, m))
        case _ => return_[Function0, M](M.zero)
      }
      go(seed, f).run
    }
  }

  def unitLazyConsReducer[C, M](u: C => M, cs: (C, => M) => M)(implicit sm: Semigroup[M]): Reducer[C, M] = new Reducer[C, M] {
    val semigroup = sm

    def unit(c: C) = u(c)

    def snoc(m: M, c: C): M = sm.append(m, u(c))

    def cons(c: C, m: M): M = cs(c, m)

    override def unfoldr[B](seed: B)(f: B => Maybe[(C, B)])(implicit M: Monoid[M]): M = {
      def unfold(s: B): M = f(s) match {
        case Just((a, r)) => cs(a, unfold(r))
        case _ => M.zero
      }
      unfold(seed)
    }
  }

  /** The reducer derived from any semigroup.  Not implicit because it is
    * suboptimal for most reducer applications.
    */
  def identityReducer[M](implicit mm: Semigroup[M]): Reducer[M, M] =
    new Reducer[M, M] {
      def semigroup = mm
      def unit(c: M): M = c
      def cons(c: M, m: M): M = append(c, m)
      def snoc(m: M, c: M): M = append(m, c)

      override def unfoldlOpt[B](seed: B)(f: B => Maybe[(B, M)]): Maybe[M] =
        mm.unfoldlSumOpt(seed)(f)

      override def unfoldrOpt[B](seed: B)(f: B => Maybe[(M, B)]): Maybe[M] =
        mm.unfoldrSumOpt(seed)(f)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy