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

kyo.Var.scala Maven / Gradle / Ivy

The newest version!
package kyo

import Var.internal.*
import kyo.Tag
import kyo.kernel.*
import scala.annotation.nowarn

sealed trait Var[V] extends ArrowEffect[Const[Op[V]], Const[V]]

object Var:

    /** Obtains the current value of the 'Var'.
      *
      * @tparam V
      *   The type of the value stored in the Var
      * @return
      *   The current value of the Var
      */
    inline def get[V](using inline tag: Tag[Var[V]], inline frame: Frame): V < Var[V] =
        use[V](identity)

    final class UseOps[V](dummy: Unit) extends AnyVal:
        /** Invokes the provided function with the current value of the `Var`.
          *
          * @param f
          *   The function to apply to the current value
          * @tparam A
          *   The return type of the function
          * @tparam S
          *   Additional effects in the function
          * @return
          *   The result of applying the function to the current value
          */
        inline def apply[A, S](inline f: V => A < S)(
            using
            inline tag: Tag[Var[V]],
            inline frame: Frame
        ): A < (Var[V] & S) =
            ArrowEffect.suspendAndMap[V](tag, Get: Op[V])(f)
    end UseOps

    /** Creates a new UseOps instance for the given type V.
      *
      * @tparam V
      *   The type of the value stored in the Var
      * @return
      *   A new UseOps instance
      */
    inline def use[V]: UseOps[V] = UseOps(())

    /** Sets a new value and returns the previous one.
      *
      * @param value
      *   The new value to set
      * @tparam V
      *   The type of the value stored in the Var
      * @return
      *   The previous value of the Var
      */
    inline def set[V](inline value: V)(using inline tag: Tag[Var[V]], inline frame: Frame): V < Var[V] =
        ArrowEffect.suspend[Unit](tag, value: Op[V])

    /** Sets a new value and then executes another computation.
      *
      * @param value
      *   The new value to set in the Var
      * @param f
      *   The computation to execute after setting the value
      * @return
      *   The result of the computation after setting the new value
      */
    private[kyo] inline def setAndThen[V, A, S](inline value: V)(inline f: => A < S)(using
        inline tag: Tag[Var[V]],
        inline frame: Frame
    ): A < (Var[V] & S) =
        ArrowEffect.suspendAndMap[Unit](tag, value: Op[V])(_ => f)

    /** Sets a new value and returns `Unit`.
      *
      * @param value
      *   The new value to set
      * @tparam V
      *   The type of the value stored in the Var
      * @return
      *   Unit
      */
    inline def setDiscard[V](inline value: V)(using inline tag: Tag[Var[V]], inline frame: Frame): Unit < Var[V] =
        ArrowEffect.suspendAndMap[Unit](tag, value: Op[V])(_ => ())

    /** Applies the update function and returns the new value.
      *
      * @param f
      *   The update function to apply
      * @tparam V
      *   The type of the value stored in the Var
      * @return
      *   The new value after applying the update function
      */
    @nowarn("msg=anonymous")
    inline def update[V](inline f: V => V)(using inline tag: Tag[Var[V]], inline frame: Frame): V < Var[V] =
        ArrowEffect.suspend[V](tag, (v => f(v)): Update[V])

    /** Applies the update function and returns `Unit`.
      *
      * @param f
      *   The update function to apply
      * @tparam V
      *   The type of the value stored in the Var
      * @return
      *   Unit
      */
    @nowarn("msg=anonymous")
    inline def updateDiscard[V](inline f: V => V)(using inline tag: Tag[Var[V]], inline frame: Frame): Unit < Var[V] =
        ArrowEffect.suspendAndMap[Unit](tag, (v => f(v)): Update[V])(_ => ())

    private[kyo] inline def runWith[V, A: Flat, S, B, S2](state: V)(v: A < (Var[V] & S))(
        inline f: (V, A) => B < S2
    )(using inline tag: Tag[Var[V]], inline frame: Frame): B < (S & S2) =
        ArrowEffect.handleState(tag, state, v)(
            [C] =>
                (input, state, cont) =>
                    input match
                        case input: Get.type =>
                            (state, cont(state))
                        case input: Update[V] @unchecked =>
                            val nst = input(state)
                            (nst, cont(nst))
                        case input: V @unchecked =>
                            (input, cont(state)),
            done = f
        )

    /** Handles the effect and discards the 'Var' state.
      *
      * @param state
      *   The initial state of the Var
      * @param v
      *   The computation to run
      * @tparam V
      *   The type of the value stored in the Var
      * @tparam A
      *   The result type of the computation
      * @tparam S
      *   Additional effects in the computation
      * @return
      *   The result of the computation without the Var state
      */
    def run[V, A: Flat, S](state: V)(v: A < (Var[V] & S))(using Tag[Var[V]], Frame): A < S =
        runWith(state)(v)((_, result) => result)

    /** Handles the effect and returns a tuple with the final `Var` state and the computation's result.
      *
      * @param state
      *   The initial state of the Var
      * @param v
      *   The computation to run
      * @tparam V
      *   The type of the value stored in the Var
      * @tparam A
      *   The result type of the computation
      * @tparam S
      *   Additional effects in the computation
      * @return
      *   A tuple containing the final Var state and the result of the computation
      */
    def runTuple[V, A: Flat, S](state: V)(v: A < (Var[V] & S))(using Tag[Var[V]], Frame): (V, A) < S =
        runWith(state)(v)((state, result) => (state, result))

    object isolate:
        abstract private[kyo] class Base[V: Tag] extends Isolate[Var[V]]:
            type State = V
            def use[A, S2](f: V => A < S2)(using Frame) = Var.use(f)
            def resume[A: Flat, S2](state: State, v: A < (Var[V] & S2))(using Frame) =
                Var.runTuple(state)(v)
        end Base

        /** Creates an isolate that sets the Var to its final isolated value.
          *
          * When the isolation ends, unconditionally updates the Var with the last value it had in the isolated computation.
          *
          * @tparam V
          *   The type of value in the Var
          * @return
          *   An isolate that updates the Var with its isolated value
          */
        def update[V: Tag]: Isolate[Var[V]] =
            new Base[V]:
                def restore[A: Flat, S2](state: V, v: A < S2)(using Frame) =
                    Var.set(state).andThen(v)

        /** Creates an isolate that merges Var values using a combination function.
          *
          * When the isolation ends, combines the Var's current value with the value from the isolated computation using the provided merge
          * function. Useful when you want to reconcile isolated Var modifications with any concurrent changes to the same Var.
          *
          * @param f
          *   Function that combines outer and isolated Var values
          * @tparam V
          *   The type of value in the Var
          * @return
          *   An isolate that merges Var values
          */
        def merge[V: Tag](f: (V, V) => V): Isolate[Var[V]] =
            new Base[V]:
                def restore[A: Flat, S2](state: V, v: A < S2)(using Frame) =
                    Var.use[V](prev => Var.set(f(prev, state)).andThen(v))

        /** Creates an isolate that keeps Var modifications local.
          *
          * Allows the isolated computation to read and modify the Var freely, but discards all modifications when the isolation ends. The
          * Var retains its original value as if the isolated modifications never happened.
          *
          * @tparam V
          *   The type of value in the Var
          * @return
          *   An isolate that discards Var modifications
          */
        def discard[V: Tag]: Isolate[Var[V]] =
            new Base[V]:
                def restore[A: Flat, S2](state: V, v: A < S2)(using Frame) =
                    v

    end isolate

    object internal:
        type Op[V] = Get.type | V | Update[V]
        object Get
        abstract class Update[V]:
            def apply(v: V): V
    end internal

end Var




© 2015 - 2025 Weber Informatics LLC | Privacy Policy