kyo.Emit.scala Maven / Gradle / Ivy
The newest version!
package kyo
import kyo.Tag
import kyo.kernel.*
/** The Emit effect allows emitting values of type V during a computation.
*
* Emit can be used to produce a stream of values alongside the main result of a computation. Values are emitted using [[Emit.apply]] and
* can be collected or processed using various run methods like [[Emit.run]] or [[Emit.runFold]].
*
* @tparam V
* The type of values that can be emitted
*/
sealed trait Emit[V] extends ArrowEffect[Const[V], Const[Ack]]
object Emit:
/** Emits a single value.
*
* @param value
* The value to emit
* @return
* An effect that emits the given value
*/
inline def apply[V](inline value: V)(using inline tag: Tag[Emit[V]], inline frame: Frame): Ack < Emit[V] =
ArrowEffect.suspend[Any](tag, value)
/** Emits a single value and maps the resulting Ack.
*
* @param value
* The value to emit
* @param f
* A function to apply to the resulting Ack
* @return
* The result of applying f to the Ack
*/
inline def andMap[V, A, S](inline value: V)(inline f: Ack => A < S)(
using
inline tag: Tag[Emit[V]],
inline frame: Frame
): A < (S & Emit[V]) =
ArrowEffect.suspendAndMap[Any](tag, value)(f(_))
final class RunOps[V](dummy: Unit) extends AnyVal:
/** Runs an Emit effect, collecting all emitted values into a Chunk.
*
* @param v
* The computation with Emit effect
* @return
* A tuple of the collected values and the result of the computation
*/
def apply[A: Flat, S](v: A < (Emit[V] & S))(using tag: Tag[Emit[V]], frame: Frame): (Chunk[V], A) < S =
ArrowEffect.handleState(tag, Chunk.empty[V], v)(
handle = [C] => (input, state, cont) => (state.append(input), cont(Ack.Continue())),
done = (state, res) => (state, res)
)
end RunOps
inline def run[V >: Nothing]: RunOps[V] = RunOps(())
final class RunFoldOps[V](dummy: Unit) extends AnyVal:
/** Runs an Emit effect, folding over the emitted values.
*
* @param acc
* The initial accumulator value
* @param f
* The folding function that takes the current accumulator and emitted value, and returns a tuple of the new accumulator and an
* Ack to control further emissions
* @param v
* The computation with Emit effect
* @return
* A tuple of the final accumulator value and the result of the computation
*/
def apply[A, S, B: Flat, S2](acc: A)(f: (A, V) => (A, Ack) < S)(v: B < (Emit[V] & S2))(
using
tag: Tag[Emit[V]],
frame: Frame
): (A, B) < (S & S2) =
ArrowEffect.handleState(tag, acc, v)(
handle = [C] =>
(input, state, cont) =>
f(state, input).map((a, ack) => (a, cont(ack))),
done = (state, res) => (state, res)
)
end RunFoldOps
inline def runFold[V >: Nothing]: RunFoldOps[V] = RunFoldOps(())
final class RunDiscardOps[V](dummy: Unit) extends AnyVal:
/** Runs an Emit effect, discarding all emitted values.
*
* @param v
* The computation with Emit effect
* @return
* The result of the computation, discarding emitted values
*/
def apply[A: Flat, S](v: A < (Emit[V] & S))(using tag: Tag[Emit[V]], frame: Frame): A < S =
ArrowEffect.handle(tag, v)(
handle = [C] => (input, cont) => cont(Ack.Stop)
)
end RunDiscardOps
inline def runDiscard[V >: Nothing]: RunDiscardOps[V] = RunDiscardOps(())
final class RunAckOps[V](dummy: Unit) extends AnyVal:
/** Runs an Emit effect, allowing custom handling of each emitted value via an Ack.
*
* @param v
* The computation with Emit effect
* @param f
* A function to process each emitted value and return an Ack
* @return
* The result of the computation
*/
def apply[A: Flat, S, S2](v: A < (Emit[V] & S))(f: V => Ack < S2)(using tag: Tag[Emit[V]], frame: Frame): A < (S & S2) =
ArrowEffect.handle(tag, v)(
[C] => (input, cont) => f(input).map(cont)
)
end RunAckOps
inline def runAck[V >: Nothing]: RunAckOps[V] = RunAckOps(())
final class RunFirstOps[V](dummy: Unit) extends AnyVal:
/** Runs an Emit effect, capturing only the first emitted value and returning a continuation.
*
* @param v
* The computation with Emit effect
* @return
* A tuple containing:
* - Maybe[V]: The first emitted value if any (None if no values were emitted)
* - A continuation function that takes an Ack and returns the remaining computation
*/
def apply[A: Flat, S](v: A < (Emit[V] & S))(using tag: Tag[Emit[V]], frame: Frame): (Maybe[V], Ack => A < (Emit[V] & S)) < S =
ArrowEffect.handleFirst(tag, v)(
handle = [C] =>
(input, cont) =>
// Effect found, return the input an continuation
(Maybe(input), cont),
done = r =>
// Effect not found, return empty input and a placeholder continuation
// that returns the result of the computation
(Maybe.empty[V], (_: Ack) => r: A < (Emit[V] & S))
)
end apply
end RunFirstOps
inline def runFirst[V >: Nothing]: RunFirstOps[V] = RunFirstOps(())
object isolate:
/** Creates an isolate that includes emitted values from isolated computations.
*
* When the isolation ends, appends all values emitted during the isolated computation to the outer context. The values are emitted
* in their original order.
*
* @tparam V
* The type of values being emitted
* @return
* An isolate that preserves emitted values
*/
def merge[V: Tag]: Isolate[Emit[V]] =
new Isolate[Emit[V]]:
type State = Chunk[V]
def use[A, S2](f: Chunk[V] => A < S2)(using Frame) = f(Chunk.empty)
def resume[A: Flat, S2](state: Chunk[V], v: A < (Emit[V] & S2))(using Frame) =
Emit.run(v)
def restore[A: Flat, S2](state: Chunk[V], v: A < S2)(using Frame) =
Loop(state: Seq[V]) {
case Seq() => Loop.done
case head +: tail =>
Emit.andMap(head) {
case Ack.Stop => Loop.done
case _ => Loop.continue(tail)
}
}.andThen(v)
end restore
/** Creates an isolate that ignores emitted values.
*
* Allows the isolated computation to emit values freely, but discards all emissions when the isolation ends. Useful when you want
* to prevent emissions from propagating to the outer context.
*
* @tparam V
* The type of values being emitted
* @return
* An isolate that discards emitted values
*/
def discard[V: Tag]: Isolate[Emit[V]] =
new Isolate[Emit[V]]:
type State = Chunk[V]
def use[A, S2](f: Chunk[V] => A < S2)(using Frame) = f(Chunk.empty)
def resume[A: Flat, S2](state: Chunk[V], v: A < (Emit[V] & S2))(using Frame) =
Emit.run(v)
def restore[A: Flat, S2](state: Chunk[V], v: A < S2)(using Frame) =
v
end isolate
end Emit
© 2015 - 2025 Weber Informatics LLC | Privacy Policy