Pure holder of a single value of type A
that can be read in the effect F
.
- Companion:
- object
- Source:
- Signal.scala
Value members
Abstract methods
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.
- Source:
- Signal.scala
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.
- Source:
- Signal.scala
Asynchronously gets the current value of this Signal
.
Asynchronously gets the current value of this Signal
.
- Source:
- Signal.scala
Concrete methods
Interrupts the supplied Stream
when this Signal
is true
.
Interrupts the supplied Stream
when this Signal
is true
.
- Source:
- Signal.scala
Converts this signal to signal of B
by applying f
.
Converts this signal to signal of B
by applying f
.
- Source:
- Signal.scala
Predicates the supplied effect f
on this Signal
being true
.
Predicates the supplied effect f
on this Signal
being true
.
- Source:
- Signal.scala
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 aSignal
to change, or waiting for the value of theSignal
of an orderedStream[IO, Int]
to be greater than a certain number.
- Source:
- Signal.scala