Skip to main content
Version: 2.x

Ref.Synchronized

Ref.Synchronized[A] models a mutable reference to a value of type A in which we can store immutable data, and update it atomically and effectfully.

note

Almost all of Ref.Synchronized operations are the same as Ref. We suggest reading Ref at first if you are not familiar with Ref.

Let's explain how we can update a shared state effectfully with Ref.Synchronized. The update method and all other related methods get an effectful operation, and then they run these effects to change the shared state. This is the main difference between Ref.Synchronized and Ref.

In the following example, we should pass in updateEffect to it which is the description of an update operation. So Ref.Synchronized is going to update the ref by running the updateEffect:

import zio._
for {
ref <- Ref.Synchronized.make("current")
updateEffect = ZIO.succeed("update")
_ <- ref.updateZIO(_ => updateEffect)
value <- ref.get
} yield assert(value == "update")

In real-world applications, there are cases where we want to run an effect, e.g. query a database, and then update the shared state. This is where Ref.Synchronized can help us to update the shared state in a more actor model fashion. We have a shared mutable state but for every different command or message, and we want execute our effect and update the state.

We can pass in an effectful program into every single update. All of them will be done parallel, but the result will be sequenced in such a fashion that they only touched the state at different times, and we end up with a consistent state at the end.

In the following example, we are going to send getAge request to usersApi for each user and updating the state respectively:

val meanAge =
for {
ref <- Ref.Synchronized.make(0)
_ <- ZIO.foreachPar(users) { user =>
ref.updateZIO(sumOfAges =>
api.getAge(user).map(_ + sumOfAges)
)
}
v <- ref.get
} yield (v / users.length)