atnos-org / origami

Monadic folds
MIT License
70 stars 8 forks source link

Origami

Join the chat at https://gitter.im/atnos-org/origami Build Status

fold

The origami project provides "Monadic folds" to process streams of data in a composable fashion. Monadic folds come in 2 flavors:

The general form of a Fold is

trait Fold[M[_], A, B] {
  type S

  def start: M[S]
  def fold: (S, A) => M[S]
  def end(s: S): M[B]
}

where:

Folds can be composed to produce "larger" folds, doing several things at the same time. For example:

import org.atnos.origami._
import org.atnos.origami.fold._
import org.atnos.origami.folds._
import org.atnos.origami.syntax.foldable._
import cats.Eval
import cats.data.EitherT
import cats.implicits._
import java.io.PrintWriter

type Safe[A] = EitherT[Eval, Throwable, A]
def protect[A](a: =>A): Safe[A] = EitherT.right(Eval.later(a))

def saveToFile(path: String): Sink[Safe, Int] =
  bracket(
    // create a new writer
    protect(new PrintWriter(path)))(

    // write a new line in the file
    (w, i: Int) => protect { w.write(s"i=$i\n"); w })(

    // close the writer
    w => protect(w.close))

val stats: Fold[Safe, Int, ((Int, Int), Double)] =
  (minimumOr(0) <*> maximumOr(Int.MaxValue) <*> averageDouble).into[Safe] <*
    saveToFile("target/readme-example")

val elements = (1 to 10).toList

elements.foldWith(stats).value.value

In the example above we create a stats fold composed from:

The Safe monad is necessary here to use the bracket combinator which creates a Fold acquiring resources at the beginning of the run and eventually release them. It needs both the ability to deal with errors (with EitherT) and to delay computations (with Eval).