raquo / Airstream

State propagation and event streams with mandatory ownership and no glitches
MIT License
247 stars 28 forks source link

Semantics of `toSignal` #83

Closed cubuspl42 closed 3 years ago

cubuspl42 commented 3 years ago

What's the semantics of toSignal?

I guess that it's an operator that takes an initial values, and creates a signal that, whenever an event occurs in the source event stream, switches to that event.

Like FRP stepper.

stepper

(Image from to Reactive Banana documentation)

But how does toSignal achieve this effect, taking into consideration that both EventStream and Signal are "lazy"? How does it ensure that it doesn't "miss" any event (which would be reflected in its state)?

raquo commented 3 years ago

Correct. The resulting signal can miss events if the events are fired while the signal is stopped. Such a situation is possible if the signal has no observers but the stream has observers other than this signal.

I don't like this, but I'm not aware of how this can be implemented any differently while keeping things lazy since the basic tenet of our memory management is that the parent observable must not have a reference to dependent observables that are stopped.

I also don't know how to make things strict (not lazy) without memory management footguns. Originally we used to have a State type which was like Signal but strict, in simple terms it was its own observer, activated immediately upon creation. But that was basically impossible to work with safely, e.g.

val state: State[S] = ??? // When does this state get released?
val models: List[Model] = ???
val views: List[View] = models.map { model =>
  makeView(model, state.map(st => st.filterByModel(model))) // When does THIS state get released?
}

Hope that makes sense, but I can elaborate if not. I'd be curious to know if there are better solutions to this conundrum.

PS I've just enabled discussions on this github project to this kind of talk separate from actionable bugs and feature requests.