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:
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!
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...