vrac

Template-based web library [WIP] - https://github.com/green-coder/vrac Zulip archive: https://clojurians.zulipchat.com/#narrow/stream/180378-slack-archive/topic/vrac Clojureverse archive: https://clojurians-log.clojureverse.org/vrac
2020-08-25T01:54:46.070700Z

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.

2020-08-25T01:55:46.071100Z

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.

2020-08-25T02:46:15.094900Z

(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.

2020-08-25T02:48:20.095600Z

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

2020-08-25T02:50:30.097Z

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

2020-08-25T03:19:57.101500Z

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.

2020-08-25T03:30:30.107600Z

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).

2020-08-25T04:26:33.124Z

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.

2020-08-25T04:38:23.132800Z

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) .

2020-08-25T04:51:44.134600Z

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.

2020-08-25T04:52:43.134900Z

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