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

org.specs2.foldm.FoldM.scala Maven / Gradle / Ivy

There is a newer version: 4.10.6
Show newest version
package org.specs2
package foldm

import scalaz.{Apply, Functor, Profunctor, Monoid, Compose, Monad, Category, MonadPlus, ~>}
import scalaz.effect.IO
import scalaz.Id._
import scalaz._
import scalaz.syntax.monad._
import FoldM._

/**
 * A FoldM is a "left fold" over a data structure with:
 *  - a 'start' value
 *  - a 'fold' method to accumulate state
 *  - an 'end' method to finalize the result
 *  
 * Both 'start' and 'end' have an effect which allows the whole folding to take place inside a context M. 
 * 
 * If 'M' has an 'Apply' instance then FoldM can be made Applicative to allow the folding of two values U and S at 
 * the same time.
 * 
 * If 'M' has a 'Monad' instance then FoldM can be made into a 'Compose' instance which allows to 
 * compose 2 folds into one, for example:
 * 
 *  - 'sum' computes the sum of some elements
 *  - 'list' accumulates all the elements in a list
 *  - the 'sum compose list' will accumulate the list of all the sums over some elements (this is a 'scan')
 *
 * A FoldM can be used with a 'FoldableM' which produces the elements to fold over. Examples of FoldableM include
 * 
 *  - a List
 *  - an Iterator
 *  - a scalaz Process
 *  
 * Usage example:
 * 
 *  sum.run(List(1, 2, 3)) == 6 
 */
trait FoldM[T, M[_], U] { self =>
  type S

  def start: M[S]
  def fold: (S, T) => S
  def end(s: S): M[U]

  /** map the output value */
  def map[V](f: U => V)(implicit m: Functor[M]) = new FoldM[T, M, V] {
    type S = self.S
    def start = self.start
    def fold = self.fold
    def end(s: S) = m.map(self.end(s))(f)
  }

  /** flatMap the output value */
  def mapFlatten[V](f: U => M[V])(implicit m: Bind[M]) = new FoldM[T, M, V] {
    type S = self.S
    def start = self.start
    def fold = self.fold
    def end(s: S) = m.bind(self.end(s))(f)
  }

  /** map with another fold */
  def pipe[V](f: FoldM[U, M, V])(implicit m: Bind[M]) = new FoldM[T, M, V] {
    type S = self.S
    def start = self.start
    def fold = self.fold
    def end(s: S) = m.bind(self.end(s))(f.run1)
  }

  /** parallel composition */
  def ***[V, W](f: FoldM[V, M, W])(implicit m: Bind[M]) = new FoldM[(T, V), M, (U, W)] {
    type S = (self.S, f.S)
    def start = m.tuple2(self.start, f.start)
    def fold = (s: S, tv: (T, V)) => (self.fold(s._1, tv._1), f.fold(s._2, tv._2))
    def end(s: S) = m.tuple2(self.end(s._1), f.end(s._2))
  }

  /** first operator on a MonadPlus monad */
  def first[V](implicit m: MonadPlus[M]) =
    ***(idM[M, V])

  /** second operator on a MonadPlus monad */
  def second[V](implicit m: MonadPlus[M]) =
    idM[M, V] *** self

  /** first-like operator */
  def firstOption[V](implicit m: Bind[M], nat: Id ~> M) =
    ***(FoldId.last[V].into[M])

  /** second-like operator */
  def secondOption[V](implicit m: Bind[M], nat: Id ~> M) =
    FoldId.last[V].into[M] *** self

  /** fanout = zip in the Arrow terminology */
  def &&&[V](f: FoldM[T, M, V])(implicit ap: Apply[M]) =
    zip(f)

  /** contramap the input values */
  def contramap[R](f: R => T)(implicit m: Functor[M]) = new FoldM[R, M, U] {
    type S = self.S
    def start = self.start
    def fold = (s: S, r: R) => self.fold(s, f(r))
    def end(s: S) = self.end(s)
  }

  /** zip 2 folds to return a pair of values. alias for zip */
  def <*>[V](f: FoldM[T, M, V])(implicit ap: Apply[M]) =
    zip(f)

  /** zip 2 folds to return a pair of values. alias for <*> */
  def zip[V](f: FoldM[T, M, V])(implicit ap: Apply[M]) = new FoldM[T, M, (U, V)] {
    type S = (self.S, f.S)
    def start = ap.tuple2(self.start, f.start)
    def fold = (s, t) => (self.fold(s._1, t), f.fold(s._2, t))
    def end(s: S) = ap.tuple2(self.end(s._1), f.end(s._2))
  }

  /** zip with another fold only for its side effects */
  def <*[V](f: SinkM[T, M])(implicit ap: Apply[M]) =
    zip(f).map(_._1)

  /** zip on the right with another fold only for self side-effects */
  def *>[V](f: FoldM[T, M, V])(implicit ap: Apply[M], ev: U <:< Unit) =
    zip(f).map(_._2)

  /** alias for <* */
  def observe[V](f: SinkM[T, M])(implicit ap: Apply[M]) =
    zip(f).map(_._1)

  /** observe both the input value and the current state */
  def observeState[V](sink: SinkM[(S, T), M])(implicit ap: Apply[M]) = new FoldM[T, M, U] {
    type S = (self.S, sink.S)
    def start = ap.tuple2(self.start , sink.start)
    def fold = (s: S, t: T) => (self.fold(s._1, t), sink.fold(s._2, (s._1, t)))
    def end(s: S) = ap.tuple2(self.end(s._1), sink.end(s._2)).map(_._1)
  }

  /** alias for observeState */
  def <<*[V](sink: SinkM[(S, T), M])(implicit ap: Apply[M]) =
    observeState(sink)

  /** observe both the input value and the next state */
  def observeNextState[V](sink: SinkM[(S, T), M])(implicit ap: Apply[M]) = new FoldM[T, M, U] {
    type S = (self.S, sink.S)
    def start = ap.tuple2(self.start , sink.start)
    def fold = (s: S, t: T) => { val next = self.fold(s._1, t); (next, sink.fold(s._2, (next, t))) }
    def end(s: S) = ap.tuple2(self.end(s._1), sink.end(s._2)).map(_._1)
  }

  /** alias for observeNextState */
  def <<<*[V](sink: SinkM[(S, T), M])(implicit ap: Apply[M]) =
    observeNextState(sink)

  /** add an effectful action at the beginning of the fold */
  def startWith(action: M[Unit])(implicit ap: Apply[M]) =  new FoldM[T, M, U] {
     type S = self.S
     def start = action *> self.start
     def fold = self.fold
     def end(s: S) = self.end(s)
  }

  /** add an effectful action at the end of the fold */
  def endWith(action: M[Unit])(implicit ap: Apply[M]) = new FoldM[T, M, U] {
     type S = self.S
     def start = self.start
     def fold = self.fold
     def end(s: S) = self.end(s) <* action
  }

  /** pipe the output of this fold into another fold */
  def compose[V](f2: FoldM[U, M, V])(implicit m: Monad[M]) = new FoldM[T, M, V] {
    type S = M[(self.S, f2.S)]
    def start = Monad[M].point(Apply[M].tuple2(self.start, f2.start))

    def fold = (s, t) =>
      s.flatMap { case (f1s, f2s) =>
        self.end(self.fold(f1s, t)).map((u: U) => (self.fold(f1s, t), f2.fold(f2s, u)))
      }

    def end(s: S) = s.flatMap { case (f1s, f2s) =>
      f2.end(f2s)
    }
  }
  
  /** create a fold that will run this fold repeatedly on input elements and collect all results */
  def nest[F[_], R](f: R => F[T])(implicit df: FoldableM[F, M], monoid: Monoid[U], monad: Monad[M]) = new FoldM[R, M, U] {
    type S = M[U]
    def start = monad.point(monad.point(monoid.zero)) 
    def fold = (s: S, r: R) =>
        Monad[M].apply2(self.run(f(r)): M[U], s: M[U])((s1: U, s2: U) => monoid.append(s1, s2))
        
    def end(s: S) = s
  }

  /**
   * run a FoldM with a FoldableM instance (like a List, an Iterator, a scalaz Process)
   */
   def run[F[_]](ft: F[T])(implicit foldableM: FoldableM[F, M]): M[U] =
     foldableM.foldM(ft)(this)

  /**
   * run a FoldM with a FoldableM instance (like a List, an Iterator, a scalaz Process)
   * and break early if possible
   */
  def runBreak[F[_]](ft: F[T])(implicit foldableM: FoldableM[F, M]): M[U] =
    foldableM.foldMBreak(ft)(self.asInstanceOf[FoldM[T, M, U] { type S = U \/ U }])

  /**
   * run a FoldM with a FoldableMS instance (like an InputStream which is specialized on producing Array[Byte])
   */
   def runS[F](f: F)(implicit foldableMS: FoldableMS[T, F, M]): M[U] =
     foldableMS.foldM(f)(this)

  /**
   * run over one element
   */
  def run1(t: T)(implicit m: Bind[M]): M[U] =
    m.bind(start)(s => end(fold(s, t)))

  /**
   * use a natural transformation to go from context M to context N
   * this can be used to transform a FoldM[A, Id, B] into a FoldM[A, Task, B] for example
   * (a fold with no effects to a fold with monadic effects from the Task monad)  
   */
  def into[N[_]](implicit nat: M ~> N) = new FoldM[T, N, U] {
    type S = self.S
    def start = nat(self.start)
    def fold = (s, t) => self.fold(s, t)
    def end(s: S) = nat(self.end(s))
  }

  /** equivalent of the as method for functors, added here for easier type inference */
  def as[V](v: =>V)(implicit m: Functor[M]) =
    map(_ => v)

  /** equivalent of the void method for functors, added here for easier type inference */
  def void(implicit m: Functor[M]) =
    as(())
}

/**
 * Typeclass instances and creation methods for folds
 */
object FoldM {

  /** alias for a non-effectful Fold */
  type Fold[T, U] = FoldM[T, Id, U]

  /** alias for a non-effectful Fold where the state type is U */
  type FoldState[T, U] = FoldM[T, Id, U] { type S = U }

  /** alias for a Fold sinking its last value */
  type SinkM[T, M[_]] = FoldM[T, M, Unit]

  /** @return a fold which uses a Monoid to accumulate elements */
  def fromMonoidMap[T, M : Monoid](f: T => M) = new Fold[T, M] {
    type S = M
    def start = Monoid[M].zero
    def fold = (s: S, t: T) => Monoid[M].append(s, f(t))
    def end(s: S) = s
  }

  /** @return a fold from arguments of a fold left */
  def fromFoldLeft[T, U](u: U)(f: (U, T) => U) = new Fold[T, U] {
    type S = U
    def start = u
    def fold = f
    def end(s: S) = s
  }

  /** @return a fold from a Monoid */
  def fromMonoid[M : Monoid] =
    fromMonoidMap[M, M](identity _)

  /** @return a fold from a Reducer */
  def fromReducer[T, S](reducer: Reducer[T, S]) =
    fromFoldLeft(reducer.monoid.zero)((s: S, t: T) => reducer.cons(t, s))

    /** @return a fold from running a State object */
  def fromStateRun[T, M[_]: Monad, U, V](state: T => State[U, V])(init: U) = new FoldM[T, M, (U, Option[V])] {
    type S = (U, Option[V])
    def start = Monad[M].point((init, None))
    def fold = (s: S, t: T) => {
      val (st, v) = s
      val (newState, newV) = state(t).run(st)
      (newState, Some(newV))
    }
    def end(s: S) = Monad[M].point(s)
  }

  /** @return a fold for the execution of a State object */
  def fromStateExec[T, M[_]: Monad, U, V](state: T => State[U, V])(init: U) =
    fromStateRun(state)(init)(Monad[M]).map(_._1)

  /** @return a fold for the evaluation of a State object */
  def fromStateEval[T, M[_]: Monad, U, V](state: T => State[U, V])(init: U) =
    fromStateRun(state)(init)(Monad[M]).map(_._2)

  /** @return a fold with just a start action */
  def fromStart[T, M[_]: Monad, S1](action: M[S1]) = new FoldM[T, M, S1] {
    type S = S1
    def start = action
    def fold = (s: S, t: T) => s
    def end(s: S) = Monad[M].point(s)
  }

  /**
   * Typeclass instances
   */
  
  /** 
   * Apply instance
   *  
   * This means that we can write:
   *  
   *   val mean: Fold[Int, Int] = (sum |@| count)(_ / _)
   *   
   * An Apply instance is also a Functor instance so we can write:
   *  
   *   val meanTimes2 = mean.map(_ * 2)   
   */
  implicit def FoldMApply[T, M[_] : Apply]: Apply[({type l[A]=FoldM[T, M, A]})#l] = new Apply[({type l[A]=FoldM[T, M, A]})#l] {
    type F[U] = FoldM[T, M, U]

    def map[A, B](fa: F[A])(f: A => B): FoldM[T, M, B] = 
      fa map f

    def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] =
      map(fa zip f) { case (a, b) => b(a) }
  }

  /** 
   *  Profunctor instance 
   *  
   *  This is especially useful because we can "map" on the input element
   *  
   *  val doubleSum = fromMonoid[Double] // sum all elements
   *  val roundedDoubleSum = doubleSum.mapfst(_.round)
   */
  implicit def FoldMProfunctor[M[_] : Functor]: Profunctor[({type l[A,B]=FoldM[A, M, B]})#l] = new Profunctor[({type l[A,B]=FoldM[A, M, B]})#l] {
    type =>:[T, U] = FoldM[T, M, U]

    /** Contramap on `A`. */
    def mapfst[A, B, C](fab: (A =>: B))(f: C => A): (C =>: B) =
      fab.contramap(f)

    /** Functor map on `B`. */
    def mapsnd[A, B, C](fab: (A =>: B))(f: B => C): (A =>: C) =
      fab map f
  }

  /**
   * A FoldM can be turned into a Category if M has a MonadPlus instance
   */
  def FoldMCategory[M[_] : MonadPlus]: Category[({type l[A,B]=FoldM[A, M, B]})#l] = new Category[({type l[A,B]=FoldM[A, M, B]})#l] {
    type F[A,B] = FoldM[A, M, B]

    def id[A] = idM[M, A]
    def compose[A, B, C](f: F[B, C], g: F[A, B]): F[A, C] =
      FoldMCompose[M].compose(f, g)
  }

  /** monoid to append sinks effects */
  implicit def SinkMMonoid[T, M[_] : Monad]: Monoid[SinkM[T, M]] = new Monoid[SinkM[T, M]] {
    def zero = unitSink

    def append(a1: SinkM[T, M], a2: => SinkM[T, M]): SinkM[T, M] =
      (a1 zip a2).void
  }

  /** sink doing nothing */
  def unitSink[T, M[_]: Monad] = new FoldM[T, M, Unit] {
    type S = Unit
    def start = Monad[M].point(())
    def fold = (s: S, t: T) => s
    def end(s: S) = Monad[M].point(())
  }

  /** identity fold for a MonadPlus monad */
  def idM[M[_] : MonadPlus, A] = new FoldM[A, M, A] {
    type S = M[A]
    def start: M[M[A]] = Monad[M].point(MonadPlus[M].empty[A])
    def fold = (s: S, a: A) => Monad[M].point(a)
    def end(a: M[A]) = a
  }

  /**
   * A FoldM can be turned into a Compose if M has a Monad instance
   * 
   * This allows us to write:
   * 
   * val scans = sum compose list
   * 
   */
  implicit def FoldMCompose[M[_] : Monad]: Compose[({type l[A,B]=FoldM[A, M, B]})#l] = new Compose[({type l[A,B]=FoldM[A, M, B]})#l] {
    type F[A,B] = FoldM[A, M, B]

    def compose[A, B, C](f: F[B, C], g: F[A, B]): F[A, C] = 
      g compose f
  }

  /** 
   * Cobind instance
   */
  def FoldMCobind[T, M[_] : Monad]: Cobind[({type l[A]=FoldM[T, M, A]})#l] = new Cobind[({type l[A]=FoldM[T, M, A]})#l] {
    type F[U] = FoldM[T, M, U]

    def cobind[A, B](fa: F[A])(f: F[A] => B): F[B] = new F[B] {
      type S = fa.S
      def start = fa.start
      def fold = fa.fold
      def end(s: S) = Monad[M].point(f(fa))
    }

    def map[A, B](fa: F[A])(f: A => B): FoldM[T, M, B] = 
      fa map f
  }

  /** 
   * Comonad instance for Fold
   */
  implicit def FoldComonad[T]: Comonad[({type l[A]=Fold[T, A]})#l] = new Comonad[({type l[A]=Fold[T, A]})#l] {
    type F[U] = Fold[T, U]

    def copoint[A](fa: F[A]): A = fa.end(fa.start)
    
    def cobind[A, B](fa: F[A])(f: F[A] => B): F[B] = new F[B] {
      type S = fa.S
      def start = fa.start
      def fold = fa.fold
      def end(s: S) = f(fa)
    }

    def map[A, B](fa: F[A])(f: A => B): Fold[T, B] = 
      fa map f
  }

  /** Natural transformation from Id to IO */
  implicit val IdIONaturalTransformation: Id ~> IO = new (Id ~> IO) {
    def apply[A](i: Id[A]): IO[A] = IO(i)
  }

  /** Natural transformation from a List to an Iterator */
  implicit val ListIteratorNaturalTransformation: List ~> Iterator = new (List ~> Iterator) {
    def apply[A](i: List[A]): Iterator[A] = i.iterator
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy