Skip to content

Commit 031ff5e

Browse files
author
Oleg Nizhnik
committed
add focusing to Atom, generate Atom from QVar, deprecate Mut
1 parent 41d6e02 commit 031ff5e

File tree

3 files changed

+43
-3
lines changed

3 files changed

+43
-3
lines changed

concurrent/src/main/scala/tofu/concurrent/Atom.scala

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package tofu.concurrent
2+
import cats.{Applicative, Functor, Monad}
23
import cats.effect.Sync
34
import cats.effect.concurrent.Ref
5+
import tofu.Guarantee
46
import tofu.concurrent.Atom.AtomByRef
57
import tofu.higherKind.{RepresentableK, derived}
8+
import tofu.optics.Contains
69
import tofu.syntax.monadic._
10+
import tofu.syntax.bracket._
711

812
/** a middleground between cats.concurrent.Ref and zio.Ref */
913
trait Atom[F[_], A] {
@@ -45,19 +49,47 @@ trait Atom[F[_], A] {
4549
* Like `tryModify` but does not complete until the update has been successfully made.
4650
*/
4751
def modify[B](f: A => (A, B)): F[B]
48-
4952
}
5053

5154
object Atom {
5255
implicit def representableKInstance[A]: RepresentableK[Atom[*[_], A]] = derived.genRepresentableK[Atom[*[_], A]]
5356

57+
final implicit class AtomOps[F[_], A](private val self: Atom[F, A]) extends AnyVal {
58+
def focused[B](bInA: A Contains B)(implicit F: Functor[F]): Atom[F, B] = self match {
59+
case FocusedAtom(v, focus) => FocusedAtom(v, focus >> bInA)
60+
case _ => FocusedAtom(self, bInA)
61+
}
62+
}
63+
5464
final case class AtomByRef[F[_], A](ref: Ref[F, A]) extends Atom[F, A] {
5565
override def get: F[A] = ref.get
5666
override def set(a: A): F[Unit] = ref.set(a)
5767
override def getAndSet(a: A): F[A] = ref.getAndSet(a)
5868
override def update(f: A => A): F[Unit] = ref.update(f)
5969
override def modify[B](f: A => (A, B)): F[B] = ref.modify(f)
6070
}
71+
72+
final case class QAtom[F[_]: Applicative: Guarantee, A](qvar: QVar[F, A]) extends Atom[F, A] {
73+
def get: F[A] = qvar.read
74+
def set(a: A): F[Unit] = getAndSet(a).void
75+
def getAndSet(a: A): F[A] = qvar.take.guaranteeAlways(qvar.put(a))
76+
def update(f: A => A): F[Unit] = qvar.take.bracketIncomplete(a => qvar.put(f(a)))(qvar.put)
77+
def modify[B](f: A => (A, B)): F[B] = qvar.take.bracketIncomplete { a =>
78+
val (next, res) = f(a)
79+
qvar.put(next) as res
80+
}(qvar.put)
81+
}
82+
83+
private[Atom] case class FocusedAtom[F[_]: Functor, A, B](v: Atom[F, A], focus: Contains[A, B]) extends Atom[F, B] {
84+
def get: F[B] = v.get.map(focus.get)
85+
def set(b: B): F[Unit] = v.update(focus.set(_, b))
86+
def getAndSet(b: B): F[B] = v.modify(a => (focus.set(a, b), focus.get(a)))
87+
def update(f: B => B): F[Unit] = v.update(focus.update(_, f))
88+
def modify[C](f: B => (B, C)): F[C] = v.modify { a =>
89+
val (b, c) = f(focus.get(a))
90+
(focus.set(a, b), c)
91+
}
92+
}
6193
}
6294

6395
trait MakeAtom[I[_], F[_]] {

concurrent/src/main/scala/tofu/concurrent/Mut.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import tofu.optics.Contains
1010
import tofu.syntax.bracket._
1111

1212
/** simplified form of synchonized mutable variable, that could be satisfied both by MVar and Ref */
13+
@deprecated("use Atom / qvar.toAtom", since = "0.5.6")
1314
trait Mut[F[_], A] {
1415
def get: F[A]
1516
def update(f: A => A): F[Unit]
@@ -34,7 +35,7 @@ object Mut {
3435
}
3536

3637
private[Mut] class FocusedMut[F[_], A, B](v: Mut[F, A], focus: Contains[A, B])(implicit F: Functor[F])
37-
extends Mut[F, B] {
38+
extends Mut[F, B] {
3839
def get: F[B] = v.get.map(focus.extract)
3940
def update(f: B => B): F[Unit] = v.update(focus.update(_, f))
4041
override def set(b: B): F[Unit] = v.update(focus.set(_, b))

concurrent/src/main/scala/tofu/concurrent/QVar.scala

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
package tofu.concurrent
2-
import cats.effect.{Concurrent, Sync}
32
import cats.effect.concurrent.MVar
3+
import cats.effect.{Concurrent, Sync}
4+
import cats.{Applicative, Functor}
5+
import tofu.Guarantee
46
import tofu.concurrent.QVar.QVarByMVar
57
import tofu.higherKind.{RepresentableK, derived}
8+
import tofu.optics.Contains
69
import tofu.syntax.monadic._
710

811
/** a middleground between cats.concurrent.MVar and zio.Queue.bounded(1) */
@@ -57,6 +60,10 @@ trait QVar[F[_], A] {
5760
object QVar {
5861
implicit def representableK[A]: RepresentableK[QVar[*[_], A]] = derived.genRepresentableK[QVar[*[_], A]]
5962

63+
final implicit class QVarOps[F[_], A](private val self: QVar[F, A]) extends AnyVal {
64+
def toAtom(implicit F: Applicative[F], FG: Guarantee[F]): Atom[F, A] = Atom.QAtom(self)
65+
}
66+
6067
final case class QVarByMVar[F[_], A](mvar: MVar[F, A]) extends QVar[F, A] {
6168
override def isEmpty: F[Boolean] = mvar.isEmpty
6269
override def put(a: A): F[Unit] = mvar.put(a)

0 commit comments

Comments
 (0)