
tofu.concurrent.Atom.scala Maven / Gradle / Ivy
package tofu.concurrent
import cats.{Applicative, Functor}
import glass.Contains
import tofu.syntax.monadic._
import cats.data.StateT
import tofu.data.calc.CalcM
import tofu.internal.instances.MakeAtomInstance
import tofu.internal.hktAny
/** a middleground between cats.concurrent.Ref and zio.Ref */
trait Atom[+F[_], A] {
/** Obtains the current value.
*
* Since `Ref` is always guaranteed to have a value, the returned action completes immediately after being bound.
*/
def get: F[A]
/** Sets the current value to `a`.Agent
*
* The returned action completes after the reference has been successfully set.
*
* Satisfies: `r.set(fa) *> r.get == fa`
*/
def set(a: A): F[Unit]
/** Replaces the current value with `a`, returning the previous value.
*/
def getAndSet(a: A): F[A]
/** Modifies the current value using the supplied update function. If another modification occurs between the time the
* current value is read and subsequently updated, the modification is retried using the new value. Hence, `f` may be
* invoked multiple times.
*
* Satisfies: `r.update(_ => a) == r.set(a)`
*/
def update(f: A => A): F[Unit]
/** Like `tryModify` but does not complete until the update has been successfully made.
*/
def modify[B](f: A => (A, B)): F[B]
}
object Atom extends AtomInstances {
final implicit class AtomOps[F[_], A](private val self: Atom[F, A]) extends AnyVal {
def focused[B](bInA: A Contains B)(implicit F: Functor[F]): Atom[F, B] = self match {
case FocusedAtom(v, focus) => FocusedAtom(v, focus >> bInA)(F)
case _ => FocusedAtom(self, bInA)
}
}
private[Atom] case class FocusedAtom[F[_]: Functor, A, B](v: Atom[F, A], focus: Contains[A, B]) extends Atom[F, B] {
def get: F[B] = v.get.map(focus.get)
def set(b: B): F[Unit] = v.update(focus.set(_, b))
def getAndSet(b: B): F[B] = v.modify(a => (focus.set(a, b), focus.get(a)))
def update(f: B => B): F[Unit] = v.update(focus.update(_, f))
def modify[C](f: B => (B, C)): F[C] = v.modify { a =>
val (b, c) = f(focus.get(a))
(focus.set(a, b), c)
}
}
def stateTAtom[F[_]: Applicative, A]: Atom[StateT[F, A, _], A] = StateTAtom()
private case class StateTAtom[F[_]: Applicative, A]() extends Atom[StateT[F, A, _], A] {
override def get: StateT[F, A, A] = StateT.get
override def set(a: A): StateT[F, A, Unit] = StateT.set(a)
override def getAndSet(a: A): StateT[F, A, A] = StateT(a1 => (a, a1).pure[F])
override def update(f: A => A): StateT[F, A, Unit] = StateT.modify(f)
override def modify[B](f: A => (A, B)): StateT[F, A, B] = StateT(a => f(a).pure[F])
}
def calcMAtom[F[+_, +_], R, S, E]: Atom[CalcM[F, R, S, S, E, _], S] =
calcMAtomAny.asInstanceOf[Atom[CalcM[F, R, S, S, E, _], S]]
private class CalcMAtom[F[+_, +_], R, S, E]() extends Atom[CalcM[F, R, S, S, E, _], S] {
def get: CalcM[F, R, S, S, E, S] = CalcM.get
def set(a: S): CalcM[F, R, S, S, E, Unit] = CalcM.set(a).void
def getAndSet(a: S): CalcM[F, R, S, S, E, S] = CalcM.get[S] << CalcM.set(a)
def update(f: S => S): CalcM[F, R, S, S, E, Unit] = CalcM.update(f).void
def modify[B](f: S => (S, B)): CalcM[F, R, S, S, E, B] = CalcM.state(f)
}
private[this] object calcMAtomAny extends CalcMAtom[hktAny.AnyKK, Any, Any, Any]
}
trait MakeAtom[I[_], F[_]] {
def atom[A](a: A): I[Atom[F, A]]
}
object MakeAtom extends MakeAtomInstance {
def apply[I[_], F[_]](implicit makeAtom: MakeAtom[I, F]) = new Applier[I, F](makeAtom)
final class Applier[I[_], F[_]](private val makeAtom: MakeAtom[I, F]) extends AnyVal {
def of[A](a: A): I[Atom[F, A]] = makeAtom.atom(a)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy