All Downloads are FREE. Search and download functionalities are using the official Maven repository.

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