Suppose I have a mechanism for long-running computations that can suspend themselves to be resumed later:
sealed trait LongRunning[+R]; case class Result[+R](result: R) extends LongRunning[R]; case class Suspend[+R](cont: () => LongRunning[R]) extends LongRunning[R]; The simplest way how to run them is
@annotation.tailrec def repeat[R](body: LongRunning[R]): R = body match { case Result(r) => r case Suspend(c) => { // perhaps do some other processing here println("Continuing suspended computation"); repeat(c()); } } The problem is creating such computations. Let's say we want to implement tail-recursive factorial that suspends its computation every 10 cycles:
@annotation.tailrec def factorial(n: Int, acc: BigInt): LongRunning[BigInt] = { if (n <= 1) Result(acc); else if (n % 10 == 0) Suspend(() => factorial(n - 1, acc * n)) else factorial(n - 1, acc * n) } But this does not compile:
error: could not optimize
@tailrecannotated methodfactorial: it contains a recursive call not in tail positionSuspend(() => factorial(n - 1, acc * n))
How to retain tail recursion on the non-suspending calls?
LongRunningis the partiality monad!LongRunningis a simplification of my original problem - I'm working on a conduit-like library for Scala scala-conduit wherePipenaturally forms a monad.Free[() => _, R]orFree[ChunkOfData => _, R]makes little fundamental difference.