Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I love technical articles about how libraries are built. This is really great!

I don't follow the last part of the article though. Specifically this part:

> We accomplish this by (under the hood) allowing every bit of data that goes into a template to separate the process of computing its current value from the process of determining whether the value might have changed.

When you say "bit of data", what exactly do you mean? I assume you mean some field that is bound to a template. Like a property on glimmer component. Using KVO this would be a compute of some kind, that triggers an event when its value changes.

Perhaps you're doing something totally different, but I don't see how? Breaking it down, we have an object like:

   {
     "foo": "bar"
   }
I think you're saying that you don't observe this object (using some kind of obj.set('foo', 'baz') convention) but rather determine that the value changed by some other means. If that is so, what is the other means?


Thinking about it a bit more I think might know what is going on here. In a traditional KVO view layer (I've worked on a couple of these myself, so my thoughts here are biased) when a property changes it triggers an event. The view layer is listening to this event and so it knows to update the DOM to reflect the changed value.

What this means is that the view layer only works with evented KVO objects. What Glimmer has achieved is the ability to work with other types of data management systems. Knowing that is key to understanding (for me, at least).

So given that, what must be going on is that the view layer gets the value of a property with some interface. That interface implementation is provided by the view model so it differs. For plain objects it might just be `return obj.foo`. This will make getting a value really fast. No notification systems are required.

For updating the view layer there must be some generic way of telling it "hey, the obj.foo property might have changed (or maybe not, shrug), figure it out for yourself". Then using the update VM it is able to figure if it really did change, and only update the subtrees if it did.

Or something, this my interpretation. Sounds smart!


Yep, this is pretty spot on!

At a high level, the Glimmer VM is built on top of two primitives:

1. References (as you describe, an interface implementation to provide the underlying value)

2. Tags, an interface that communicates "is it possible this value has changed?"

Every reference has an associated tag, so once you have a reference, at any time you can ask: Is it possible this thing has changed? If so, what is it's value?

In KVO systems, every time a dynamic value is used in the template, that view usually adds an observer on to the root object. This adds a fair bit of overhead to both rendering and teardown. And if multiple properties change, you have to figure out the optimal re-rendering strategy. (E.g., if a value inside an `if` changes, you probably don't want to re-render it if the conditional also changed from truthy to falsy!)

Today in Ember, the view layer no longer sets up observers. Instead, during rendering, we create a tag for each property. When you mutate a property (this.set('firstName', 'Matthew') for example), two things happen:

1. That tag is marked as dirty.

2. A revalidation of the entire tree is scheduled.

The revalidation process starts from the top of the render tree and walks down, asking every reference/tag "is it possible you've changed?" Because this is just an integer comparison, it's very very fast on modern JavaScript VMs, even if you have lots of data on screen.

The tag is like a Bloom filter, though. It means a change MAY have happened, not that one necessarily did. If the tag is dirty, we do a last chance identity check for primitive values. Only if it has actually changed do we update the DOM.

One nice thing that falls out of this is that the application developer can change as much component state as they'd like at once, and we can avoid doing any expensive computation to figure out the optimal place to start re-rendering (the `if` case I mentioned above). By revalidating the tree from the top down and keeping constant time factors low, you get that optimization "for free."

The other nice thing is that you can express all sorts of cool semantics on top. For example, if you have immutable data you can attach a tag that always says "I'm never modified." If you don't want to do any bookkeeping at all, you can attach a tag that says "Always recheck me to see if I've changed." Best of all, as Yehuda mentioned, you can mix and match these semantics in your components. It also allows the data to drive the change semantics, not the component, which you often don't want to have care about how model data might change.

If this is interesting to you, there is some WIP documentation in the Glimmer VM repository that talks more about the philosophy behind references and tags:

https://github.com/glimmerjs/glimmer-vm/blob/master/guides/0...

https://github.com/glimmerjs/glimmer-vm/blob/master/guides/0...


Thanks, this explanation is very helpful. One last question, when you say:

> Because this is just an integer comparison, it's very very fast on modern JavaScript VMs, even if you have lots of data on screen.

I don't follow, what is an integer comparison? Don't you have to compare the reference's value to the previous value? Meaning you need to get that reference's value. That reference could be somewhat expensive. Imagine if it is a property getter that calls into a few more functions.

This seems like the disadvantage of not using KVO, you don't know a property's value without asking for it. with KVO the value is cached and only changes when the underlying dependency tree changes. Of course vdom works without this, so it can't be that bad.

But I'm guessing you've found a clever way to avoid having to call out to get the reference's value to compare, so what is it? :-)

And I'll definitely read me in those docs, thanks!

EDIT: Probably should have read the docs first. A global counter id! Very clever... I wonder if you could get away with just a lastModified though. The view layer could keep track of the last time it updated and then just compare lastModified, if something has changed since the last time it updated then it must have changed too.

Going to think some more about this, thanks for the puzzle!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: