7 ms·
EDIT: The article is about javascript. Everything below does not apply to javascript. You need more than one thread, at least, for any of these problems. I str
by fmstephe 11y ago
EDIT: The article is about javascript. Everything below does not apply to javascript. You need more than one thread, at least, for any of these problems.
I strongly suspect that combining async streams will prove to be a problem in practice, whether we resolve glitches or not. This article requires quite subtle reasoning about async systems. In my experience this type of reasoning is rarely done effectively on real life projects.
I am not criticising Rx, I am interested in Rx. But I am always concerned when a project/community starts producing articles on very subtle topics and claims 'x isn't a problem if you just understand it properly'. Here properly probably means deeply, and deep knowledge in software is scarce. There are too many things to know, to know many things deeply.
Let me look at one example (and please comment if I'm really not understanding something).
So we consume, asynchronously, two streams. Because they are asynchronous we might hit some point where it's a bit uncertain where two outputs are related.
errors ---e1----e2------------------
userActions -u1------u2------------u3----
analyticsMessages ---e1u1--(e1u2 e2u2)---e2u3--
That's a good example, and we fixed that problem with 'errors.withLatestFrom(...'. But, since we are asynchronous don't we also have to worry about this scenario.
errors ---e1-------------------e2--
userActions -u1--u2--u3-u4-u5-u6-u7-u8--
analyticsMessages ---e1u1---------------e2u8--
Now, let me clarify what happened here. The error occurred after u2, but the errors thread got descheduled and the userActions thread kept on processing. I am not certain exactly what to expect from 'errors.withLatestFrom(userActions,...' here but I suspect that 'e2u8' may be the expected output.
So this error reporting system is fundamentally unreliable. If this is correct then I would say that combining Rx streams is not appropriate for this use case. This also suggests that although we managed to get rid of our glitches we still produced a terrible piece of software.
Do I have to wrong end of the stick? (The answer was yes, but it was good anyway)
- staltz 11y ago> The error occurred after u2, but the errors thread got descheduled and the userActions thread kept on processing. Your suggested case is contrived and doesn't apply to JavaScript (the examples were in JavaScript where I assume all these streams are in the same thread). I am not claiming withLatestFrom is the correct mechanism for determining order of events in multi-threaded assumptions. That's an orthogonal concern to the issue at hand. > I strongly suspect that combining async streams will prove to be a problem in practice Combining async results is a true problem and people often attempt to solve them with callbacks or heavy usage of Futures/Promises. Rx solves these problems easily, and I've witnessed a lot of developers confirm this when learning Rx. It's not hard to teach the difference between withLatestFrom and combineLatest and their intended use cases. Much easier than managing callback hell, which is what RxJS intends to replace.
- fmstephe 11y ago(A great thing about HN is getting to talk with the authors directly) I didn't recognise that you were talking chiefly about javascript. It is true that my example doesn't apply here. Ok, then I would make one comment (and edit my first comment). I would avoid the discussion of simultaneity at the start 'Events in parentheses happen “simultaneously”. In practice they happen at slightly different times, but separated by only a couple of nanoseconds, so people understand them to be simultaneous. Events (c1c2) are called glitches and sometimes considered a problem because one would expect only c2 to happen.' Because you are working in a single threaded simultaneous events just don't exist. The glitches you describe occur completely independently of timing, nanoseconds or otherwise. Drawing up diagrams where events occur at the same time is adding complexity which doesn't exist. errors ---e1-------e2--------------- userActions -u1------u2------------u3---- analyticsMessages ---e1u1--(e1u2 e2u2)---e2u3-- errors ---e1----e2------------------ userActions -u1----------u2--------u3---- analyticsMessages ---e1u1--(e2u1 e2u2)---e2u3-- Both of these scenarios produce glitches (as I understand it). So we don't need any notion of 'simultaneous events' to create them. Anyway - that is nitpicking. I enjoyed your article. I agree that the scenario described at top does not apply in javascript. I too strongly dislike callbacks :)
- noelwelsh 11y agoI agree with the essence of what you say. Rx trades off simple semantics for a simple implementation. Given the issues such as the above I think it is generally the wrong tradeoff. Simpler semantics do not require a great deal more work, as demonstrated by numerous other systems that have implemented them.
- lomnakkus 11y agoWhat are the "numerous other systems" that you had in mind? Links would be greatly appreciated!
- noelwelsh 11y agoSome alternatives are linked in the article. Other systems include FrTime / Flapjax (glitch-free push based systems in Racket and JS respectively), and the work on FRP.
- lomnakkus 11y agoWell, yes, but those don't seem objectively superior. They all have problems to do with it being too easy to accidentally introduce space leaks or time leaks... (At least as far as I recall. If there's some documentation/papers that suggest otherwise, please link!) EDIT: I should say: FRPNow! seems to have cracked a bit of this puzzle and Neel Krishnaswami had a recent-ish (2014?) paper on avoiding both space and time leaks with static checking, but I don't think we have anything approaching the mainstream, unfortunately.
- WorldMaker 11y agoAn issue with this contrived example, as well, is that Rx has an error flow at the stream level, so typically you are more likely to build things similar to: userActions(combined) \ \--u1-----------------e1--X \--u2---------------------------e2--X \--u3--... \--u4--... \--u5--... \... e1 \ X While you could do your analytics message on the combined diagonal stream, you'd probably want to do it on the horizontal streams.