The rendering algorithm of forester should be an incremental computation: After
making an edit to one or more trees, we should only rerender those trees
affected by the changes. Not only would this make forester much faster,
it will (hopefully) also have positive repercussions on the development of the
language server and perhaps [silviculture](https://github.com/LocalCharts/silviculture)
# Structure of the problem
We can divide the problem of dependency into two classes:
Dependencies evaluation-time and render-time dependencies.
The first kind of dependency is common to any incremental compiler, and
we can consult the literature. I think that the expand and eval
functions need to be made a bit more flexible so that we can
re-expand/re-eval trees in contexts of choice. We will use the import
graph to figure out which trees need to be re-evaluated. It would be
interesting to find out how `yuujinchou` fits into this picture. Very
eager for suggestions here.
The second kind of dependency is concerned with keeping content up to
date across the forest, so running a query or transcluding a subtree,
either in the mainmatter or backmatter.
Presupposing that all Sem.trees are up to date, we only want to rerender
those trees that contain content that has changed after reevaluation.
This does not only include reevaluated trees, but any tree that contains
a query that matches one of said trees. In order to find those trees we
need a data structure that, given an address, allows us to efficiently
look up the set of trees that contain queries that match the tree at
that address. We can populate this table during the first render when
all queries are ran.
# What is to be done
A working example of all this is not too far off. All it really requires
is to write some code that deals with the relation and import graphs and
passing around some hashtbls/maps/graphs/etc. I have made some progress
on this my as of yet unreleased branch. However, this code won't
actually provide any speedups for the `forester build` command. At the
moment, forester forgets everything after each run. For incrementality
to actually have an effect, the build step either needs to look up
information that is stored on disk in some sort of build cache, or we
implement a development server that runs and reacts to changes, keeping
the build and render caches in memory.
## Build cache
For this we need some database. I am inclined to use
[irmin-fs](https://mirage.github.io/irmin/irmin-fs/Irmin_fs/index.html).
I sent out a [patch adding some reprs to the query type](https://lists.sr.ht/~jonsterling/forester-devel/patches/53850),
which can't be part of [forester-irmin](https://git.sr.ht/~jonsterling/ocaml-forester-irmin)
as some types in that module are kept abstract. We also need to update
the types in `Xml_tree` to not contain any inline record types. This
type will be used for the render cache, the already existing (but out of
date) representation of Sem.tree will be used for the eval cache.
## Dev server
The fact that there is no cross-platform file system monitoring solution
for OCaml has been a painpoint for a while (github.com/kentookura/forest-server).
Other than that, this kind of program seems pretty straightforward. This
will work nicely with the [forester-htmx experiment](https://git.sr.ht/~jonsterling/ocaml-forester-htmx)
# Misc
While doing research on build systems and incremental computation, I
came across this interesting talk by Olle Fredriksson:
https://www.youtube.com/watch?v=3D-ngGIP4fQ
https://github.com/ollef/rock
with inspiration by this extremely enjoyable paper:
https://www.microsoft.com/en-us/research/uploads/prod/2020/04/build-systems-jfp.pdf
by developers of Dune, Shake, Hadrian... Simon Peyton Jones...
I meant to play around with some of the ideas in the paper, but the crux
of the whole `Task` abstraction is that it is polymorphic in
`f : * -> *` (Section 3.4), which OCaml does not support. (Maybe with
https://github.com/yallop/higher? Seems a bit silly). In general, the
whole paper leans on typeclass-style programming, so I don't know how
well it translates to OCaml.