MakeRef
Cats-Effect: Ref
An asynchronous, concurrent mutable reference.
Ref is a mutable reference which is non-blocking, accessed and modified concurrently, modified atomically and dealt with using only pure functions. The default implementation is nonblocking and lightweight, consisting essentially of a purely functional wrapper over an AtomicReference.
Provides safe concurrent access and modification of its content, but no functionality for synchronisation, which is instead handled by Deferred. For this reason, a Ref is always initialised to a value.
abstract class Ref[F[_], A] {
def get: F[A]
def set(a: A): F[Unit]
def modify[B](f: A => (A, B)): F[B]
def update(f: A => A): F[Unit]
// ... and more
}
Ref's companion object contains some methods which creates new Ref instance:
import cats.effect.Sync
import cats.effect.concurrent.Ref
object Ref {
def of[F[_], A](a: A)(implicit F: Sync[F]): F[Ref[F, A]] = ???
def in[F[_], G[_], A](a: A)(implicit F: Sync[F], G: Sync[G]): F[Ref[G, A]] = ???
//...
}
In fact, mutable content modifying is a side effect. Creating of mutable reference itself is a side effect too. Sometimes Ref helps to solve by offering some method to create instance which takes one or two type parameters as effect constructors. Although, this method needs to be given Sync instances for our effects.
Tofu: MakeRef
Tofu offers an easy and understandable initializer for Ref.
import cats.effect.concurrent.Ref
trait MakeRef[I[_], F[_]] {
def refOf[A](a: A): I[Ref[F, A]]
}
MakeRef has a companion object that offers easier initialization of Ref instances. There is defined implicit syncInstance that helps in creating Ref on Sync based effects.
import cats.effect.Sync
import tofu.concurrent.MakeRef
object MakeRef {
def apply[I[_], F[_]](implicit makeRef: MakeRef[I, F]) = ???
implicit def syncInstance[I[_]: Sync, F[_]: Sync]: MakeRef[I, F] = ???
}
Ref creation
You can use object MakeRef
that can produce values of type I[Ref[F]]
(where I
and F
can be two different effects) and initialize it with ,
for example:
import cats.effect.IO
import tofu.concurrent.MakeRef
def program: IO[(Int,Int)] =
for {
ref <- MakeRef[IO,IO].of[Int](42)
c1 <- ref.get
_ <- ref.modify(x => (x + 1, x))
c2 <- ref.get
} yield (c1,c2)
program.unsafeRunSync() // (42, 43)
You can simplify this by using Refs[F[]] type alias defined in tofu.concurrent
package object.
import cats.effect.IO
import tofu.concurrent.Refs
for {
ref <- Refs[IO].of[Int](42)
c1 <- ref.get
_ <- ref.modify(x => (x + 1, x))
c2 <- ref.get
} yield (c1,c2)
You can also omit the explicit indication of the value type.
import cats.effect.IO
import tofu.concurrent.Refs
for {
ref <- Refs[IO].of(42)
c1 <- ref.get
_ <- ref.modify(x => (x + 1, x))
c2 <- ref.get
} yield (c1,c2)