Skip to content

Commit

Permalink
Implement foldWhileM and takeWhileM foldable syntax
Browse files Browse the repository at this point in the history
  • Loading branch information
Odomontois committed Feb 19, 2020
1 parent 8ba96b3 commit 0917f40
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 0 deletions.
16 changes: 16 additions & 0 deletions core/src/main/scala/tofu/internal/FoldableStream.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package tofu.internal

import cats.Foldable
import cats.Eval

sealed trait FoldableStream[+A]

object FoldableStream {
def from[F[_], A](fa: F[A])(implicit F: Foldable[F]): FoldableStream[A] =
F.foldRight(fa, emptyE[A])((h, t) => Eval.always(Cons(h, t))).value

val emptyNothing: Eval[FoldableStream[Nothing]] = Eval.now(Empty)
def emptyE[A]: Eval[FoldableStream[A]] = emptyNothing
case object Empty extends FoldableStream[Nothing]
final case class Cons[+A](a: A, tail: Eval[FoldableStream[A]]) extends FoldableStream[A]
}
39 changes: 39 additions & 0 deletions core/src/main/scala/tofu/syntax/foldable.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package tofu.syntax

import cats.Foldable
import cats.Monad
import tofu.internal.FoldableStream

object foldable {
final implicit class TofuFoldableOps[F[_], A](private val fa: F[A]) extends AnyVal {

/**
* Applies monadic transfomation, feeding source collection,
* until operation results in None or collection is consumed
*
* @param initial initial state
* @param f state transformation, None would not be continued
* @return final achieved state or initial
*/
def foldWhileM[G[_], S](initial: S)(f: (S, A) => G[Option[S]])(implicit F: Foldable[F], G: Monad[G]): G[S] =
G.tailRecM((initial, FoldableStream.from(fa))) {
case (s, FoldableStream.Empty) => G.pure(Right(s))
case (s, FoldableStream.Cons(h, t)) =>
G.map(f(s, h)) {
case None => Right(s)
case Some(s1) => Left((s1, t.value))
}
}

/**
* transforms each element to another type using monadic transformation
* until it resutls in None
*
* @param f element transformation, None would not be continued
* @return a collection of transformed elements
*/
def takeWhileM[G[_], B](f: A => G[Option[B]])(implicit F: Foldable[F], G: Monad[G]): G[List[B]] =
G.map(foldWhileM(List.empty[B])((acc, a) => G.map(f(a))(_.map(acc.::))))(_.reverse)

}
}

0 comments on commit 0917f40

Please sign in to comment.