deprecate "search.*binder" functions
scolsen opened this issue · comments
Currently, we can traverse environments in the compiler using three different schemes, each represented by a different function naming convention:
get*
looks for a binding in the given environment and no further.find*
looks for a binding in the given environment and its child environments (modules) only.search*
looks for a binding in the given environment, its parent, and its child environments (modules) as well as the parents of its child environments.
we currently need search
for a couple cases related to parameters in functions/macros. However, search
is difficult to reason about because child environments may contain cached parents. For example:
g = global environment
-> = child of
e = environment (module... or local/internal)
d = defintion (def* <thing>...)
<- = get parent
T1 : g -> e {e made child of g}
T2: g -> d {d made child of g}
T3: g' <- e {get parent of e}
At T3
we mark g
as g'
because there's no guarantee that g' == g
because of "caching". If we forget to update the parent of e
when d
is added to g
at T2
, the parent of e
is no longer g
but a different, older version of g
that lacks d
.
Currently, our uses of search actually rely on this behavior to find variations of different definitions. Instead of searching unidirectionally from a root node, we traverse everything, possibly grabbing "stale" environments--which currently we actually need for some cases!
Instead, we should define a function findInParents*
which, like search
can traverse upwards from a node in the environment chain, but only the parents of that node and not the parents of its children. This would be a lot easier to reason about. Currently, when search*
succeeds, it's really anybody's guess as to which environment the binding was actually found in.
Of course, if we move to a mutable environment approach this issue is obviated.