There are a few things around "properties" I'd like to have in Typer
which are similar yet different. If any of you has ideas about how to
conflate/merge some of them, or how to clearly distinguish them or what
to do about them, I'd like to hear it:
Some properties I'm thinking of would be:
- docstrings (as annotations on functions, types, and other variables):
These could be properties of *values*, or properties of *bindings*.
For types and functions, properties of values would probably work well
(tho it prevents giving different docstrings to different names of
the same function), but for variables holding things like integers,
associating the docstring to the integer is not going to work well,
so we probably need to support properties of bindings (we can
probably limit them to actual declarations (i.e. let-bindings) since
docstrings of function arguments are probably not needed).
- decision procedures: These are the "macros" associated with a given
type to automatically find/construct an expression of that type, as
needed for implicit arguments. This is what we need to implement
type classes. These can be associated to *values* or the
*bindings*. In general I'd prefer using values than bindings since
its natural/normal to manipulate values whereas it's unusual to
manipulate bindings.
Also, I'd like to have those properties be lexically scoped, so I can
locally add a decision procedure for a type and elsewhere add another
decision procedure for that same type.
- declaration macros: normal macros (invoked using the function call
syntax) are special values of type Macro, but there are other macros
to use in other syntactic contexts, such as declaration macros
(i.e. macros that can be invoked in the <decls> part of "let <decls>
in <exp>"). Currently, declaration macros are shared with normal
macros, i.e. it's one and the same namespace so if you define a
`foo` macro it will apply to invocations of `foo` in <exp> as in
<decls>. I'd like to separate those two cases. Especially because
there will inevitably be more cases (e.g. lvalue macros, case-pattern
macros, ...). These could be implemented as properties of bindings.
But that means that if I rebind the `or` function, I end up also
affecting the `or` pattern macro. So maybe another way to solve this
would be to provided different namespaces. So I can independently
define the `foo` var, the `foo` pattern-macro, the `foo` lvalue-macro,
the `foo` declaration macro etc... without interference. A cheap way
to do that is to use some name-prefixing scheme, so the `foo`
pattern-macro is kept in the `patternmacro_foo` variable. I kind of
like this name-prefixing solution, but I'm not sure what the
convention should look like (which magic character to use). Also,
better would be to have the "namespace prefix" be an object rather
than a string.
Hmm... so reading what I wrote, maybe what I need/want is to extend the
`senv`, which is the part of the elaboration environment used to find the
deBruijn index of a variable. IOW currently it's a Map from strings
(variable identifiers) to (reverse) deBruijn indices, and the above
suggests I should maybe extend this map such that it can be indexed by
arbitrary objects (e.g. a pair of a namespace-object and a string).
This said, currently those `senv` are environments specific to the
elaboration phase, they tend to be transient, so we'd probably want to
promote them somehow.
Related to this: how should the above interact with modules (which are
basically tuples with names fields)? How can a module indicate what is
the docstring/decision-procedure/declaration-macro corresponding to
a particular field?
Stefan