Topological / Suspending Recursive Builder #
This module defines a recursive build function that topologically (ι.e., via a depth-first search with memoization) builds the elements of a build store.
This is called a suspending scheduler in Build systems à la carte.
Recursive Fetching #
In this section, we define the primitives that make up a builder.
A dependently typed monadic fetch function.
That is, a function within the monad m
and takes an input a : α
describing what to fetch and and produces some output b : β a
(dependently
typed) or b : B
(not) describing what was fetched. All build functions are
fetch functions, but not all fetch functions need build something.
Equations
- Lake.DFetchFn α β m = ((a : α) → m (β a))
Instances For
In order to nest builds / fetches within one another,
we equip the monad m
with a fetch function of its own.
A transformer that equips a monad with a DFetchFn
.
Equations
- Lake.DFetchT α β m = Lake.EquipT (Lake.DFetchFn α β m) m
Instances For
A DFetchT
that is not dependently typed.
Equations
- Lake.FetchT α β m = Lake.DFetchT α (fun x => β) m
Instances For
We can then use the such a monad as the basis for a fetch function itself.
Equations
- Lake.DRecFetchFn α β m = Lake.DFetchFn α β (Lake.DFetchT α β m)
Instances For
A DRecFetchFn
that is not dependently typed.
Equations
- Lake.RecFetchFn α β m = (α → Lake.FetchT α β m β)
Instances For
A DFetchFn
that provides its base DRecFetchFn
with itself.
The basic recFetch
can fail to terminate in a variety of ways,
it can even cycle (i.e., a
fetches b
which fetches a
). Thus, we
define the acyclicRecFetch
below to guard against such cases.
A recFetch
augmented by a CycleT
to guard against recursive cycles.
If the set of visited keys is finite, this function should provably terminate.
We use keyOf
to the derive the unique key of a fetch from its descriptor
a : α
. We do this because descriptors may not be comparable and/or contain
more information than necessary to determine uniqueness.
Equations
- Lake.recFetchAcyclic keyOf fetch = Lake.recFetch fun a recurse => Lake.guardCycle (keyOf a) fun stack => fetch a (fun x => liftM (recurse x stack)) stack
Instances For
When building, we usually do not want to build the same thing twice during
a single build pass. At the same time, separate builds may both wish to fetch
the same thing. Thus, we need to store past build results to return them upon
future fetches. This is what recFetchMemoize
below does.
recFetchAcyclic
augmented with a MonadDStore
to
memoize fetch results and thus avoid computing the same result twice.
Equations
- Lake.recFetchMemoize keyOf fetch = Lake.recFetchAcyclic keyOf fun a recurse => Lake.fetchOrCreate (keyOf a) (fetch a recurse)
Instances For
Building #
In this section, we use the abstractions we have just created to define the desired topological recursive build function (a.k.a. a suspending scheduler).
Recursively builds objects for the keys κ
, avoiding cycles.
Equations
- Lake.buildAcyclic keyOf a build = Lake.recFetchAcyclic keyOf build a []
Instances For
Dependently typed version of buildTop
.
Equations
- Lake.buildDTop β keyOf a build = Lake.recFetchMemoize keyOf build a []
Instances For
Recursively fills a MonadStore
of key-object pairs by
building objects topologically (ι.e., depth-first with memoization).
If a cycle is detected, the list of keys traversed is thrown.
Equations
- Lake.buildTop keyOf a build = Lake.recFetchMemoize keyOf build a []