top | item 6936975

The Future of JavaScript MVCs

564 points| swannodette | 12 years ago |swannodette.github.io

156 comments

order
[+] ibdknox|12 years ago|reply
We've been thinking about this a lot lately for some of the projects we've been doing for Light Table and we've essentially been doing the same thing as what David's proposing here.

What react ultimately opens up is a way to do immediate mode UI [1] on top of the DOM _efficiently_, which changes things pretty dramatically. It means we can start to treat the browser as just a renderer and get the infectious design decisions of the DOM out of our programs. If nothing else, this gives us freedom, but as david is suggesting, I think this also gives us an opportunity to treat UI much more directly than we currently are. If you want to know what the state of your UI is, you just have to read linearly down through the code that produces your tree. No nest of dependencies, no state hidden in the UI components, you could even get rid of event hierarchies if you wanted.

More important than anything else, this gives us a chance to dramatically simplify our model for UI and magically be even faster than we were before. Sounds like a win to me.

[1]: http://en.wikipedia.org/wiki/Immediate_mode

[+] cia_plant|12 years ago|reply
If you want to do immediate mode, then why would you use the DOM at all? Canvas is supported in IE9+, and is much less complicated for such applications.
[+] ThomasDeutsch|12 years ago|reply
I thought that "Aurora" would be something like OM :)
[+] bretthopper|12 years ago|reply
There's two big highlights for me:

> Thus we don't need React operations like setState which exists to support both efficient subtree updating as well as good Object Oriented style. Subtree updating for Om starting from root is always lightning fast because we're just doing reference equality checks all the way down.

I don't think anyone actually likes using explicit setters/getters in frameworks like Backbone and Ember. Of course Angular avoids it but that's by the crazy "dirty-checking". Obviously the new Object.observe will help this situation, but I love how simple Om/CLJS makes this.

> This also means that Om UIs get undo for free. You can simply snapshot any state in memory and reinstate it whenever you like. It's memory efficient as ClojureScript data structures work by sharing structure.

> VCR playback of UI state

I can't wait for details on this. This has gotten me really excited about client-side apps again.

[+] DannoHung|12 years ago|reply
Are there a lot of details to explain? An immutable data structure means that modifications to the structure tend to be memory cheap. So you save every instance you care about secure in the knowledge that it doesn't waste much space. Then to perform forward or backward operations, you just switch which version you're looking at.
[+] porker|12 years ago|reply
>> VCR playback of UI state > I can't wait for details on this. This has gotten me really excited about client-side apps again.

Why? I'm curious what I'm missing about it and the possibilities you see?

[+] asolove|12 years ago|reply
Om looks very interesting and seems to handle exactly what I've been looking for. We have reactive widgets, which is great for making changes in the data automatically update the UI. But the hard part is closing the loop: how does the widget communicate back to the data about changes? It would be interesting if we had a zipper-like abstraction, so that the widget gets handed both its data and a function to call when it wants to change just its data. Then that function is smart enough to go find the right place in the big data structure to go do the replace.

Edit: Ok, I now see how the Om todo example is handling update, and it's really cool. It creates a set of channels that encapsulate the knowledge of how to handle each type of change to a todo [0]. That gets passed in to the todo widget as "chans" and the widget sends messages to it in its event handlers [1]. I wonder if this whole channel CRUD abstraction is general enough to make it part of Om or another layer so that it didn't have to be recreated each time.

[0] https://github.com/swannodette/todomvc/blob/gh-pages/labs/ar...

[1] https://github.com/swannodette/todomvc/blob/om/labs/architec...

[+] dustingetz|12 years ago|reply
> so that the widget gets handed both its data and a function to call when it wants to change just its data

this is precisely how React works.

[+] swannodette|12 years ago|reply
I apologize that the Om TodoMVC version is a little bit buggy at the moment, I put it together mostly to demonstrate the benefits of the React/Om model and it appears I missed a couple of TodoMVC behavior issues as they weren't important for demonstrating the approach - I'll try to clean up these annoyances later this evening.

Feel free to ask any questions.

[+] _sh|12 years ago|reply
Thanks for posting this. Forgive me for not looking over your code before asking: how does Om compare with Pedestal? Am I correct in thinking there is a lot of architecture overlap, with Pedestal's application state map occupying a similar role to React's virtual DOM, and mutation through message tuples in channels?

Both frameworks seem to offer similar advantages: decoupled model and view, UI state playback (VCR style) and instrumentation. How significantly do these frameworks differ? Or are we seeing different paths towards a convergence around a new set of model/view practices?

[+] drcode|12 years ago|reply
I know it sounds crazy, but I think your post just outlined the next 5 years of web development innovation, swannodette- This ties together a lot of ideas that are extremely important, for the first time in one place. Thanks for doing this.

I already have my own React.js+Clojurscript bridge for personal projects, because I think it's an extremely powerful web dev combination. I'm glad I can finally abandon my own library for Om!

[+] swannodette|12 years ago|reply
Great to hear, it needs more work :) It's more or less a conceptual foundation at this point, but I think it's going to rock with help from the community.

BTW, it was your React mailing lists posts that also really got me digging into this.

[+] bguthrie|12 years ago|reply
I agree––I've found this tremendously exciting. Thanks @swannodette for the code and the writeup.
[+] EvilTrout|12 years ago|reply
> Om never does any work it doesn't have to: data, views and control logic are not tied together. If data changes we never immediately trigger a re-render - we simply schedule a render of the data via requestAnimationFrame.

Ember.js has done this since day one with the Run Loop. Additionally it allows to coalesce operations yourself if you need control.

Angular also would not update the DOM as many times as the backbone example as it uses dirty checking to get around this problem.

[+] swannodette|12 years ago|reply
While it's nice that Ember.js provides a run loop that's not enough, just batching all your updates into one place just puts all of the work together, you'll still going to pay for the work and in the worse case you have to hand coalesce, yuck. In Om if there is no work ... there is no work, it's implicit in the system! No need for hand coalescing your state changes.

Angular.js still suffers as shown by the optimization article I linked to in my post. These kinds of typical optimizations can and should be pushed out of the user's hands.

[+] cpprototypes|12 years ago|reply
How does the performance of this compare to AngularJS? Also a common performance issue in AngularJS is when there are too many watchers (such as a large table with many rows and columns) which causes the $digest to become really slow. Would Om/React avoid these kind of performance issues?
[+] avolcano|12 years ago|reply
Yeah, I think it's rather misleading to compare om to the performance of the least optimized and most naive (on purpose, mind you) framework instead of a more robust one.
[+] avolcano|12 years ago|reply
The title of this post is strange. This seems more like the future of JavaScript views than the future of models or controllers.

I don't see a large movement to immutable data structures on the horizon in JS. I can appreciate the performance implications in Om, and would be interested in using React + Mori to the same end, but I'm not sure that it would keep me from having mutable data structures to represent most of my application state.

There are so many now-solved problems in JS MVCs that were a complete trainwreck several years ago - client-side routing, sanely managing data, and intelligently organizing your code base - that all assume mutable data structures and traditional object-oriented paradigms.

This might be the future of ClojureScript (in fact, it should be the future of CLJS, as it's much more elegant than any other view solution I've seen for it), and functional data structures may be a clever way to optimize the DOM, but this certainly doesn't seem like the future of JavaScript to me.

[+] swannodette|12 years ago|reply
React is used by Facebook and Instagram and many others - I think these companies know a thing or two about scaling rich complex client side applications. React + immutable data has been used by Facebook to get order of magnitude performance enhancements.

So I dunno, seems like it might catch on eventually ;)

[+] jaredly|12 years ago|reply
I can imagine client-side routing, sane data management, and intelligent code base organization all without mutating the data of your state. Granted, JS won't move to immutable data structures, but React encourages you to treat your states as immutable, which leads to lots of benefits.
[+] jarpineh|12 years ago|reply
This is like a dream coming true. I don't like Javascript's quirks, nor programming DOM with templates nor functions. I'm looking to build somewhat complex UIs without having to think in JS and manage state. React absolves me from DOM and ClojureScript keeps JS at bay.

Only if starting ClojureScript development wasn't so hard. I'd like to use browser REPL, IDE like LightTable. I am used to LiveReload's speed which makes loading changes instantaneous. But ClojureScript compiling seems to be still slow(ish) and I have already found half a dozen of different cljsbuild configuration examples. Compiling simple cljs files can take anything from sub second to 20 secs, and I don't understand why.

Could you perhaps tell more about your development process, Swannodette? How do you develop ClojureScript apps? I don't see anything beyond base cljsbuild in Om's project.clj. I confess I haven't yet had time to play with Om and see how fast it can be compiled.

[+] swannodette|12 years ago|reply
The first build of ClojureScript app is somewhat slow because we need boot the JVM, compile ClojureScript, and then analyze and compile your code. After the first compile it should be sub second. I always use the auto build mode and when I'm developing I don't use any optimizations. Works well enough for me.

I'd start with this, http://swannodette.github.io/2013/10/27/the-essence-of-cloju...

[+] lucian1900|12 years ago|reply
I don't think ClojureScript could become popular beyond a subset of Clojure users unless it becomes self-hosted, so it can be compiled without a JVM and eval in a browser with an extension. The JVM is not universally loved/acceptable.

I keep meaning to make a game with it, but always give up because it's so much more painful than pretty much any other to-JS language.

[+] taybin|12 years ago|reply
But I just finished rewriting everything in angular!
[+] gfodor|12 years ago|reply
This is really cool, and I am one of those people who rolls their eyes whenever there is another article about some newfangled way to build Javascript apps on HN.

One logical step from here is instead of having a one-to-one correspondence between the "virtual" DOM and the browser DOM is to introduce a higher level meta representation based upon the context. This seems like a logical path towards a generative, projectional approach to controlling UI and browser document rendering in general. It's been tried before in several contexts and the hacks I've tried myself have always been to hard to get my head around since it's a complex problem, but this seems like it could be a really decent foothold to build a projectional, transform-based paradigm. For example, having a meta-DOM that encodes mathematical notation (probably inspired by LaTeX), which gets transformed into the current virtual DOM, which is used to update the real DOM. User manipulates a integral on screen, and the downstream transformations are performed lazily and efficiently all the way to the screen. This type of lazy evaluation from document to screen is essentially the core challenge (from a engineering standpoint) in building a usable real-time projectional editor like that demonstrated by intentional software back in 2010 [1].

[1] http://www.infoq.com/presentations/Intentional-Software-at-W...

[+] dgreensp|12 years ago|reply
I've been watching Clojure for a while, and I love the spirit of this work.

However, as a framework author, I feel obliged to point out that of course you can create a more expressive and performant UI framework in a language with S-expressions, macros, and value semantics. What's hard is doing it in JavaScript. :)

It also feels a bit like reverse logic to cite ever-faster JavaScript VMs as a reason to choose a new framework for performance reasons -- shouldn't it matter less exactly how you structure your application logic when you're running on a "fusion reactor"? -- but I realize there's some subtlety here about lower constants enabling better algorithms. (Still, if your framework includes a language compiler or gets to take advantage of an expressive macro system, it should be able to run on anything.)

Once it's possible to compile ClojureScript without booting up a JVM -- which could happen if it becomes self-hosting -- I'll make a Meteor package for it. I'd also like to see the compile times get a little shorter and the runtime library get a little smaller.

[+] rads|12 years ago|reply
The main features David talks about in the post are available in vanilla JavaScript:

> If you're a JavaScript developer, I think taking a hard look at React is a really good idea. I think in the future, coupling React with a persistent data structure library like mori could bring JS applications all the way to the type of flexible yet highly-tuned architecture that Om delivers.

I think there are a couple reasons Mori and other libraries like it haven't caught on in the JS mainstream:

1. It's clunky to use them in vanilla JS compared to the default mutable objects and arrays. While you gain simpler semantics, the code becomes harder to read, and that tradeoff isn't always worth it.

2. Most JS developers are not experienced with building programs around immutable values.

[+] d_j_s|12 years ago|reply
So I rewrote Backbone views to use a queue system that fires on requestAnimationFrame -https://github.com/danshearmur/backbone-fast-view

I'm getting pretty good results with swannodette's benchmarks - http://danshearmur.github.io/backbone-fast-view/

I'm getting approx 150ms in Chrome for benchmark 1 and about 400 ms for Chrome for benchmark 2

[+] swannodette|12 years ago|reply
About what I'd expect for the first benchmark, but that's the kind of intervention from the user that I think frameworks should not require. The time still spent by Backbone.js on the second one more or less illustrates why I don't believe in event oriented MVCs.
[+] mtrimpe|12 years ago|reply
I've been expecting this after seeing a few of your teaser tweets and as expected I absolute love it! I've been waiting for something like this ever since I read up on persistent data structures and functional reactive programming almost 10 years ago.

I'm wondering how this compare to the Javelin library as that seems to offer the same functionality when combined with hlisp. Would I be correct in saying that Om achieves the same by using ClojureScript's data structures and core.async to offload the FRP part to React?

[+] swannodette|12 years ago|reply
I haven't looked at Javelin enough to have a strong opinion about it. Om at the moment is completely focused on rendering EDN data and making that blazing fast. Personally I think representing UI components as generic data has a lot of legs.

I also don't really believe in templating languages, but I also think that functional boilerplate for rendering children is a bummer. I'd like to see a client side query language modeled after Datomic's Datalog syntax instead.

[+] rubiquity|12 years ago|reply
What about long GC pauses and running for cleaning up all those wonderful immutable data structures? Immutability is great when it's at the forefront of how a language is designed. Plastering immutability all over hot code paths in a language that wasn't designed with immutability in mind isn't great.
[+] ibdknox|12 years ago|reply
In practice, unless you're animating at 60fps over hundreds of objects (and who would do that with dom elements?), you shouldn't run into any GC issues. LightTable uses ClojureScript datastructures for pretty much everything and there were only a small handful of cases we had to optimize. Any "normal" application probably won't ever have to.
[+] programnature|12 years ago|reply
So far its not a big deal. I haven't seen perceptible GC pauses, not to say they aren't possible for some use case.
[+] coldtea|12 years ago|reply
>What about long GC pauses and running for cleaning up all those wonderful immutable data structures?

Yes, what about them? Have they been measured to be a problem with this approach?

If anything, it leads so colletions of objects of small lifetimes, that are quickly collected by the GC.

[+] jordwalke|12 years ago|reply
Jordan, from the React core developer team here. Awesome post, swannodette! This is exactly how we intended React to be used. As swannodette said, at Facebook, we use persistent data structures, in order to prune the update search space for comment updates. We've seen as much as a 10x improvement in update speed for certain operations.

React is a really great fit for Om, persistent data structures, and functional programming in the following ways:

1. We want to allow developers to elegantly describe their user interface at any point in time, as a pure-as-possible function of data dependencies.

2. We allow hooks for your system to help guide the updating process along. These hooks are not necessary. Often, we'll add optimizations long after we ship. We strongly believe that perf optimizing shouldn't get in the way of writing code elegantly and shouldn't get in the way of the creative development process and actually shipping to your users. At the same time, performance matters - a lot. So we ensure that at any point in the update process, if you know better than the framework, you can help guide the system. The fact that this is optional and doesn't change the functionality or correctness of the system is critical. Persistent data structures are an excellent (likely the very best) way to hook into the update system without making the developer do anything special.

Some people here were wondering about the apparent OO influence in React. Here's how I personally think of React's OO support/influence:

1. It's there to help you bridge with other existing mutative, stateful libraries in your stack - you know you have them. The DOM falls into this category as well.

2. It's there when you want to treat state as an implementation detail of a subcomponent. This is only because we don't have a good way of externalizing state changes, while simultaneously keeping the nature of them private. We just need more people to think about it (I'm sure the ClojureScript community can help us chew on this). Our internal motto is to keep things as stateless as possible.

3. A lot of the OO support in React is there as a concession, more than being considered a virtue. It's really cool to have the FP community involved in the UI space. Those people are already sold on FP and statelessness and get the luxury of programming in tomorrow's paradigms today (how ironic that FP has been around for decades!) To accelerate this momentum, we also want to reach out to people who aren't yet sold and change how they think about building UIs and software in general. The most effective way to do this is to reach out to them where they stand today, on some middle ground. It's really great to see eyes light up when they see that they can use simple functional composition in order to build large, sophisticated apps.

We're really glad to have swannodette and the ClojureScript community checking out React (github.com/facebook/react). We should consider adding some level of support for persistent data structures in the React core. Let us know if there's anything we can do to help.

[+] programminggeek|12 years ago|reply
There was a really good talk from Charles Nutter about making JRuby fast and the mutable things that Ruby does that basically break caching and things that you do to make things faster at runtime.

I'm not surprised that persistent data structures can make things fast, in fact I've spent the last week speeding up a Rails app in some ugly spots by preloading the data structures to keep DB queries from happening, effectively turning a lot of just in time queries at the ORM level into a pre-loaded data graph. The speed is fantastic, but what is interesting is you could add a level of immutability to this and would be potentially even faster, especially on top of the JVM.

I've been playing with the idea of immutable entities in Obvious Architecture for a while and it really changes the way you look at your business logic and performance.

[+] rtfeldman|12 years ago|reply
This kind of thing is exactly what makes me think CLJS is the current frontrunner to be the first compile-to-JS language to gain mass adoption without JS-like semantics (as CoffeeScript has).

It's the performance.

So many of us who want JS alternatives have made our peace with the idea that we'll have to sacrifice a bit of performance if we want to use a nice language.

But being able to improve performance while using a nicer one?!

Count me in! I already have a serious project in mind for this.

[+] TheHydroImpulse|12 years ago|reply
For me, the non-idiomatic compiled JavaScript and Google's library that's stopping me. Wisp seems like a good solution with it being a Clojure dialect that provides super clean JavaScript that you can work with.
[+] _pmf_|12 years ago|reply
On another note: maybe if the installation of ClojureScript would be manageable in, say, an hours instead of having to search half a day among outdated information, more people would try it (and this is from someone who already uses Clojure and Leiningen a bit).

Clojure and its libraries has the worst documentation, and this malpractice seems to be continued in ClojureScript.

[+] davedx|12 years ago|reply
I had a few teething problems getting it configured properly, but I managed to get ClojureScript working from scratch on a Windows machine in about an hour, I think. What problems did you have? (Mine were Java related)
[+] chc|12 years ago|reply
It's as easy as "lein new mies". Hard to get much easier.
[+] invalidOrTaken|12 years ago|reply
This is fantastic. I've been goofing around trying to figure out a way to approach the general case (representing an interactive DOM with just EDN), but I hadn't done jack on performance. Thank you thank you David.