Generalized `sampledBy` for dealing with glitches by hand #383
Comments
Well that makes sense indeed. I've often felt the need for an easy way to say "i need the values of x,y,z and updates on x only". This method should be pretty straightforward to implement too. Wanna try? Are you suggesting we remove the automatic glitch prevention mechanisms? :) They've proved quite a lot harder to implement correctly than it first seemed, I have to admit. |
I can try to make a PR, but not very soon (on next week probably). I would keep current limited mechanism, but wouldn't try to extend it to streams with dynamic dependencies (like flatMapLatest), at least until we have good algorithm for that. |
Hey guys, I currently do not really have a very good understanding of Bacon's approach to glitch prevention. I'm not sure there is any way of completely removing glitches that doesn't require maintaining a dependency graph of there observables, and traversing that in its entirety on notifications. Any paper I've read on the subject uses this approach, with optimizations of special cases. Bacon seems stuck in some kind of middle ground between non-glitchy/glitchy, and I'm not quite sure I completely support this design decision, since I've never quite understood the intended semantics. Perhaps we're only aiming for a loosely defined sense of "prevent some glitches some of the time" for common cases, but this does feel hard to reason about. For instance if I wanted to prove the associativity law for monads, I'd be unsure of how to argue whether changing the order of composition might affect glitchiness. In fact I'm not sure I can even say exactly what the semantics should be. I'd argue the right way to go about this is either
Perhaps I only need to be educated on the intended semantics here, so take what I'm saying with a grain of salt. I do feel it's gotten slightly hard to contribute to Bacon (or at least the core) since this aspect of the library is a bit hazy to me, both in terms of semantics and implementation. Perhaps you could formalize what your vision of this feature is a bit @raimohanska? |
@philipnilsson sorry for late reply. I've been too busy with other stuff... Yeah, atomic updates thing proved harder that it seemed at first. There is indeed a dependency graph, but I'm using some (not as) smart (as I thought) optimizations to keep performance nearly on pre-0.7 level. I just fixed #363 with a simple fix, just like I fixed a number of issues before that. Still, issue #353 is open and seems non-trivial to fix without sacrificing performance. The problem there is that unlike all other streams, flatMapped ones have changing dependencies because new dependencies can be introduced for each source event. So even though I think it's possible to fix #353 and probably any future findinds too, it comes at the cost of increased complexity. Btw, the goal of Bacon.js is quite ambititious, compared to Rx and Elm for instance:
This is not to undermine Rx or Elm, which have different and simpler approaches in the core (correct me if I'm wrong here):
Both are tools that do their thing very well. It's just that I want best of both worlds and that's challenging! As for my vision of what to do with glitches. Not entirely sure:) I think we should stress correctness over performance and eliminate glitches totally. This is not easy, but will provide the best user experience. On the other hand there might be room for a lighter-weight high-performance bacon without the glitch prevention mechanism. |
Nice comparison of Elm, Rx, and Bacon. One observation on "Lazy evaluation whenever possible (unlike Rx?)". Rx has lazy evaluation and they call it "Cold observables": http://www.introtorx.com/Content/v1.0.10621.0/14_HotAndColdObservables.html |
Cold observables are not what I meant. Instead they are one of the problems that caused me to start writing my own library. Lazy eval in bacon means that combinator funcs are nit called until value is needed.
|
Could you explain how combinator funcs on cold observables are not lazily evaluated in Rx? |
Do you, @raimohanska, mean I'd say there is huge confusion of terms: lazy evaluation vs. lazy propagation, is combinator function the combinator (e.g. map) or its parameter? etc. You should really write a blog post about differences of Rx and bacon (and try to establish the vocabulary), or give a (recorded) talk ;) |
Funny that this issue turned into a discussion on laziness. But let's discuss that :) @staltz I didn't say anything about combinator funcs on cold observables. Just said that cold observables are not the thing I was talking about. The laziness I'm talking about is for instance that @phadej Yeah I should or someone should. Unfortunately I don't have the time right now. I feel bad for not being able to dedicate enough time for Bacon.js. On the other hand, no-one pays me to do so. |
Understood now. I guess it's hard to compare the libraries when they seem like slightly different paradigms. Like there isn't |
I think there's sampledBy or equivalent in Rx for sure. |
@staltz There is e.g. @raimohanska, I guess you can build Yet one can argue if lazy valuation is that important, after all you can pass thunks around. That will need some boilerplate, but it might be good thing — to have lazyness explicit. @raimohanska I'm still a bit confused. Aren't eventstreams similar to cold observables, i.e. deliver events only when there is subscriber. Or is subscriber here different, in Rx anything and in bacon.js nodes with And similarly Rx's EDIT: disclaimer: my understanding about Rx are based on ReactiveCocoa (long shot!), which I use for data-flow, i.e. with behaviorsubjects — which is almost against official best practices |
@phadej both Rx and Bacon.js share the same idea of laziness wrt subscriptions: the observables only connect to their underlying data sources when there are observers. The difference here is that Rx observables by default establish separate connections for each incoming subscriber while Bacon.js always uses a single connection. In Rx you can use Rx cold observables are such that output the same sequence of events to any incoming subscriber at any time. For instance, if you make an Here's my old rant about Rx.js that describes why thes Rx design choices pushed me to start working on a library of my own... |
So if I have var b = a.map(f);
var c = b.map(g);
var d = b.map(h); then in on one event in Maybe cause I played with behavioursubjects, I didn't fall into scan-trap. Though I observed the multi-evaluation, just weren't aware why it happened. This explains, great thanks! P.S. Rx documentation improved over the years, we have to catch up! |
@phadej, behaviour subjects (and other subjects) are by definition hot observables, so that's why. Nowadays I try to use Any way, this is supposed to be about Bacon. Sorry for off topics. |
@phadej I believe that would happen. I mean double evaluation in your case. And yeah, bacon.js docs haven't evolved much. You've contributed some improvements though :) |
I'm short on free time too. And there so much interesting things to contribute to! Though I'm making slight progress on showing types in docs more explicitly (the toggle). /offtopic |
Rx still has very simple semantics essentially as collections, in many ways http://conal.net/papers/push-pull-frp/ Now when I ask myself what the behaviour of some combinators are I'm not What are the semantics of the following stream?
In the current version it prints 10, and nothing else. On what basis do we The other big issue is how to implement Bacon in a way where can be On Mon, Jun 23, 2014 at 11:37 PM, Juha Paananen notifications@github.com
|
@philipnilsson the example with There's a limited number of combinators that are concerned by glitch elimination: sampledBy, combine, takeUntil I think. It might make sense to spell out the semantics. I'm afraid automatic tests are the best guarantee of correctness we can get. Property-based testing with generated test data might provide more coverage. To get everything working according to semantics? For me it is to fix known issues #367 and #353. |
Conal's semantics for streams is In this semantic domain we can describe the We can define the semantics of delay as What is a semantic domain where we can talk about things like glitch elimination? It seems like in this domain events are somehow tagged with their source, and some combinators will use this information to sometimes combine events. As the problem with synchronous events show, the criteria is not one of events occuring at the same time. Perhaps the correct modification to the semantics is some notion of "infinitesimals" of time, such that in It might be that this would lead us to a place where synchronous events (in the js runtime) might not be possible? |
TL;DR assigning sematics afterwards is not easy. infinitesimals@philipnilsson the infinitesimals as such would work. If the events produced by var x = Bacon.fromArray([0, 1, 2]);
var y = Bacon.fromArray([3, 4]);
x.merge(y).log(); // ??? If events in Yet currently the output is Than again, if we swap arguments of merge: var x = Bacon.fromArray([0, 1, 2]);
var y = Bacon.fromArray([3, 4]);
y.merge(x).log(); the output is And this could be explained also by another idea that even in synchronous block t is not fixed,
x.merge(x)As @raimohanska said var z = Bacon.fromArray([{x: 10, y: 20}]);
z.map('.x').merge(z.map('.y')).log(); should produce In this case semantics for streams should be merge = OrderedMap.mergeWith (++) -- concat the events And again, difficulties. Properties aren't simple. If their sematics is current value is the last event occured in underlying eventstream, what is combineWith f = OrderedMap.mergeWith (zipWith f)
combineWith f = OrderedMap.mergeWith (liftA2 f)
combineWith f = OrderedMap.mergeWith (last . liftA2 f) var x = Bacon.fromArray([0, 1, 2]).toProperty();
var y = Bacon.fromArray([3, 4]).toProperty();
Bacon.combineWith(function (l, r) { return [l, r]; }, x, y).log(); // this is really hard to guess! But i definitely don't expect:
flatMapAll simple? and non-implementation driven ideas about Cyclic graphsWith postponed side-effects ( Also with |
With the "infinitesimals" semantics I'd expect combining
I have no idea how that could be implemented though, which is part of my problem with the current situation. I can't easily find semantics that are both reasonably clear and possible to implement. This would be problematic even if With glitchy semantics the result of |
Here's an attempt to put FRP semantics on firm ground: http://haskell.cs.yale.edu/wp-content/uploads/2011/02/frp-1st.pdf |
This issue has diverged into two
The former is almost trivial (who's gonna PR?), while the latter has proven very difficult. The current implementations of |
Actually I made a PR #387, but failed to finish the job. Maybe it not as trivial as it seems. Problem is May be it good as it is. Semantically speaking it not make much sense to sample something by synchronous events, or by property's current values. I am completely ok if somebody make another PR, but if you help to finish mine it would be nice too :) |
As glitches issue not fixed completely (#353) I propose to introduce more generalized
sampledBy
for thouse people who want to deal with glitches by hand.Here is the signature:
It is basically same as
stream.sampledBy(sampler, fn)
, but with multiple streams and sampler streams.How it may help with glitches? For example you have four streams A, B, C, and D. And you know than A and B has common original stream (they always trigger simultaneously), and so do C and D. And you need to combine all those four streams. Having
Bacon.sampledBy
you can instead of doing this:do:
And have same result as with
combineWith
, but without need of inspecting dependencies and trying to get rid of glitches automatically.What do you think? :)
The text was updated successfully, but these errors were encountered: