sealed trait IOLocal[A] extends AnyRef
IOLocal provides a handy way of manipulating a context on different scopes.
In some scenarios, IOLocal can be considered as an alternative to cats.data.Kleisli.
IOLocal should not be treated as Ref, since the former abides different laws.
Once a fiber is forked, for example by Spawn[F].start
, the forked fiber manipulates the
copy of the parent's context. For example, two forked fibers will never see each other's
modifications to the same IOLocal, each fiber will only see its own modifications.
Operations on IOLocal are visible to the fiber
┌────────────┐ ┌────────────┐ ┌────────────┐ │ Fiber A │ update(_ + 1) │ Fiber A │ update(_ + 1) │ Fiber A │ │ (local 42) │──────────────►│ (local 43) │──────────────►│ (local 44) │ └────────────┘ └────────────┘ └────────────┘
def inc(name: String, local: IOLocal[Int]): IO[Unit] = local.update(_ + 1) >> local.get.flatMap(current => IO.println(s"fiber $$name: $$current")) for { local <- IOLocal(42) _ <- inc(1, local) _ <- inc(2, local) current <- local.get _ <- IO.println(s"fiber A: $$current") } yield () // output: // update 1: 43 // update 2: 44 // fiber A: 44
A forked fiber operates on a copy of the parent IOLocal
A forked fiber (i.e. via Spawn[F].start
) operates on a copy of the parent
IOLocal
. Hence, the children operations are not reflected on the parent context.
┌────────────┐ ┌────────────┐ fork │ Fiber B │ update(_ - 1) │ Fiber B │ ┌─────►│ (local 42) │──────────────►│ (local 41) │ │ └────────────┘ └────────────┘ ┌────────────┐─┘ ┌────────────┐ │ Fiber A │ │ Fiber A │ │ (local 42) │────────────────────────────────────►│ (local 42) │ └────────────┘─┐ └────────────┘ │ ┌────────────┐ ┌────────────┐ │ fork │ Fiber C │ update(_ + 1) │ Fiber C │ └─────►│ (local 42) │──────────────►│ (local 43) │ └────────────┘ └────────────┘
def update(name: String, local: IOLocal[Int], f: Int => Int): IO[Unit] = local.update(f) >> local.get.flatMap(current => IO.println(s"$$name: $$current")) for { local <- IOLocal(42) fiber1 <- update("fiber B", local, _ - 1).start fiber2 <- update("fiber C", local, _ + 1).start _ <- fiber1.joinWithNever _ <- fiber2.joinWithNever current <- local.get _ <- IO.println(s"fiber A: $$current") } yield () // output: // fiber B: 41 // fiber C: 43 // fiber A: 42
Parent operations on IOLocal are invisible to children
┌────────────┐ ┌────────────┐ fork │ Fiber B │ update(_ + 1) │ Fiber B │ ┌─────►│ (local 42) │──────────────►│ (local 43) │ │ └────────────┘ └────────────┘ ┌────────────┐─┘ ┌────────────┐ │ Fiber A │ update(_ - 1) │ Fiber A │ │ (local 42) │────────────────────────────────────►│ (local 41) │ └────────────┘─┐ └────────────┘ │ ┌────────────┐ ┌────────────┐ │ fork │ Fiber C │ update(_ + 2) │ Fiber C │ └─────►│ (local 42) │──────────────►│ (local 44) │ └────────────┘ └────────────┘
def update(name: String, local: IOLocal[Int], f: Int => Int): IO[Unit] = IO.sleep(1.second) >> local.update(f) >> local.get.flatMap(current => IO.println(s"$$name: $$current")) for { local <- IOLocal(42) fiber1 <- update("fiber B", local, _ + 1).start fiber2 <- update("fiber C", local, _ + 2).start _ <- fiber1.joinWithNever _ <- fiber2.joinWithNever _ <- update("fiber A", local, _ - 1) } yield () // output: // fiber B: 43 // fiber C: 44 // fiber A: 41
- A
the type of the local value
- Self Type
- IOLocal[A]
- Source
- IOLocal.scala
- Alphabetic
- By Inheritance
- IOLocal
- AnyRef
- Any
- Hide All
- Show All
- Public
- Protected
Abstract Value Members
- abstract def get: IO[A]
Returns the current value.
- abstract def getAndReset: IO[A]
Replaces the current value with the initial value, returning the previous value.
- abstract def getAndSet(value: A): IO[A]
Replaces the current value with
value
, returning the previous value. - abstract def modify[B](f: (A) => (A, B)): IO[B]
Like update but allows the update function to return an output value of type
B
. - abstract def reset: IO[Unit]
Replaces the current value with the initial value.
- abstract def set(value: A): IO[Unit]
Sets the current value to
value
. - abstract def update(f: (A) => A): IO[Unit]
Modifies the current value using the given update function.
Concrete Value Members
- final def !=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def ##: Int
- Definition Classes
- AnyRef → Any
- final def ==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
- final def asInstanceOf[T0]: T0
- Definition Classes
- Any
- def clone(): AnyRef
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.CloneNotSupportedException]) @native()
- final def eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- def equals(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef → Any
- def finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.Throwable])
- final def getClass(): Class[_ <: AnyRef]
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- def hashCode(): Int
- Definition Classes
- AnyRef → Any
- Annotations
- @native()
- final def isInstanceOf[T0]: Boolean
- Definition Classes
- Any
- final def lens[B](get: (A) => B)(set: (A) => (B) => A): IOLocal[B]
Creates a lens to a value of some type
B
from current value and two functions: getter and setter.Creates a lens to a value of some type
B
from current value and two functions: getter and setter.All changes to the original value will be visible via lens getter and all changes applied to 'refracted' value will be forwarded to the original via setter.
Note that .set method requires special mention: while from the
IOLocal[B]
point of view old value will be replaced with a new one, fromIOLocal[A]
POV old value will be updated via setter. This means that for 'refracted'IOLocal[B]
use ofset(b)
is equivalent toreset *> set(b)
, but it does not hold for originalIOLocal[A]
:update(a => set(setter(a)(b)) =!= (reset *> update(default => set(setter(default)(b)))
for { base <- IOLocal(42 -> "empty") lens = base.lens(_._1) { case (_, s) => i => (i, s) } _ <- lens.update(_ + 1) // `42` is visible in lens closure _ <- base.get // returns `(43, "empty")` _ <- base.set(1 -> "some") _ <- lens.set(42) _ <- base.get // returns `(42, "some")` } yield ()
Example: - final def ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
- final def notify(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def notifyAll(): Unit
- Definition Classes
- AnyRef
- Annotations
- @native()
- final def synchronized[T0](arg0: => T0): T0
- Definition Classes
- AnyRef
- def toString(): String
- Definition Classes
- AnyRef → Any
- final def wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException])
- final def wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws(classOf[java.lang.InterruptedException]) @native()