Classes and instances
The biblo
Section titled “The biblo”The biblo is the class registry and the instance store. A class gives:
cells: the data of each instance. Each cell has an expression, and optionally atype(the class of a child instance) andbindings.methods: the behavior, for examplerender,hydrate,dehydrate,splash,flowandderef.extends: the parent class. The cells and the methods resolve through the extends chain, and the most specific one wins. A cycle in the chain stops at the first class that repeats.
Instances
Section titled “Instances”instantiate(b, store, className, parentId?, bindings?) makes an instance. The instance gets an ID and a scope (self, parent and children). Its root node is a container with one slot for each cell. All nodes evaluate before the end of the function.
- In a cell expression,
ref("self", ...)andref("parent", ...)become node IDs. Afnparameter with the nameselfstays a parameter. - A typed cell makes a child instance of its class. Its bindings are in the scope of the child:
{ value: ref("parent", "key") }binds the cellvalueof the child to the cellkeyof the parent. - A binding of the caller replaces a cell. A binding for a typed cell makes it a plain cell.
destroyInstance(b, store, id) is the inverse. It destroys the children first, then removes the nodes and detaches the instance from its parent. The readers of the removed cells become none in one epoch.
renameClass(b, from, to) renames a class. Its subclasses, the typed cells that use it and its live instances get the new name. moveChild(b, childId, index) changes the place of a child among the children of its parent.
Classes are templates
Section titled “Classes are templates”updateClass(b, store, cls) registers a new version of a class, and the live instances follow it. For each instance of the class or of a subclass:
- A cell that still has the old expression of the class gets the new expression.
- A cell with an edit of its own, or a binding, keeps it.
- A new cell is added to each instance, and a removed cell is removed.
Change the label of the class below. The instances without their own label follow the class. Change the label of one instance: that instance keeps its label after the next change of the class.
| instance | its own label | text = concat(label, " × ", count) |
|---|---|---|
| A | "star × 1" | |
| B | "star × 1" | |
| C | "star × 1" |
Traits: views for each structure
Section titled “Traits: views for each structure”A trait gives methods to each class with a set of cells, whatever its extends chain:
registerTrait(b, { name: "Point", requires: ["x", "y"], methods: { summary: app("summaryView", ref("self", "classRef"), app("concat", lit("("), /* ... */ lit(")"))) },});Each cell and each typed cell (kid:Point) gets a unique prime, and the fingerprint of a class is the product of its primes. A trait applies when its fingerprint divides the fingerprint of the class (resolveTraits). Only the most specific traits count. When two of them give the same method, the method is ambiguous, and no trait gives it.
resolveMethods puts the traits between the root of the extends chain and the other classes of the chain. Thus a trait changes a default of Top, and a class changes a trait. The page level of detail shows the traits of the summaries.
Class-level reactive semantics
Section titled “Class-level reactive semantics”classNodeOps(b) makes the NodeOps of a store from the classes. The splash, flow, deref and targets of a node come from the class that owns the node, through the extends chain. The targets op gives the nodes that a read goes through. A class with its own deref gives its own targets too, thus its reads stay reactive. The class Top gives the defaults, and each class can change them. Give the result to nodeStore:
const b = biblo();const store = nodeStore({ nodeOps: classNodeOps(b), ops: standardOps });The node layer knows nothing of biblo. This factory gives the class semantics to the node layer without a dependency in the wrong direction.