The origami project provides "Monadic folds" to process streams of data in a composable fashion. Monadic folds come in 2 flavors:
min, max, average, hash,...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:
M must have a Monad instanceA is the type of input elements, being fed one by one to the foldB is the final resultS is the type of some internal statestart is a method to "initialize" the foldend is a method to "finalize" the foldfold is the method called for each element A and current type SFolds 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 zip operator)saveToFile using the Safe monadThe 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).
Scala
72.0%
Shell
28.0%
The origami project provides "Monadic folds" to process streams of data in a composable fashion. Monadic folds come in 2 flavors:
min, max, average, hash,...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:
M must have a Monad instanceA is the type of input elements, being fed one by one to the foldB is the final resultS is the type of some internal statestart is a method to "initialize" the foldend is a method to "finalize" the foldfold is the method called for each element A and current type SFolds 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 zip operator)saveToFile using the Safe monadThe 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).
Scala
72.0%
Shell
28.0%