All Downloads are FREE. Search and download functionalities are using the official Maven repository.

tofu.concurrent.QVar.scala Maven / Gradle / Ivy

package tofu.concurrent

import cats.Applicative
import tofu.Guarantee
import tofu.internal.instances.MakeQVarInstance

import tofu.syntax.guarantee.*
import tofu.syntax.monadic.*

/** a middleground between cats.concurrent.MVar and zio.Queue.bounded(1) */
trait QVar[+F[_], A] {

  /** Returns `true` if the `MVar` is empty and can receive a `put`, or `false` otherwise.
    *
    * Note that due to concurrent tasks, logic built in terms of `isEmpty` is problematic.
    */
  def isEmpty: F[Boolean]

  /** Fills the `MVar` if it is empty, or blocks (asynchronously) if the `MVar` is full, until the given value is next
    * in line to be consumed on [[take]].
    *
    * This operation is atomic.
    *
    * @return
    *   a task that on evaluation will complete when the `put` operation succeeds in filling the `MVar`, with the given
    *   value being next in line to be consumed
    */
  def put(a: A): F[Unit]

  /** Empties the `MVar` if full, returning the contained value, or blocks (asynchronously) until a value is available.
    *
    * This operation is atomic.
    *
    * @return
    *   a task that on evaluation will be completed after a value was retrieved
    */
  def take: F[A]

  /** Tries reading the current value, or blocks (asynchronously) until there is a value available.
    *
    * This operation is atomic.
    *
    * @return
    *   a task that on evaluation will be completed after a value has been read
    */
  def read: F[A]
}

object QVar extends QVarInstances {

  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] = QAtom(self)
  }
}

trait MakeQVar[I[_], F[_]] {
  def qvarOf[A](a: A): I[QVar[F, A]]
  def qvarEmpty[A]: I[QVar[F, A]]
}

object QVars {
  def apply[F[_]](implicit qvars: MakeQVar[F, F]): MakeQVar.Applier[F, F] = new MakeQVar.Applier(qvars)
}

object MakeQVar extends MakeQVarInstance {
  def apply[I[_], F[_]](implicit mkvar: MakeQVar[I, F]) = new Applier[I, F](mkvar)

  final class Applier[I[_], F[_]](private val makeMVar: MakeQVar[I, F]) extends AnyVal {
    def empty[A]: I[QVar[F, A]]    = makeMVar.qvarEmpty[A]
    def of[A](a: A): I[QVar[F, A]] = makeMVar.qvarOf(a)
  }
}

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)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy