I seem to be a little bit confused when comparing Clojure's core.async to the so called Reactive Extensions (Rx) and FRP in general. They seem to tackle similar problem of async-hronicity, so I wonder what are the principal differences and in what cases is one preferred over the other. Can someone please explain?
EDIT: To encourage more in-depth answers I want to make the question more specific:
Core.async allows me to write synchronous-looking code. However as I understand it FRP only needs one level of nested callbacks (all the function that handle logic are passed as arguments to FRP API). This seems that both approaches make the callback pyramids unnecessary. It is true that in JS I have to write function() {...}
many times, but the main problem, the nested callbacks, is gone in FRP also. Do I get it right?
"FRP complects the communication of messages with flow of control" Can you (someone) please give a more specific explanation?
Can't I pass around FRP's observable endpoints the same way as i pass channels?
In general I understand where both approaches historically come from and I have tried few tutorials in both of them. However I seem to be "paralyzed" by the non-obviousness of differences. Is there some example of a code that would be hard to write in one of these and easy using the other? And what is the architectural reason of that?
I think main problem is your assumption about the tackled problem are not quite so, since none of them are tackling the asynchronicity "problem".
FRP
main idea is propagation of change, think about accomplishing the same thing Excel does, where you define cells depending on each other in a cascade, and when one cell changes, all the dependent cells on the cascade are recalculated.
core.async
main idea is systems decomposition, think as separating concerns using a queue
in the middle of different processes, in core.async
case instead of queues you have channels but you get the idea.
So, removing the pyramidal code is not the objective of either technology, and they operate on different abstraction layers.
The idea about complecting communication and flow control is taken from the original core async post.
While there are various mechanisms to make events/callbacks cleaner (FRP, Rx/Observables) they don't change their fundamental nature, which is that upon an event an arbitrary amount of other code is run, possibly on the same thread, leading to admonitions such as "don't do too much work in your handler", and phrases like "callback hell".
Rephrasing, if you have business domain code inside an event handler, you have complected the X event processing with the what to do when X happens.
Which is what core.async
tackles, since introducing a queue/channel in the middle, helps for a better separation of concerns.
All your questions regarding callbacks and passing observable endpoints as parameters are just implementation questions, it really depends on the Rx
implementation and API.
If you look at React reusable components you really don't have much of a callback hell to be seen, and you get that idea of passing observables around.
Even if Rx
can be used to model any data flow, is more commonly used for UI Rendering a-la Excel, to simplify how you update your view when your model changes.
On the other hand, Core.Async
can be used to model separation of concerns when any two sub-systems communicate with each other (same usage scenario as queues), using it on the UI rendering chain main idea is to separate:
So you can have core.async
and FRP
together, since core.async
will separate concerns, and FRP
will define your cascading data-flow once your model is updated.