5 ms·
I appreciate the skepticism :) But remember the goal of Om is to be simple and fast in more scenarios than are allowed in traditional designs. Emphasis on simpl
by swannodette 13y ago
I appreciate the skepticism :) But remember the goal of Om is to be simple and fast in more scenarios than are allowed in traditional designs. Emphasis on simple.
Take a look at my undo example implemented in about 5 lines of relevant code
http://swannodette.github.io/todomvc/labs/architecture-examples/om-undo/index.html http://swannodette.github.io/todomvc/labs/architecture-examp.... Note the render time when undoing changes, much less than 16ms. That's undo playback at 60 frames per second.
The ability to snapshot the application state is not a small thing, it's a global application concern with lots of benefits besides user level undo - UI rollback on network request failure, app state restoration sans boilerplate when the user cancels something, simpler model for offline applications, and UIs based on scrubbing come immediately to mind.
I'm sure you could achieve similar or better performance in a system that relies on events with batching and mutable objects but I'm not convinced that it can be done as simply as we can do it today in Om.
- skrebbel 13y agoDavid, thanks for your work on Om and, especially, writing about it. Your writing introduced me to React and how to use it with a de-emphasis on state. Having very little FP experience and zero Clojure experience, however, sometimes you're going a little fast for me, so was hoping whether you or someone else who reads this could help me out. I don't completely understand how entirely immutable data can be effectively used in front end apps (I do understand how, if you have immutable data, React can be made fast and undo is easy). If data changes, it needs to mutate, right? Do you then maintain one global object that does change, and have that object be a giant immutable tree with all model data? Or am I completely missing the approach? If that's the way, how can you efficiently do updates on relatively large chunks of data? Say, I'm coding Gmail and my user wants to star the 2053rd email in a list? Doesn't that mean 2052 items in a list have to be copied? In general, doesn't this make model code much much more complicated? Or am I missing something and is it really not that bad? Yeah, a bit many question marks, but I hope you catch my drift :) If anyone could point me to some good reading about thinking in immutable data terms, that would be warmly appreciated too. Thanks!
- swannodette 13y agoYes in Om the application state is treated like a database - it contains all the relevant application data. However we don't encourage accessing the application state as a global if it can be avoided. Components should only receive the parts of application state they need to render. Updating the 2053rd email in an immutable vector is not going to copy the previous 2052 items. ClojureScript data structures are modeled directly on Clojure's - structural sharing avoids needless copying.
- skrebbel 13y agoYeah, good point on the not-global. I didn't mean globally accessible, more like, a single "current state" object, rather than many little ones as you would see in a typical backbone application. Thanks!
- programnature 13y agoFast persistent data structures, combined with the idea of "reference types" that encapsulate mutation points when you really need them. http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey http://www.infoq.com/presentations/Value-Identity-State-Rich... So yes in Om there is a single mutable point which then points to the current immutable tree to be used for rendering. This is very fast. Clojurescript performance is not a bottleneck, most of the time is spent within React.
- grayrest 13y agoIn order to understand the frame of reference for Om, you really need to watch some Rich Hickey presentations [1][2] (or have a good understanding of functional programming). The first one is more relevant to your questions here, the second is just because Clojure programmers have very specific definitions of "simple" and "easy" and because it's a good talk. [1] http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hickey http://www.infoq.com/presentations/Are-We-There-Yet-Rich-Hic... [2] http://www.infoq.com/presentations/Simple-Made-Easy http://www.infoq.com/presentations/Simple-Made-Easy That said, on to answering your questions: > If data changes, it needs to mutate, right? ... Data doesn't change in this model (see [1]). Incoming events (keyup, network request, page load) occur and your program responds to those events by producing a new, immutable set of data that you're going to use going forward. Om apps keep one reference to the root of the data tree–Om examples use app-state as the name–which represents the official current state of the data. You can reasonably argue that swapping out app-state is changing data except that anything that had a reference to the previous root(s) still has that reference and you have to go dereference app-state to get the current value instead of having it changed out from under you. > If that's the way, how can you efficiently do updates on relatively large chunks of data? ... Or am I missing something and is it really not that bad? [3] http://eclipsesource.com/blogs/wp-content/uploads/2009/12/clojure-trees.png http://eclipsesource.com/blogs/wp-content/uploads/2009/12/cl... Clojure(script)'s data structures use structural sharing. The above picture shows inserting a single node into the middle of the structure. The red outlined nodes are the parents which need to be copy+updated, as shown on the right with all the dotted lines being shared references. The most misleading thing about the picture is that the actual cljs trees have 32 branches at each node instead of 2 or 3 so the lookup time is log32 N (basically constant [4], impl in a systems language vs classic datastructures for comparison). In your Gmail example you'd have to make ~3 new nodes. [4] https://github.com/michaelwoerister/rs-persistent-datastructures https://github.com/michaelwoerister/rs-persistent-datastruct... > In general, doesn't this make model code much much more complicated? It requires a different mindset and generally some helper code. In Clojure using Om it's pretty straightforward once you're over the initial hurdle. In javascript using Mori [5] it looks a lot like awkward Backbone with very heavy Underscore use. I've poked around at it and if I were going to try to adopt Mori+React for a real project I'd want to do some quality of life tweaks on Mori. Mostly setting a prototype on the Mori objects to get the feel closer to Backbone+Underscore and trying to get the data structure console output to be more useful. [5] https://github.com/swannodette/mori https://github.com/swannodette/mori
- haberman 13y agoHi there -- thanks for your work on Om, it's helped to bend my mind and think about different ways of building apps. I agree completely that the easy snapshot ability is very cool and it wasn't my intention to minimize it. The main reason I dug into this so deep is because after reading your article I had a nagging feeling that using anything other than React or immutable data structures was an architectural dead end. But I'm not so worried about that now; I think that any framework that aggressively batches DOM updates will probably be ok performance-wise, and I think that mutable data structures can perform well. Also I think that some features are easier to provide with mutable structures, such as automatic two-way data binding a la Angular. How do you propagate DOM changes to your models? Manual event handlers or do you have something more automatic? I definitely think that immutable data structures are worth consideration for their snapshotting ability though.
- swannodette 13y agoI personally don't see much value in automatic two way binding. It's a little bit of convenience that's really easy to build over React/Om if that's your cup of tea, http://facebook.github.io/react/docs/two-way-binding-helpers.html http://facebook.github.io/react/docs/two-way-binding-helpers.... Immutability doesn't present any challenges here.
- haberman 13y agoReactLink (which you linked to) calls setState() on the React component state when the DOM changes -- wouldn't this violate the assumptions of your shouldComponentUpdate() implementation?
- swannodette 13y agoWe don't use setState, but we have om.core/set-state! which does pretty much the same thing - an OmLink would not be very hard to do.