Reactive.coffee: reactive programming and declarative UIs in CoffeeScript
yz.mit.edu
yz.mit.edu
Also, our startup, Infer, is hiring great engineers, and we love open-source - come learn about us: https://www.infer.com/careers.html
(Thanks jashkenas!)
I'm glad you're thinking about how to manage garbage collection. It's tricky with these push-based frameworks.
Have you given consideration to asynchronous vs synchronous reactivity? The advantage of asynchronous is you don't propagate the changes as they are made but instead once all the changes are done. You can avoid redundant computations this way, for example if a bind is dependent on multiple observables that change in the same "turn". And in some ways (worth debating) asynchronous semantics are more understandable than synchronous since unknown code is not being run underneath you while you are in the middle of changing observables.
Here's Ember's writeup on their rationale for asynchronous: http://emberjs.com/guides/understanding-ember/managing-async...
Also since Object.observe is asynchronous, Google's polymer/MDV will also I believe have these semantics.
Typically this is solved with weak listeners, but Javascript doesn't support weak references. I'm worried that this library never removes the listeners, effectively leaking like crazy?
I'm not talking about nested binds, but just a simple bind such as "z = bind -> x.get() + y.get()". This will add listeners to x and y. When will those listeners be removed again (without adding new ones during the recalculation of z's new value in case of a change to x or y)?
It may be that x and y are the basic models in my app, and during my clicking around in the application I create and close many panels, and each panel has bind-operations that add listeners to x and y. Now the panels may be long gone, and will never be used again, but when will the listeners that was added to x and y as part of creating the panels be removed?
The question is what you're doing with z. As long as you're also adding and removing your hypothetical panels in turn with bind, you're good:
div {class: 'container'}, bind -> [
if show.get()
div {class: 'z'}, bind -> x.get() + y.get()
else
div {class: 'nothing'}
]
The question is what happens at the top level or when you want to break out and do your own thing. We don't have the luxury of weak refs in JS, and as a result it's less forgiving if you do "silly" things like create binds that go nowhere and that you don't want to keep. But even with weak references, one can't tell if you added a bind only to subscribe a console.log caller to its changes, save to localStorage, or some other side effect that you do want to keep. In any case, it's incumbent on us to fully document what "silly" means, include simple-to-use API calls to capture and dispose entire subgraphs (for when you want to do your own thing manually), and provide good debugging introspection tools for finding these `bind`s to nowhere, in case you really don't want them lingering around.We've also been bouncing around ideas for reversing the reference DAG and having cells named by the reversed paths through the DAG, which we may experiment with. You then get to deal with the converse problem of manually needing to hold references to the sinks in the DAG. In any case, we're very open to learning from how things play out in practice, and shape the direction of the library accordingly.
bind is "stupid" in the way that it completely recalculates a cells value on any change of a cell it's previous calculation depended on. It doesn't "understand" the calculation in a way to only recalculate the part that was strictly needed to update its value to a change.
I mention this because if the entire UI is in a bind, then on every change the entire UI would be recreated. I worry that this could get prohibitively slow on a sufficiently complex UI? (It can also lead to problems with widgets losing focus, but that can be solved in a similar way as in Immediate Mode GUI's).
Say I have something like a button with an action, and the action has a cell whose value is used when the action is executed by clicking the button.
If I create the button in a bind, and the cell in the action is also made with a bind, but during the calculation to create the button the cell for the action is never actually read, then no listeners will be attached to the cell for the action, and the cell will therefore not update itself to underlying changes.
Now sometime later I click the button, but the cell for the action has an expired value, what happens?
I guess my question is, what happens when the reading of a cell value only happens outside the evaluation of binds?
One solution could be to always add a listener to new reactive cells created during a bind, another solution is simply to evaluate a cell everytime it's value is requested when it has no listeners added.
As I was playing around with the JS fiddle todo example I noticed that there is no focus restoration when I have the todos update on 'keyup' instead of on the form submission:
theForm.find('input').keyup ->
opts.onSubmit(descrip.val().trim(), priority.val().trim())
(you can see this in action at http://jsfiddle.net/EwfB8/2/)Any plans to add a feature for dealing with this issue?
Is this project related to, or draw inspiration from, Reactive Extensions (Rx.NET, Rx.js, Rx.cocoa, etc.)? https://github.com/Reactive-Extensions/RxJS
Recently, I've seen the idea of reactive user interface pop up all over HackerNews and GitHub. What a lot of folks are missing is that whole applications themselves can be reactive, from UI to servers to data sources. I feel like many of these reactive UI libraries are missing this.
We're also strong believers that the entire stack can be architected in this way. Stay tuned! For now, check out projects like Fun, Ur/Web, and Meteor/Derby:
http://marcuswest.in/essays/fun-intro/
A nice JS library for reactive programming is Bacon.js - https://github.com/raimohanska/bacon.js - it's smaller, the source code is easier to read and I found it a little easier to use.
I've learned the hard way to embrace the "best in breed small libraries" philosophy over the "monolithic do-everything framework" approach, and I'm particularly excited to see a small, simple reactive library that goes as far as to use efficient DOM diffs.
Can't wait to try it out!
x = rx.cell(3)
y = rx.cell(5)
z = bind -> x.get() + y.get() #1
z.get() # 8
x.set(1)
z.get() # 6
I wonder when #1 will be simply expressed as z <- x + y.That said, you could imagine a simple implementation by reflecting on each sub-expression (or auto-lifting operators/functions), where something like `z <- x + y` is compiled to:
z = bind ->
tmp0 = if x instanceof ObsCell then x.get() else x
tmp1 = if y instanceof ObsCell then y.get() else y
tmp0 + tmp1
Rather than forking CoffeeScript for the `<-`, you could start with a simple expression parser: z = rx.expr('x + y')Couldn't `bind` be ordered (meaning always reading its input) to avoid '.get()' ?