Fork me on GitHub
#vrac
<
2020-08-25
>
Vincent Cantin01:08:46

Good morning. In my brainstorming yesterday, I was trying to do 2 things: 1. Skipping some potential computations (in the compute node) where the result would not be used in the dom, for example if the elements are being removed. Today I realized that it is probably not a practical concern and will just forget about it. 2. Avoiding a pass over all the html elements from the root to the leaves to see what data has changed, each time something changed in the local db.

Vincent Cantin01:08:46

Vrac is different than React in the sense that I don’t want the user to think at all about how to organize his data to optimize the rendering process. Therefore, some of the optimizations React does during its pass over the vdom tree cannot be used in Vrac. However, Vrac templates has the advantage to be an open book, everything inside is readable, and I should use it to conduct the rendering optimizations.

Vincent Cantin02:08:15

(Note: by “html node”, I mean structures managed by Vrac whose function is to update the DOM nodes created by the browser) If I take the approach of re-computing all the “compute nodes” (non-including the html nodes) each time their inputs change, there are things I can do more easily on the render part: 1. If the compute nodes have a reference to their output nodes, I can filter them by type and get a set of the html nodes which have to be updated. 2. From this set, I differentiate the “structural nodes” (which may delete/create some html nodes under them) from the “content nodes” (which are all the other html nodes). 3. I sort all the structural nodes by topological order, and ask them to re-compute. In that process, some html nodes may be created (and may create and register some compute nodes), and some html nodes may destroyed (and may unregister some compute nodes). The destroyed structural nodes should be skipped from the current sequence of nodes to re-compute, and the newly created one can be re-computed afterward, independently. 4. The content nodes which have not been destroyed and whose inputs have changed can be updated in any order. Those nodes are responsible for all the changes which will ever happen to the DOM nodes.

Vincent Cantin02:08:20

In this way, I am avoiding a pass on the whole app’s html nodes tree.

Vincent Cantin02:08:30

The topological sort of the structural nodes should be based on a depth value in the sub-tree made of only the structural nodes.

Vincent Cantin03:08:57

The difference with an optimized React renderer is that React would re-render a whole component each time it detects that some of its inputs changed, and look for a diff in the vdom output. I think that my approach (which is based on https://github.com/thheller/shadow-arborist) would be a lot faster. I should probably stop thinking about optimizations past this point and focus on how to make Vrac more convenient for the user.

Vincent Cantin03:08:30

One last thing I want to do brainstorming about is how to improve the data access and navigation. There are some compute nodes which are used only for data navigation. They transform a value representing an entity (in the local db) into another value representing another entity in the db. I feel that they would be a lot to gain to have this type of transformation more transparent, in a way which can be understood by Vrac as a transformation that navigates from an entity’s location to another entity’s location (assuming that entity location would be equivalent to its identity).

Vincent Cantin04:08:33

My goal is to calculate a canonical path to navigate to a data, so that the user don’t have to think about it. Is here a fictional and abstract example, just to illustrate my idea, a non-canonical path to a data as could be extracted from a Vrac template:

(-> current-user-id get-user :user/id get-user :user/id get-user (get-blog 3) (get-blog-author 0) :user/name)
Canonical path to the same data:
(navigate-in-as db x [[:current-user-id] [:users-by-id x :blog-ids 3] [:blogs-by-id x :author-id 0] [:users-by-id x :name]])
Now if the user point its mouse to the name of the blog's author in the web page and ask Vrac where this data comes from, instead of showing a path of compute nodes (the non-canonical path above), we can show where the data is in the db (`[:users-by-id 42 :name]`) and also show the canonical path that leads to it. We can also show the non-canonical path, but the user will be happy to forget about it.

Vincent Cantin04:08:23

In a re-frame app, there is often the choice to make on how we pass a reference to an entity from parent component A to child component B, using its id or its full value. If we already have all the fields of the entity in A, we could choose to pass the full value of the entity to B. But if we don’t know who else in the future will need to include B, we might choose to only pass the entity’s id, to not force any other component C to have the full entity prior to include B. Passing the entity id is the choice which is the most robust to future development, so most sane programmers would do it. But the consequence is that the app has to re-subscribe to it in B even if it was already subscribed in A. The consequence of that pattern leads to non-canonical subscriptions in the app, like (-> current-user-id get-user :user/id get-user :user/id get-user) .

Vincent Cantin04:08:44

This idea of “canonical path to data” will need time to mature and be practical, but it won’t block the rest of Vrac’s design.

Vincent Cantin04:08:43

— end of my daily “open brainstorming”, feedback welcome as always.