SigRef

abstract class SigRef[F[_], A] extends Signal[[_] =>> Rx[F, _$2], A] with RefSink[F, A] with DeferredSource[[_] =>> Rx[F, _$3], A]
Companion:
object
trait DeferredSource[[_] =>> Rx[F, _$3], A]
trait RefSink[F, A]
trait Signal[[_] =>> Rx[F, _$2], A]
class Object
trait Matchable
class Any

Value members

Abstract methods

def zoom[B](lens: Lens[A, B]): SigRef[F, B]

Concrete methods

final def getF: F[A]
final def signalF: Signal[F, A]

Inherited methods

def continuous: Stream[[_] =>> Rx[F, _$2], A]

Returns a stream of the current value of the signal. An element is always available -- on each pull, the current value is supplied.

Returns a stream of the current value of the signal. An element is always available -- on each pull, the current value is supplied.

Inherited from:
Signal
def discrete: Stream[[_] =>> Rx[F, _$2], A]

Returns a stream of the updates to this signal.

Returns a stream of the updates to this signal.

Updates that are very close together may result in only the last update appearing in the stream. If you want to be notified about every single update, use a Queue or Channel instead.

Inherited from:
Signal
def get: Rx[F, A]

Obtains the value of the Deferred, or waits until it has been completed. The returned value may be canceled.

Obtains the value of the Deferred, or waits until it has been completed. The returned value may be canceled.

Inherited from:
DeferredSource
def set(a: A): F[Unit]

Sets the current value to a.

Sets the current value to a.

The returned action completes after the reference has been successfully set.

Satisfies: r.set(fa) *> r.get == fa

Inherited from:
RefSink
def tryGet: Rx[F, Option[A]]

Obtains the current value of the Deferred, or None if it hasn't completed.

Obtains the current value of the Deferred, or None if it hasn't completed.

Inherited from:
DeferredSource
def waitUntil(p: A => Boolean)(implicit F: Concurrent[[_] =>> Rx[F, _$2]]): Rx[F, Unit]

Returns when the condition becomes true, semantically blocking in the meantime.

Returns when the condition becomes true, semantically blocking in the meantime.

This method is particularly useful to transform naive, recursive polling algorithms on the content of a Signal/ SignallingRef into semantically blocking ones. For example, here's how to encode a very simple cache with expiry, pay attention to the definition of view:

trait Refresh[F[_], A] {
 def get: F[A]
}
object Refresh {
 def create[F[_]: Temporal, A](
   action: F[A],
   refreshAfter: A => FiniteDuration,
   defaultExpiry: FiniteDuration
 ): Resource[F, Refresh[F, A]] =
   Resource
     .eval(SignallingRef[F, Option[Either[Throwable, A]]](None))
     .flatMap { state =>
       def refresh: F[Unit] =
         state.set(None) >> action.attempt.flatMap { res =>
           val t = res.map(refreshAfter).getOrElse(defaultExpiry)
           state.set(res.some) >> Temporal[F].sleep(t) >> refresh
         }

       def view = new Refresh[F, A] {
         def get: F[A] = state.get.flatMap {
           case Some(res) => Temporal[F].fromEither(res)
           case None => state.waitUntil(_.isDefined) >> get
         }
       }

       refresh.background.as(view)
     }
}

Note that because Signal prioritizes the latest update when its state is updating very quickly, completion of the F[Unit] might not trigger if the condition becomes true and then false immediately after.

Therefore, natural use cases of waitUntil tend to fall into two categories:

  • Scenarios where conditions don't change instantly, such as periodic timed processes updating the Signal/SignallingRef.
  • Scenarios where conditions might change instantly, but the p predicate is monotonic, i.e. if it tests true for an event, it will test true for the following events as well. Examples include waiting for a unique ID stored in a Signal to change, or waiting for the value of the Signal of an ordered Stream[IO, Int] to be greater than a certain number.
Inherited from:
Signal