convex
convex copied to clipboard
Compile-time Symbol resolution
It would be helpful if the on-chain compiler performed some level of Symbol resolution when compiling a Symbol. Ideally it should be able to determine the following cases:
- Reference to a lexically bound variable
- Reference to a variable in the current Environment
- Reference to a variable in the default Environment (currently the core namespace)
- An undeclared Symbol
This would allow several useful things:
- Get a compiler error on reference to an Undeclared symbol immediately (e.g.
(fn [] undeclared-thing)) - Possibility to optimise / inline references to core functions (perhaps anything declared with
{:static true}metadata) - Could more precisely control the use of facilities such as
set! - In the future, making use of the Type of the referenced Symbol
I discussed detecting undeclared symbols at compilation with @pbaille some time ago.
While the first reflex is wanting to track and disallow undeclared symbols, things are quite different in Convex due to its decentralized nature. You can try being very strict about it, yet defined things can be undefined at any moment and it is even promoted (memory allowance model). Many scenarios would look valid yet still end up broken. In a single transaction you can:
(do
(def foo 42)
(defn f [x] (+ foo x))
;; All is fine, `foo` is declared says the compiler
(undef foo)
;; Now `f` is broken
So there is less of a compulsory need to track those undeclared things. Could it even be a feature? A poor one for a regular language but a good one for a decentralized language?
undef considered harmful.... actually even def is pretty dangerous if you don't have good assumptions regarding how the value is used.
I created an experimental language ( https://github.com/mikera/magic ) a while back looking at this particular problem (immutable environments and tracking / updating dependencies safely) but it would be a poor fit for Convex (basically way too much compiler complexity for a decentralised VM)
We would have to discuss it with @pedrorgirardi but it might be wiser tracking UNDECLARED at the level of the linter. For instance, if you write a standalone contract (doesn't need anything else but core and std libs), you could configure the linter to be very aggressive (eg. you can statically prove that any symbol is declared and warn about case like my example above).
I realize that there are quite a few things that we could do at the level of the linter, keeping the compiler lightweight and more permissive if you want to do expert-level stuff. I don't know how good or bad going that direction is. Plus, simpler compiler -> less expensive to run.
Simpler compiler is definitely a win. Ideally, we keep the cleverness in the compiler to the absolute minimum