til-lang / til

An easy to extend command language
56 stars 5 forks source link

Compile-time (AOT/AOD/AOE) safety, static analysis, types, arbitrary "compile-time" computation, run-time patching/update of the app #17

Open dumblob opened 1 year ago

dumblob commented 1 year ago

Since we discussed last time that Til can not "up front" provide much guarantees (except for (some) syntactical errors) I was contemplating how this could be achieved in the most generic way. And few days ago something happened in my head and things started to self-organize themself and form the following.

Note, this is still very preliminary write up, rather a brain dump than anything presentable to other human beings. Please ask, bombard me with questions, point out discrepancies, contradictions, etc. Also please accept this is a moving target and I would probably refine and change things (sometimes significantly) over time.

I realized SW lifecycle is much more fine-grained than usually presented. This new fine-grained lifecycle might be briefly summarized like so:

  1. one makes a set of changes to source code
  2. one runs part of the overall existing interrelated code base in a special mode to see the impact of (1) ASAP - this is AOT static analysis (usually incl. type checking, static asserts, etc.)
  3. one runs another part (might partially, fully, or not at all overlap with the part from (2) ) in another special mode to see the impact of (1) - this is "ahead-of-production-deployment" dynamic/runtime analysis (unit testing, beta testing, runtime asserts, stress testing, fuzzy testing, scalability testing, etc.)
  4. one wants to run the whole updated code base in production - some parts of the previously running version of the code base get preserved (e.g. persisted to disk to be read upon start of the new version), some parts stay in memory (Linux's runtime patching, Erlang's code replacement in runtime, Julia's world age, etc.) and some parts are completely thrown away and then finally new ones are loaded to memory and jumped to.
  5. goto (1)

Viewed like this, one can perceive a clear pattern - basically a chain of "substitute" operations over time. First substitution in the chain is special - because it substitutes nothing (no code base is running yet) for something (the very first revision of the code base). Last substitution in the chain is also special - it substitutes currently running code base with nothing (i.e. the opposite of the first chain substitution). Anything in between are regular substitutions of designated parts in the currently running code base with new parts (some might be empty - effectively "removing" the given parts from the currently running code base).

This all does not sound much new nor surprising until we think of all the effects of the "substitute" activity from the chain above.

If there was a syntax support for the "substitute" operation and then some ordering (either relative "which substitute first incl. whole list of dependencies" or absolute "at world age 1 substitute A with B and at world age 3 substitute X with Y" or combination of both), then one could declare a stopping condition like "run from age 1 to 5" or "run until this particular substitue operation on this line gets reached after running all the dependencies" effectively allowing one to run the code base until this stopping condition or in other words perform e.g. only static analysis without any specialized complex type/generics/assert/you_name_it syntax (i.e. a complete equivalent of a static AOT analysis).

Of course, the substitute operation would do lots of things under the hood - mainly producing a new revisions of ASTs at designated places and then compiling them (to machine code, byte code, lowered AST, whatsoever...). In case of Til AST is code due to homoiconicity, so it should be even easier.

Now the only thing missing is the syntax. I deliberately left it out so far because there are many many ways how to approach this. My expectation would be meeting the following criteria.

  1. substitution has to be explicit (not like in Julia)
  2. substitution shall be part of syntax (i.e. "first class citizen") and not just an "interface" like in Erlang
  3. substitution is by definition independent from the places it substitutes (the what and with) but for humans it is in 99% of cases convenient to have the substitution close to what while also seeing the with am I substituting with
  4. substitution shall support some form of "transactional substitutions" (multiple substitutions regarded as one) as well as individual substitutions (the default)
  5. syntactic sugar could make things nicer if things get unwieldy :wink:

So far I did not dare to engineer any syntax for Til as I would like to hear ideas of others first without influencing them :wink:.


To wrap up, any dynamically typed language can achieve arbitrary AOT safety guarantees if the language supports some form of AST manipulation.

Vocabulary:

AOT - ahead-of-time AOE - ahead-of-execution AOD - ahead-of-deployment