Wrapping in Positron templates

2013-07-19

In the Positron templates, I'm adding a wrapping functionality. Wrapping functionality is like file inclusion, only nested. In typical file inclusion, you start in template A, and in some place you say "please include template B at this spot". You end up with template B included in template A, like this:

Template B included in template A
Template B has been included in template A, which continues after B has finished.

With wrapping, one starts in template A, and then says "please include template B at this spot, using this part of template A" and delineate a small part of A, which we will call C. The template B usually includes a marker, at which point C is included in turn. So you have

Template A includes template B includes template C
Template B has been included in template A. Template C, which appears to be included in B, is actually defined in template A at the point of wrapping.

Incidently, this is another idea I found in the Template Toolkit, specifically the WRAPPER directive.

Now the thing is, C is a regular part of the template. It has its own directives, constructs, loops, replacements and everything else. These will need to be replaced at some time, but the question is: is this time before the inclusion, or during the inclusion? Because the results could be different, depending on what environment B places C in; for example within a loop or under an assignement construct.

It turns out that this mirrors function calls in typical programming languages.

Wrapping as function calls

In a typical function call, f(a(b)), the parameters are evaluated first, and then the function is called. In our example, b would be evaluated first, then a(b), and finally f called with the result of a(b) as its parameter. Following this model, C should be evaluated before evaluating B. The constructs of C are resolved with the environment that A has at the time the wrapping construct is first encountered. The template B gets passed a simple chunk of HTML, or data, or text, which it may look at, but not touch.

The other variant is not to pass simple parameters, but a callback. This could be a function pointer in C, a function reference in JavaScript or Perl, or a function object (or "functor") in object-oriented languages. Instead of receiving already-evaluated parameters, this callback is called every single time the values of the parameters are needed. Following this model, C should be evaluated during the evaluation of B. The constructs of C are resolved each and every time B needs the results of template C, in whatever environment is active at the moment.

There is one difference, though: in programming languages, it is considered unusual, or downright dangerous, for the callback C to have access to the scoped variables (the environment) of B. In the case of closures, C may even access local variables at the point it is defined, but this is in A, not B. This is referred to as dynamic vs. lexical binding.

Positron's choice: dynamic binding and evaluation

I'm going to choose the dynamic evaluation (the "callback" variant). If template A wraps template B around subtemplate C, then C is evaluated every time B calls it. And it will be called in the innermost environment, that of B. This environment will differ from that of A only when B calls C inside a loop, or an assignment construct, or similar. Now C acts as a blueprint or behaviour modifier for B, so it is assumed that C can access the variables B is passing too it. After all, in Positron (and other template languages), most of the variables are "global", and only a few special cases are "local".