Agent
Agent: Reference with effectful mutators
Section titled “Agent: Reference with effectful mutators”Agent is like cats-effect Ref but it allows effectful updates of referenced value. It also allows enqueuing of mutations without waiting for their completion and mutation of values by filter(partial function).
trait Agent[F[_], A] {
def get: F[A]
def updateM(f: A => F[A]): F[A]
def fireUpdateM(f: A => F[A]): F[Unit]
def modifyM[B](f: A => F[(B, A)]): F[B]
def updateSomeM(f: PartialFunction[A, F[A]]): F[A]
def modifySomeM[B](default: B)(f: PartialFunction[A, F[(B, A)]]): F[B]}
Agent’s companion object contains default implementation that can be built from cats-effect’s Ref and Semaphore:
object Agent {
final case class SemRef[F[_]: Monad: Fire, A](ref: Ref[F, A], sem: Semaphore[F]) extends Agent[F, A] { // impl code... }}
Creation
Section titled “Creation”One can create Agent of some value with helper MakeAgent.
It allows different effects for creating and running Agent:
trait MakeAgent[I[_], F[_]] {
def agentOf[A](a: A): I[Agent[F, A]]}
MakeAgent has a companion object that offers easier creation of Agent instances. Agent could also be constructed from tofu.concurrent’s MakeRef and MakeSemaphore implicit instances.
object MakeAgent {def apply[I[_], F[_]](implicit mkAgent: MakeAgent[I, F]): Applier[I, F] = // impl
implicit def byRefAndSemaphore[I[_]: FlatMap, F[_]: Monad: Fire]( implicit refs: MakeRef[I, F], sems: MakeSemaphore[I, F] ) = // impl}
If you are using the same effect for creation and running you can use Agents type alias defined in tofu.concurrent package object.
package tofu
package object concurrent { // other aliases... type Agents[F[_]] = MakeAgent[F, F] // other aliases...}
Examples
Section titled “Examples”Using Agents:
Section titled “Using Agents:”import cats.effect.Syncimport cats.implicits._import cats.Monadimport cats.syntax.flatMap._import tofu.common.Consoleimport tofu.concurrent.Agents
def example[F[_]: Agents: Sync: Monad: Console]: F[Unit] = for { _ <- Monad[F].unit agent <- Agents[F].of(42) newValue <- agent.updateM(a => Console[F].putStrLn(s"current value is $a") *> Monad[F].pure(a + 27)) _ <- Console[F].putStrLn(s"new value is $newValue") // new value is 69 } yield ()
Using MakeAgent:
Section titled “Using MakeAgent:”import cats.effect.Syncimport cats.implicits._import cats.Monadimport cats.syntax.flatMap._import tofu.common.Consoleimport tofu.concurrent.{Agents, MakeAgent, MakeRef, MakeSemaphore, Refs, Semaphores}import tofu.Fire
def example[F[_]: Agents: Fire: Monad: Console: Sync: Refs: Semaphores]( implicit refs: MakeRef[Option, F], sems: MakeSemaphore[Option, F] ): F[Unit] = for { _ <- Monad[F].unit agent <- MakeAgent[Option, F].of(42).map(Monad[F].pure(_)).getOrElse(Agents[F].of(42)) newValue <- agent.updateM(a => Console[F].putStrLn(s"current value is $a") *> Monad[F].pure(a + 27)) _ <- Console[F].putStrLn(s"new value is $newValue") // new value is 69 } yield ()