Skip to content

Commit

Permalink
Merge pull request #4656 from typelevel/oscar/20240922_defer_recursivefn
Browse files Browse the repository at this point in the history
Add Defer.recursiveFn to aid in recursion
  • Loading branch information
johnynek authored Sep 25, 2024
2 parents fd7fb67 + e45b590 commit 5fc8ede
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 0 deletions.
23 changes: 23 additions & 0 deletions core/src/main/scala/cats/Defer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,29 @@ trait Defer[F[_]] extends Serializable {
lazy val res: F[A] = fn(defer(res))
res
}

/**
* Useful when you want a recursive function that returns F where
* F[_]: Defer. Examples include IO, Eval, or transformers such
* as EitherT or OptionT.
*
* example:
*
* val sumTo: Int => Eval[Int] =
* Defer[Eval].recursiveFn[Int, Int] { recur =>
*
* { i =>
* if (i > 0) recur(i - 1).map(_ + i)
* else Eval.now(0)
* }
* }
*/
def recursiveFn[A, B](fn: (A => F[B]) => (A => F[B])): A => F[B] =
new Function1[A, F[B]] { self =>
val loopFn: A => F[B] = fn(self)

def apply(a: A): F[B] = defer(loopFn(a))
}
}

object Defer {
Expand Down
13 changes: 13 additions & 0 deletions tests/shared/src/test/scala/cats/tests/EvalSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -297,4 +297,17 @@ class EvalSuite extends CatsSuite {
assert(n2 == 1)
}
}

test("test Defer.recursiveFn example") {
val sumTo: Int => Eval[Int] =
cats.Defer[Eval].recursiveFn[Int, Int] { recur =>

{ i =>
if (i > 0) recur(i - 1).map(_ + i)
else Eval.now(0)
}
}

assert(sumTo(300000).value == (0 to 300000).sum)
}
}

0 comments on commit 5fc8ede

Please sign in to comment.