pauleveritt / fdom

Template engine based on tag strings and functional ideas.
0 stars 0 forks source link

Evaluation in tag strings #2

Open jimbaker opened 1 year ago

jimbaker commented 1 year ago

Unlike the usual ordering in functions, evaluation is outside-in, vs inside-out. That is:

Note that this is similar to a lot of code - it might explicitly iterate over an outer loop, then an inner loop, etc.

Because of this explicit, outside-in evaluation, it's possible to have the DSL template to control this scheduling as it "peels the onion" of thunks. For example, if rendering HTML, it could just evaluate what is visible up to that point in the scrolling (at least in principle). This also allows for an incremental output of results in general, similar to what is done in WSGI.

Next, for each interpolation, we only see lambda: expr (unless we try to do a rewrite trick to use the expr text and any lexically scoped names attached, but that's nontrivialin general). So we don't know necessary what the value of expr will be in advance. That's fine, we just evaluate the expression at some point in this outside-in evaluation and support some of the below possibilities:

  1. "Normal" function. Evaluates and returns some result that can be directly interpolated - a string or an HTML chunk, for example.
  2. Iterator/iterable, such as from a generator.
  3. Async function. It evaluates to a coroutine (use asyncio.iscoroutine() to test for this), which can then be scheduled onto an event loop.
  4. "Placeholder" object, generally set up through some decorators on the functions that would actually compute the real value. So this object could have additional attributes on it to say, I'm just a placeholder for an expensive computation, here's what you need to actually build me. This is what can be linked to a build system with SQLite managing the depedency graph.
jimbaker commented 1 year ago

Another way to see the outside-in evaluation approach is the usual pattern of code of

def mytag(*args: Chunk | Thunk):
  for arg in args:
    match arg:
      # process arg by its type

This explicit evaluation enables a lot of the power of this approach, while being hidden in, it's just working with strings and interpolations.