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

kyo.Local.scala Maven / Gradle / Ivy

The newest version!
package kyo

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

/** Represents a local value that can be accessed and modified within a specific scope.
  *
  * Local provides a way to manage thread-local-like state in a functional manner. There are two types of locals: regular and isolated.
  *
  * Regular locals behave similarly to inheritable thread locals, where child fibers inherit the value from their parent fiber. Isolated
  * locals, on the other hand, are similar to non-inheritable thread locals, where child fibers always start with the default value.
  *
  * @tparam A
  *   The type of the local value
  */
abstract class Local[A]:
    /** The default value for this Local. */
    def default: A

    /** Retrieves the current value of this Local.
      *
      * @return
      *   An effect that produces the current value
      */
    def get(using Frame): A < Any

    /** Applies a function to the current value of this Local.
      *
      * @param f
      *   The function to apply to the local value
      * @return
      *   An effect that produces the result of applying the function
      */
    def use[B, S](f: A => B < S)(using Frame): B < S

    /** Runs an effect with a temporarily modified local value.
      *
      * @param value
      *   The temporary value to use
      * @param v
      *   The effect to run with the modified value
      * @return
      *   The result of running the effect with the modified value
      */
    def let[B, S](value: A)(v: B < S)(using Frame): B < S

    /** Runs an effect with an updated local value.
      *
      * @param f
      *   The function to update the local value
      * @param v
      *   The effect to run with the updated value
      * @return
      *   The result of running the effect with the updated value
      */
    def update[B, S](f: A => A)(v: B < S)(using Frame): B < S
end Local

/** Companion object for Local, providing utility methods for creating Local instances. */
object Local:

    /** Creates a new regular Local instance with the given default value.
      *
      * Regular locals are similar to inheritable thread locals, where child fibers inherit the value from their parent fiber.
      *
      * @param defaultValue
      *   The default value for the Local
      * @return
      *   A new regular Local instance
      */
    @nowarn("msg=anonymous")
    inline def init[A](inline defaultValue: A): Local[A] =
        new Base[A, State]:
            def tag             = Tag[State]
            lazy val default: A = defaultValue

    /** Creates a new isolated Local instance with the given default value.
      *
      * Isolated locals are similar to non-inheritable thread locals, where child fibers always start with the default value and do not
      * inherit from their parent fiber.
      *
      * @param defaultValue
      *   The default value for the Local
      * @return
      *   A new isolated Local instance
      */
    @nowarn("msg=anonymous")
    inline def initIsolated[A](inline defaultValue: A): Local[A] =
        new Base[A, IsolatedState]:
            def tag             = Tag[IsolatedState]
            lazy val default: A = defaultValue

    object internal:

        sealed private[kyo] trait State         extends ContextEffect[Map[Local[?], AnyRef]]
        sealed private[kyo] trait IsolatedState extends ContextEffect[Map[Local[?], AnyRef]] with ContextEffect.Isolated

        abstract class Base[A, E <: ContextEffect[Map[Local[?], AnyRef]]] extends Local[A]:

            def tag: Tag[E]

            def get(using Frame) =
                ContextEffect.suspendAndMap(tag, Map.empty)(_.getOrElse(this, default).asInstanceOf[A])

            def use[B, S](f: A => B < S)(using Frame) =
                ContextEffect.suspendAndMap(tag, Map.empty)(map => f(map.getOrElse(this, default).asInstanceOf[A]))

            def let[B, S](value: A)(v: B < S)(using Frame) =
                ContextEffect.handle(tag, Map(this -> value), _.updated(this, value.asInstanceOf[AnyRef]))(v)

            def update[B, S](f: A => A)(v: B < S)(using Frame) =
                ContextEffect.handle(
                    tag,
                    Map(this -> f(default)),
                    map => map.updated(this, f(map.getOrElse(this, default).asInstanceOf[A]).asInstanceOf[AnyRef])
                )(v)
        end Base
    end internal

end Local




© 2015 - 2025 Weber Informatics LLC | Privacy Policy