

Reactive.coffee: reactive programming and declarative UIs in CoffeeScript - jashkenas
http://yz.mit.edu/wp/introducing-reactive-coffee/

======
xyzzyrz
Library author here - happy to answer questions! Canonical blog post is
actually over at [http://eng.infer.com/post/57598286968/introducing-
reactive-c...](http://eng.infer.com/post/57598286968/introducing-reactive-
coffee-reactive-programming-and). Project home page, courtesy of GitHub:
[http://yang.github.io/reactive-coffee/](http://yang.github.io/reactive-
coffee/).

Also, our startup, Infer, is hiring great engineers, and we love open-source -
come learn about us:
[https://www.infer.com/careers.html](https://www.infer.com/careers.html)

(Thanks jashkenas!)

------
tqs
This is a really nice cleanup/update on Knockout. The source code at ~400
lines is very understandable. I've looked at a lot of reactive frameworks,
this is great work!

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...](http://emberjs.com/guides/understanding-ember/managing-asynchrony/)

Also since Object.observe is asynchronous, Google's polymer/MDV will also I
believe have these semantics.

~~~
xyzzyrz
Yes! There's already a lag-delay primitive, but it's a one-off hack we needed
to get things working - more general asynchronous propagation is definitely a
requirement.

------
madzanderzen
"z = bind -> x.get() + y.get()" seems nice, but when are the attached
listeners removed again?

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?

~~~
xyzzyrz
Astute observation - this is actually something we do call out in
[http://yang.github.io/reactive-
coffee/infelicities.html#garb...](http://yang.github.io/reactive-
coffee/infelicities.html#garbage-collection). As you note, JS doesn't have it
as easy as in other platforms, but it's not a fundamental problem and is still
reasonably straightforward to address (as described). The fix is slated for
this coming release, and we'll need to more thoroughly document/explain what
this all means.

~~~
madzanderzen
Thanks for the explanation in the link. The problem that is described in the
link is clearly solvable, however what I'm referring to is more fundamental.

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?

~~~
xyzzyrz
Yeah, this is what I'm talking about - a fuller explanation would require more
text. :)

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.

~~~
madzanderzen
Ahh, you're putting the entire UI in binds. That's a really clever way to
handle the lifecycle of the listeners!

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).

~~~
xyzzyrz
A bind only re-evaluates if changes happen within its own immediate scope, and
not if the changes are within any descendant binds' scopes. Whenever you want
to carve off finer-granularity updates, scope things to a separate bind. This
has scaled to, for instance, a complex WYSIWYG web page editor, where the
whole page/document being edited is structured using cells and rendered with
recursive binds - the app needs to be very responsive to each change a user
makes, whether it's typing in text or dragging a style slider, even if the
focus is on the top-level DOM element. That's not to say we've seen all the
use cases - we definitely want to see where things fall down as well.

~~~
madzanderzen
Just one last question...

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.

~~~
xyzzyrz
Think of cells (including binds) as just containers: when you call .get()
(inside or outside a bind), you just get the currently stored value. Besides
the container abstraction for its own sake, values also enable the scoping
effect described earlier - if you have x.get() + y.get(), and x updates, you
don't need to re-evaluate y (which in turn may be a bind - its body is scoped
off so that you're not re-evaluating an entire sub-graph of the application).
They're also necessary groundwork for smarter propagation strategies, e.g.
stopping propagation if the value hasn't changed.

------
jkarmel
Really cool stuff. I love the simplicity of reactive cells and I've been
writing views in coffeescript for a while now.
([https://github.com/jkarmel/space-pen](https://github.com/jkarmel/space-pen))

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/](http://jsfiddle.net/EwfB8/2/))

Any plans to add a feature for dealing with this issue?

------
judah
Interesting project. I'm uneasy about zero HTML, and instead having most/all
the HTML in .coffee. But I'm opened to it.

Is this project related to, or draw inspiration from, Reactive Extensions
(Rx.NET, Rx.js, Rx.cocoa, etc.)? [https://github.com/Reactive-
Extensions/RxJS](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.

~~~
xyzzyrz
We elaborate briefly on various related work at
[http://yang.github.io/reactive-
coffee/related.html](http://yang.github.io/reactive-coffee/related.html), but
absolutely - there's a large body of existing work and trailblazers we
liberally draw inspiration from. In the case of RxJS in particular, the styles
actually feel quite different - RxJS is more about explicitly merging streams
and constructing the data flow graph with some set of combinators, whereas
dependencies are inferred automatically in reactive.coffee (along with a UI-
building layer).

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/](http://marcuswest.in/essays/fun-
intro/)

[http://www.impredicative.com/ur/demo/](http://www.impredicative.com/ur/demo/)

[http://www.meteor.com/](http://www.meteor.com/)

[http://derbyjs.com/](http://derbyjs.com/)

~~~
zcrar70
Facebook's recent React framework is also worth mentioning
([http://facebook.github.io/react/](http://facebook.github.io/react/)).

A nice JS library for reactive programming is Bacon.js -
[https://github.com/raimohanska/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.

~~~
xyzzyrz
Indeed, both are mentioned in the related work page!

------
rtfeldman
This looks like just the sort of thing I've been looking for. Reactivity is
very convenient, but it's so often tangled up in a massive library that's
trying to do much more at once.

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!

~~~
peterhunt
You may be interested in what we've built with React
([http://facebook.github.io/react/](http://facebook.github.io/react/)). We're
focused exclusively on rendering and wiring up event handlers and offer a
super straightforward, highly performant reactive programming model. There is
some particularly interesting technical aspects around how we keep state
consistent between React and the browser as well as how we've architected the
core (it's a bit like a game!). And it's been proven at scale (both userbase
and eng org size) time and time again.

~~~
xyzzyrz
Hi Peter - React is great! To those of you who haven't already, go check it
out. It shares a lot of similarities with reactive.coffee (besides our highly
original names), including the "code-first" approach to building up views.

~~~
peterhunt
Yep glad that we're validating each other :) Also ractive too!

------
icambron
I really like this. As an avid user of Coffeescript, Knockout.js and Erector-
style code-as-templates, this is a great fit for me. Additionally, while I now
understand how Knockout works internally, I was initially frustrated by how
much magic it involved; Reactive.coffee looks like it does a better job of
exposing the relevant parts of how it works.

------
invalidOrTaken
Do you feel that you've lost anything in terms of composeability as compared
to a more stream/combinator-oriented approach, like, say, Flapjax? If so, have
you found that painful in the real world, or do you think it's more of an
academic concern with little meaningful impact?

~~~
xyzzyrz
Good question - the reason why we built the library this way was exactly
because we find declaring expressions to be more natural than wiring together
streams by combinators—which, BTW, Flapjax does support. Far from academic-
without-impact, Leo's work was actually directly influential on me—esp. since
he worked in PL and I didn't, so much of my initial exposure to reactive
programming came from a few projects including his.

~~~
alipang
I'm a big fan of bacon.js. Is there any way you can abstract over values over
time, or asynchronously? This was always my big problem with knockout.

------
agumonkey

        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.

~~~
xyzzyrz
That would require a new language/syntax layer, which we wanted to avoid.
(There's also something to be mentioned for being explicit and reducing
"magic," which was part of what drove us to create this library in the first
place.)

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')

~~~
agumonkey
Right, I've been doing too much lisp recently, I assume macros everywhere.

Couldn't `bind` be ordered (meaning always reading its input) to avoid
'.get()' ?

