kyo.kernel.Loop.scala Maven / Gradle / Ivy
package kyo.kernel
import internal.*
import kyo.Flat
import kyo.Frame
import scala.annotation.nowarn
import scala.annotation.tailrec
import scala.annotation.targetName
import scala.util.NotGiven
/** Provides utilities for creating and managing iterative computations with effects.
*
* While Kyo already provides stack-safe recursion through its core functionality, Loop offers a more performant and ergonomic way to write
* iterative computations that can perform effects between iterations. It manages state between iterations and provides control over when
* to continue or terminate the loop.
*
* Loops can maintain multiple state values (up to 4) between iterations through Continue variants. This enables complex stateful
* computations while maintaining type safety and pure functional semantics.
*
* The outcome of each iteration is represented by an Outcome type, which can either signal continuation with new state values or
* completion with a final result.
*/
object Loop:
/** Represents the state to be carried forward to the next iteration of a loop.
*
* @tparam A
* The type of the single state value maintained between iterations
*/
abstract class Continue[A]:
private[Loop] def _1: A
/** Represents the state of two values to be carried forward to the next iteration.
*
* @tparam A
* The type of the first state value
* @tparam B
* The type of the second state value
*/
abstract class Continue2[A, B]:
private[Loop] def _1: A
private[Loop] def _2: B
/** Represents the state of three values to be carried forward to the next iteration.
*
* @tparam A
* The type of the first state value
* @tparam B
* The type of the second state value
* @tparam C
* The type of the third state value
*/
abstract class Continue3[A, B, C]:
private[Loop] def _1: A
private[Loop] def _2: B
private[Loop] def _3: C
end Continue3
/** Represents the state of four values to be carried forward to the next iteration.
*
* @tparam A
* The type of the first state value
* @tparam B
* The type of the second state value
* @tparam C
* The type of the third state value
* @tparam D
* The type of the fourth state value
*/
abstract class Continue4[A, B, C, D]:
private[Loop] def _1: A
private[Loop] def _2: B
private[Loop] def _3: C
private[Loop] def _4: D
end Continue4
given [A, O: Flat]: Flat[Outcome[A, O]] = Flat.unsafe.bypass
given [A, B, O: Flat]: Flat[Outcome2[A, B, O]] = Flat.unsafe.bypass
given [A, B, C, O: Flat]: Flat[Outcome3[A, B, C, O]] = Flat.unsafe.bypass
given [A, B, C, D, O: Flat]: Flat[Outcome4[A, B, C, D, O]] = Flat.unsafe.bypass
/** Represents the result of a loop iteration, which can either continue with new state or complete with a final value.
*
* @tparam A
* The type of the state value if continuing
* @tparam O
* The type of the final value if completing
*/
opaque type Outcome[A, O] = O | Continue[A]
/** Represents the result of a loop iteration with two state values.
*
* @tparam A
* The type of the first state value if continuing
* @tparam B
* The type of the second state value if continuing
* @tparam O
* The type of the final value if completing
*/
opaque type Outcome2[A, B, O] = O | Continue2[A, B]
/** Represents the result of a loop iteration with three state values.
*
* @tparam A
* The type of the first state value if continuing
* @tparam B
* The type of the second state value if continuing
* @tparam C
* The type of the third state value if continuing
* @tparam O
* The type of the final value if completing
*/
opaque type Outcome3[A, B, C, O] = O | Continue3[A, B, C]
/** Represents the result of a loop iteration with four state values.
*
* @tparam A
* The type of the first state value if continuing
* @tparam B
* The type of the second state value if continuing
* @tparam C
* The type of the third state value if continuing
* @tparam D
* The type of the fourth state value if continuing
* @tparam O
* The type of the final value if completing
*/
opaque type Outcome4[A, B, C, D, O] = O | Continue4[A, B, C, D]
private val _continueUnit: Continue[Unit] =
new Continue:
def _1 = ()
/** Creates an outcome signaling continuation with no state value.
*
* This is a convenience method for continuing a loop without maintaining any state between iterations. It's particularly useful for
* simple loops that only need to track iteration progress.
*
* @return
* An Outcome indicating continuation with Unit state
*/
inline def continue[A]: Outcome[Unit, A] = _continueUnit
/** Creates an outcome signaling continuation with a single state value.
*
* @param v
* The state value to continue with
*/
@nowarn("msg=anonymous")
inline def continue[A, O, S](inline v: A): Outcome[A, O] =
new Continue:
def _1 = v
/** Creates an outcome signaling continuation with two state values.
*
* @param v1
* The first state value
* @param v2
* The second state value
*/
@nowarn("msg=anonymous")
inline def continue[A, B, o](inline v1: A, inline v2: B): Outcome2[A, B, o] =
new Continue2:
def _1 = v1
def _2 = v2
/** Creates an outcome signaling continuation with three state values.
*
* @param v1
* The first state value
* @param v2
* The second state value
* @param v3
* The third state value
*/
@nowarn("msg=anonymous")
inline def continue[A, B, C, O](inline v1: A, inline v2: B, inline v3: C): Outcome3[A, B, C, O] =
new Continue3:
def _1 = v1
def _2 = v2
def _3 = v3
/** Creates an outcome signaling continuation with four state values.
*
* @param v1
* The first state value
* @param v2
* The second state value
* @param v3
* The third state value
* @param v4
* The fourth state value
*/
@nowarn("msg=anonymous")
inline def continue[A, B, C, D, O](inline v1: A, inline v2: B, inline v3: C, inline v4: D): Outcome4[A, B, C, D, O] =
new Continue4:
def _1 = v1
def _2 = v2
def _3 = v3
def _4 = v4
/** Creates an outcome signaling completion with no value. */
@targetName("done0")
def done[A]: Outcome[A, Unit] = ()
/** Creates an outcome signaling completion with a final value.
*
* @param v
* The final value
*/
@targetName("done1")
def done[A, O](v: O): Outcome[A, O] = v
/** Creates an outcome signaling completion with a final value for a two-state loop.
*
* @param v
* The final value
*/
@targetName("done2")
def done[A, B, O](v: O): Outcome2[A, B, O] = v
/** Creates an outcome signaling completion with a final value for a three-state loop.
*
* @param v
* The final value
*/
@targetName("done3")
def done[A, B, C, O](v: O): Outcome3[A, B, C, O] = v
/** Creates an outcome signaling completion with a final value for a four-state loop.
*
* @param v
* The final value
*/
@targetName("done4")
def done[A, B, C, D, O](v: O): Outcome4[A, B, C, D, O] = v
/** Executes a loop with a single state value.
*
* This method runs an iterative computation that maintains one state value between iterations. Each iteration can either continue with
* a new state value or complete with a final result. The loop continues until an iteration produces a completion outcome.
*
* @param input
* The initial state value
* @param run
* The function to execute for each iteration, receiving the current state and producing an outcome
* @return
* The final result after loop completion
*/
inline def apply[A, O, S](inline input: A)(inline run: Safepoint ?=> A => Outcome[A, O] < S)(
using
inline _frame: Frame,
safepoint: Safepoint
): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(i1: A)(v: Outcome[A, O] < S = run(i1))(using Safepoint): O < S =
v match
case next: Continue[A] @unchecked =>
loop(next._1)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome[A, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(i1)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(input)()
end apply
/** Executes a loop with two state values.
*
* Similar to the single-state version, but maintains two independent state values between iterations. This enables more complex
* stateful computations while keeping the values properly typed and separated.
*
* @param input1
* The first initial state value
* @param input2
* The second initial state value
* @param run
* The function to execute for each iteration, receiving both current states and producing an outcome
* @return
* The final result after loop completion
*/
inline def apply[A, B, O, S](input1: A, input2: B)(inline run: Safepoint ?=> (A, B) => Outcome2[A, B, O] < S)(
using
inline _frame: Frame,
safepoint: Safepoint
): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(i1: A, i2: B)(v: Outcome2[A, B, O] < S = run(i1, i2))(using Safepoint): O < S =
v match
case next: Continue2[A, B] @unchecked =>
loop(next._1, next._2)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome2[A, B, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(i1, i2)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(input1, input2)()
end apply
/** Executes a loop with three state values.
*
* Maintains three independent state values between iterations, allowing for even more complex stateful computations while preserving
* type safety and separation of concerns.
*
* @param input1
* The first initial state value
* @param input2
* The second initial state value
* @param input3
* The third initial state value
* @param run
* The function to execute for each iteration, receiving all current states and producing an outcome
* @return
* The final result after loop completion
*/
inline def apply[A, B, C, O, S](input1: A, input2: B, input3: C)(
inline run: Safepoint ?=> (A, B, C) => Outcome3[A, B, C, O] < S
)(using inline _frame: Frame, safepoint: Safepoint): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(i1: A, i2: B, i3: C)(v: Outcome3[A, B, C, O] < S = run(i1, i2, i3))(using Safepoint): O < S =
v match
case next: Continue3[A, B, C] @unchecked =>
loop(next._1, next._2, next._3)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome3[A, B, C, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(i1, i2, i3)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(input1, input2, input3)()
end apply
/** Executes a loop with four state values.
*
* The most complex variant, maintaining four independent state values between iterations. This enables sophisticated stateful
* computations while keeping all state values properly typed and organized.
*
* @param input1
* The first initial state value
* @param input2
* The second initial state value
* @param input3
* The third initial state value
* @param input4
* The fourth initial state value
* @param run
* The function to execute for each iteration, receiving all current states and producing an outcome
* @return
* The final result after loop completion
*/
inline def apply[A, B, C, D, O, S](input1: A, input2: B, input3: C, input4: D)(
inline run: Safepoint ?=> (A, B, C, D) => Outcome4[A, B, C, D, O] < S
)(using inline _frame: Frame, safepoint: Safepoint): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(i1: A, i2: B, i3: C, i4: D)(v: Outcome4[A, B, C, D, O] < S = run(i1, i2, i3, i4))(using Safepoint): O < S =
v match
case next: Continue4[A, B, C, D] @unchecked =>
loop(next._1, next._2, next._3, next._4)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome4[A, B, C, D, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(i1, i2, i3, i4)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(input1, input2, input3, input4)()
end apply
/** Executes an indexed loop without state values.
*
* This method runs an iterative computation that maintains a counter between iterations. Each iteration receives the current index and
* can either continue to the next index or complete with a final result. The loop continues until an iteration produces a completion
* outcome.
*
* @param run
* The function to execute for each iteration, receiving the current index and producing an outcome
* @return
* The final result after loop completion
*/
inline def indexed[O, S](inline run: Int => Outcome[Unit, O] < S)(using
inline _frame: Frame,
safepoint: Safepoint
): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(idx: Int)(v: Outcome[Unit, O] < S = run(idx))(using Safepoint): O < S =
v match
case next: Continue[Unit] @unchecked =>
loop(idx + 1)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome[Unit, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(idx)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(0)()
end indexed
/** Executes an indexed loop with a single state value.
*
* Similar to the standard indexed loop, but also maintains one state value between iterations. Each iteration receives both the
* current index and state value, enabling more complex stateful computations with index tracking.
*
* @param input
* The initial state value
* @param run
* The function to execute for each iteration, receiving the current index and state, and producing an outcome
* @return
* The final result after loop completion
*/
inline def indexed[A, O, S](input: A)(inline run: Safepoint ?=> (Int, A) => Outcome[A, O] < S)(using
inline _frame: Frame,
safepoint: Safepoint
): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(idx: Int, i1: A)(v: Outcome[A, O] < S = run(idx, i1))(using Safepoint): O < S =
v match
case next: Continue[A] @unchecked =>
loop(idx + 1, next._1)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome[A, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(idx, i1)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(0, input)()
end indexed
/** Executes an indexed loop with two state values.
*
* Maintains two independent state values between iterations along with an index counter. This enables complex stateful computations
* that need to track iteration count while managing multiple state values.
*
* @param input1
* The first initial state value
* @param input2
* The second initial state value
* @param run
* The function to execute for each iteration, receiving the current index and both states
* @return
* The final result after loop completion
*/
inline def indexed[A, B, O, S](input1: A, input2: B)(
inline run: Safepoint ?=> (Int, A, B) => Outcome2[A, B, O] < S
)(using inline _frame: Frame, safepoint: Safepoint): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(idx: Int, i1: A, i2: B)(v: Outcome2[A, B, O] < S = run(idx, i1, i2))(using Safepoint): O < S =
v match
case next: Continue2[A, B] @unchecked =>
loop(idx + 1, next._1, next._2)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome2[A, B, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(idx, i1, i2)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(0, input1, input2)()
end indexed
/** Executes an indexed loop with three state values.
*
* Maintains three independent state values between iterations along with an index counter. This enables sophisticated stateful
* computations that need to track iteration count while managing multiple state values.
*
* @param input1
* The first initial state value
* @param input2
* The second initial state value
* @param input3
* The third initial state value
* @param run
* The function to execute for each iteration, receiving the current index and all states
* @return
* The final result after loop completion
*/
inline def indexed[A, B, C, O, S](input1: A, input2: B, input3: C)(
inline run: Safepoint ?=> (Int, A, B, C) => Outcome3[A, B, C, O] < S
)(using inline _frame: Frame, safepoint: Safepoint): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(idx: Int, i1: A, i2: B, i3: C)(v: Outcome3[A, B, C, O] < S = run(idx, i1, i2, i3))(using Safepoint): O < S =
v match
case next: Continue3[A, B, C] @unchecked =>
loop(idx + 1, next._1, next._2, next._3)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome3[A, B, C, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(idx, i1, i2, i3)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(0, input1, input2, input3)()
end indexed
/** Executes an indexed loop with four state values.
*
* The most complex indexed variant, maintaining four independent state values between iterations along with an index counter. This
* enables highly sophisticated stateful computations that need to track iteration count while managing multiple state values.
*
* @param input1
* The first initial state value
* @param input2
* The second initial state value
* @param input3
* The third initial state value
* @param input4
* The fourth initial state value
* @param run
* The function to execute for each iteration, receiving the current index and all states
* @return
* The final result after loop completion
*/
inline def indexed[A, B, C, D, O, S](input1: A, input2: B, input3: C, input4: D)(
inline run: Safepoint ?=> (Int, A, B, C, D) => Outcome4[A, B, C, D, O] < S
)(using inline _frame: Frame, safepoint: Safepoint): O < S =
@nowarn("msg=anonymous")
@tailrec def loop(idx: Int, i1: A, i2: B, i3: C, i4: D)(v: Outcome4[A, B, C, D, O] < S =
run(idx, i1, i2, i3, i4))(using Safepoint): O < S =
v match
case next: Continue4[A, B, C, D] @unchecked =>
loop(idx + 1, next._1, next._2, next._3, next._4)()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome4[A, B, C, D, O], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, O, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(idx, i1, i2, i3, i4)(kyo(v, context))
case res =>
res.asInstanceOf[O]
loop(0, input1, input2, input3, input4)()
end indexed
/** Executes a loop that continues until explicitly completed.
*
* This method runs a loop that will continue executing until an iteration produces a completion outcome. It's useful for loops that
* need to run indefinitely or until some condition is met.
*
* @param run
* The function to execute repeatedly until completion
* @return
* Unit after the loop completes
*/
inline def foreach[S](inline run: Safepoint ?=> Outcome[Unit, Unit] < S)(using inline _frame: Frame, safepoint: Safepoint): Unit < S =
@nowarn("msg=anonymous")
@tailrec def loop(v: Outcome[Unit, Unit] < S = run)(using Safepoint): Unit < S =
v match
case next: Continue[Unit] @unchecked =>
loop()
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome[Unit, Unit], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, Unit, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(kyo(v, context))
case res =>
()
loop()
end foreach
/** Repeats an operation a specified number of times.
*
* A simpler looping construct that executes an operation exactly n times. Unlike other loop variants, this doesn't maintain state or
* require explicit continuation/completion decisions.
*
* @param n
* The number of times to repeat the operation
* @param run
* The operation to repeat
* @return
* Unit after completing all iterations
*/
inline def repeat[S](n: Int)(inline run: Safepoint ?=> Unit < S)(using inline _frame: Frame, safepoint: Safepoint): Unit < S =
@nowarn("msg=anonymous")
@tailrec def loop(i: Int)(v: Outcome[Unit, Unit] < S = run)(using Safepoint): Unit < S =
if i == n then ()
else
v match
case kyo: KyoSuspend[IX, OX, EX, Any, Outcome[Unit, Unit], S] @unchecked =>
new KyoContinue[IX, OX, EX, Any, Unit, S](kyo):
def frame = _frame
def apply(v: OX[Any], context: Context)(using Safepoint) =
loop(i)(kyo(v, context))
end new
case _ =>
loop(i + 1)()
end if
end loop
loop(0)()
end repeat
/** Executes a loop indefinitely until explicitly terminated.
*
* Creates an infinite loop that will continue executing until interrupted by some other means (like an effect handler). This is useful
* for creating long-running processes or servers that should run continuously.
*
* @param run
* The function to execute repeatedly
* @return
* Nothing, as this loop runs forever unless interrupted
*/
inline def forever[S](inline run: Safepoint ?=> Unit < S)(using Frame, Safepoint): Unit < S =
def _loop(): Unit < S = loop()
@tailrec def loop(): Unit < S =
run match
case kyo: Kyo[Unit, S] @unchecked =>
kyo.andThen(_loop())
case _ =>
loop()
loop()
end forever
end Loop