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
- All
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( ... ) @native()
-
final
def
eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
def
equals(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
def
finalize(): Unit
- Attributes
- protected[lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( classOf[java.lang.Throwable] )
-
final
def
getClass(): Class[_]
- 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( ... )
-
final
def
wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... ) @native()