Signal

fs2.concurrent.Signal
See theSignal companion object
trait Signal[F[_], A]

Pure holder of a single value of type A that can be read in the effect F.

Attributes

Companion
object
Source
Signal.scala
Graph
Supertypes
class Object
trait Matchable
class Any
Known subtypes
class SignallingRef[F, A]
Self type
Signal[F, A]

Members list

Value members

Abstract methods

def continuous: Stream[F, 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.

Attributes

Source
Signal.scala
def discrete: Stream[F, A]

Returns a stream of the current value and subsequent updates to this signal.

Returns a stream of the current value and subsequent updates to this signal.

Even if you are pulling as fast as possible, updates that are very close together may result in only the last update appearing in the stream. In general, when you pull from this stream you may be notified of only the latest update since your last pull. If you want to be notified about every single update, use a Queue or Channel instead.

Attributes

Source
Signal.scala
def get: F[A]

Gets the current value of this Signal.

Gets the current value of this Signal.

Attributes

Source
Signal.scala

Concrete methods

def changes(implicit eqA: Eq[A]): Signal[F, A]

Returns a signal derived from this one, that drops update events that did not change the value.

Returns a signal derived from this one, that drops update events that did not change the value.

Attributes

Source
Signal.scala
def getAndDiscreteUpdates(implicit F: Concurrent[F]): Resource[F, (A, Stream[F, A])]

Returns the current value of this Signal and a Stream to subscribe to subsequent updates, with the same semantics as discrete. The updates stream should be compiled at most once.

Returns the current value of this Signal and a Stream to subscribe to subsequent updates, with the same semantics as discrete. The updates stream should be compiled at most once.

Attributes

Source
Signal.scala
def interrupt[A](s: Stream[F, A])(implicit F: Concurrent[F]): Stream[F, A]
Implicitly added by BooleanSignalOps

Interrupts the supplied Stream when this Signal is true.

Interrupts the supplied Stream when this Signal is true.

Attributes

Source
Signal.scala
def map[B](f: A => B)(implicit F: Functor[F]): Signal[F, B]
Implicitly added by SignalOps

Converts this signal to signal of B by applying f.

Converts this signal to signal of B by applying f.

Attributes

Source
Signal.scala
def predicate[A](f: F[A])(implicit F: Monad[F]): F[Unit]
Implicitly added by BooleanSignalOps

Predicates the supplied effect f on this Signal being true.

Predicates the supplied effect f on this Signal being true.

Attributes

Source
Signal.scala
def waitUntil(p: A => Boolean)(implicit F: Concurrent[F]): 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.

Attributes

Source
Signal.scala

Concrete fields

val self: Signal[F, Boolean]
Implicitly added by BooleanSignalOps

Attributes

Source
Signal.scala
val self: Signal[F, A]
Implicitly added by SignalOps

Attributes

Source
Signal.scala