Skip to content

Commit

Permalink
Merge pull request #74 from TinkoffCreditSystems/atoms
Browse files Browse the repository at this point in the history
add focusing to Atom, generate Atom from QVar, deprecate Mut
  • Loading branch information
Odomontois authored Dec 18, 2019
2 parents 4e5b750 + 031ff5e commit 20fd2d6
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 3 deletions.
34 changes: 33 additions & 1 deletion concurrent/src/main/scala/tofu/concurrent/Atom.scala
Original file line number Diff line number Diff line change
@@ -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] {
Expand Down Expand Up @@ -45,19 +49,47 @@ 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)
override def getAndSet(a: A): F[A] = ref.getAndSet(a)
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[_]] {
Expand Down
3 changes: 2 additions & 1 deletion concurrent/src/main/scala/tofu/concurrent/Mut.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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))
Expand Down
9 changes: 8 additions & 1 deletion concurrent/src/main/scala/tofu/concurrent/QVar.scala
Original file line number Diff line number Diff line change
@@ -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) */
Expand Down Expand Up @@ -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)
Expand Down

0 comments on commit 20fd2d6

Please sign in to comment.