From 031ff5e9c653621c29e6f570fa4c75c7a39697f4 Mon Sep 17 00:00:00 2001 From: Oleg Nizhnik Date: Wed, 18 Dec 2019 16:14:57 +0300 Subject: [PATCH] add focusing to Atom, generate Atom from QVar, deprecate Mut --- .../src/main/scala/tofu/concurrent/Atom.scala | 34 ++++++++++++++++++- .../src/main/scala/tofu/concurrent/Mut.scala | 3 +- .../src/main/scala/tofu/concurrent/QVar.scala | 9 ++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/concurrent/src/main/scala/tofu/concurrent/Atom.scala b/concurrent/src/main/scala/tofu/concurrent/Atom.scala index 9b23e6f4c..5599140d9 100644 --- a/concurrent/src/main/scala/tofu/concurrent/Atom.scala +++ b/concurrent/src/main/scala/tofu/concurrent/Atom.scala @@ -1,9 +1,13 @@ package tofu.concurrent +import cats.{Applicative, Functor, Monad} import cats.effect.Sync import cats.effect.concurrent.Ref +import tofu.Guarantee import tofu.concurrent.Atom.AtomByRef import tofu.higherKind.{RepresentableK, derived} +import tofu.optics.Contains import tofu.syntax.monadic._ +import tofu.syntax.bracket._ /** a middleground between cats.concurrent.Ref and zio.Ref */ trait Atom[F[_], A] { @@ -45,12 +49,18 @@ trait Atom[F[_], A] { * Like `tryModify` but does not complete until the update has been successfully made. */ def modify[B](f: A => (A, B)): F[B] - } object Atom { implicit def representableKInstance[A]: RepresentableK[Atom[*[_], A]] = derived.genRepresentableK[Atom[*[_], A]] + 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) + case _ => FocusedAtom(self, bInA) + } + } + final case class AtomByRef[F[_], A](ref: Ref[F, A]) extends Atom[F, A] { override def get: F[A] = ref.get override def set(a: A): F[Unit] = ref.set(a) @@ -58,6 +68,28 @@ object Atom { override def update(f: A => A): F[Unit] = ref.update(f) override def modify[B](f: A => (A, B)): F[B] = ref.modify(f) } + + final case class QAtom[F[_]: Applicative: Guarantee, A](qvar: QVar[F, A]) extends Atom[F, A] { + def get: F[A] = qvar.read + def set(a: A): F[Unit] = getAndSet(a).void + def getAndSet(a: A): F[A] = qvar.take.guaranteeAlways(qvar.put(a)) + def update(f: A => A): F[Unit] = qvar.take.bracketIncomplete(a => qvar.put(f(a)))(qvar.put) + def modify[B](f: A => (A, B)): F[B] = qvar.take.bracketIncomplete { a => + val (next, res) = f(a) + qvar.put(next) as res + }(qvar.put) + } + + 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) + } + } } trait MakeAtom[I[_], F[_]] { diff --git a/concurrent/src/main/scala/tofu/concurrent/Mut.scala b/concurrent/src/main/scala/tofu/concurrent/Mut.scala index 7f0e9df54..79a47aabe 100644 --- a/concurrent/src/main/scala/tofu/concurrent/Mut.scala +++ b/concurrent/src/main/scala/tofu/concurrent/Mut.scala @@ -10,6 +10,7 @@ import tofu.optics.Contains import tofu.syntax.bracket._ /** simplified form of synchonized mutable variable, that could be satisfied both by MVar and Ref */ +@deprecated("use Atom / qvar.toAtom", since = "0.5.6") trait Mut[F[_], A] { def get: F[A] def update(f: A => A): F[Unit] @@ -34,7 +35,7 @@ object Mut { } private[Mut] class FocusedMut[F[_], A, B](v: Mut[F, A], focus: Contains[A, B])(implicit F: Functor[F]) - extends Mut[F, B] { + extends Mut[F, B] { def get: F[B] = v.get.map(focus.extract) def update(f: B => B): F[Unit] = v.update(focus.update(_, f)) override def set(b: B): F[Unit] = v.update(focus.set(_, b)) diff --git a/concurrent/src/main/scala/tofu/concurrent/QVar.scala b/concurrent/src/main/scala/tofu/concurrent/QVar.scala index d243ea602..392bb55d3 100644 --- a/concurrent/src/main/scala/tofu/concurrent/QVar.scala +++ b/concurrent/src/main/scala/tofu/concurrent/QVar.scala @@ -1,8 +1,11 @@ package tofu.concurrent -import cats.effect.{Concurrent, Sync} import cats.effect.concurrent.MVar +import cats.effect.{Concurrent, Sync} +import cats.{Applicative, Functor} +import tofu.Guarantee import tofu.concurrent.QVar.QVarByMVar import tofu.higherKind.{RepresentableK, derived} +import tofu.optics.Contains import tofu.syntax.monadic._ /** a middleground between cats.concurrent.MVar and zio.Queue.bounded(1) */ @@ -57,6 +60,10 @@ trait QVar[F[_], A] { object QVar { implicit def representableK[A]: RepresentableK[QVar[*[_], A]] = derived.genRepresentableK[QVar[*[_], A]] + final implicit class QVarOps[F[_], A](private val self: QVar[F, A]) extends AnyVal { + def toAtom(implicit F: Applicative[F], FG: Guarantee[F]): Atom[F, A] = Atom.QAtom(self) + } + final case class QVarByMVar[F[_], A](mvar: MVar[F, A]) extends QVar[F, A] { override def isEmpty: F[Boolean] = mvar.isEmpty override def put(a: A): F[Unit] = mvar.put(a)