|
1 | 1 | package tofu.concurrent
|
| 2 | +import cats.{Applicative, Functor, Monad} |
2 | 3 | import cats.effect.Sync
|
3 | 4 | import cats.effect.concurrent.Ref
|
| 5 | +import tofu.Guarantee |
4 | 6 | import tofu.concurrent.Atom.AtomByRef
|
5 | 7 | import tofu.higherKind.{RepresentableK, derived}
|
| 8 | +import tofu.optics.Contains |
6 | 9 | import tofu.syntax.monadic._
|
| 10 | +import tofu.syntax.bracket._ |
7 | 11 |
|
8 | 12 | /** a middleground between cats.concurrent.Ref and zio.Ref */
|
9 | 13 | trait Atom[F[_], A] {
|
@@ -45,19 +49,47 @@ trait Atom[F[_], A] {
|
45 | 49 | * Like `tryModify` but does not complete until the update has been successfully made.
|
46 | 50 | */
|
47 | 51 | def modify[B](f: A => (A, B)): F[B]
|
48 |
| - |
49 | 52 | }
|
50 | 53 |
|
51 | 54 | object Atom {
|
52 | 55 | implicit def representableKInstance[A]: RepresentableK[Atom[*[_], A]] = derived.genRepresentableK[Atom[*[_], A]]
|
53 | 56 |
|
| 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 | + |
54 | 64 | final case class AtomByRef[F[_], A](ref: Ref[F, A]) extends Atom[F, A] {
|
55 | 65 | override def get: F[A] = ref.get
|
56 | 66 | override def set(a: A): F[Unit] = ref.set(a)
|
57 | 67 | override def getAndSet(a: A): F[A] = ref.getAndSet(a)
|
58 | 68 | override def update(f: A => A): F[Unit] = ref.update(f)
|
59 | 69 | override def modify[B](f: A => (A, B)): F[B] = ref.modify(f)
|
60 | 70 | }
|
| 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 | + } |
61 | 93 | }
|
62 | 94 |
|
63 | 95 | trait MakeAtom[I[_], F[_]] {
|
|
0 commit comments