Most use cases would be supported by eager evaluation. The most important of these is being able to support a tag function’s ability to parse the tag string (generally into an AST), and then apply the context from that AST to producing correctly escaped (or passed through) interpolations for the target DSL. I would generally descibe this process as 1) substitute appropriate placeholders into the tag string; 2) use an off the shelf parser to an AST; 3) walk/compile the AST to produce desired output (such as a DOM). Whether eager or deferred doesn’t change this process, nor opportunities for memoization.
Deferred evaluation can support at least the following:
-
Globally-scoped names being bound. This could be useful when these globally-scoped names are actually bound by a tool like numexpr, formulaic, Pandas, etc to an application specific context like a column in a Pandas dataframe. The counterargument is globally scoped names.

-
Straightforward deferred evaluation - everything is wrapped by a lambda, so traversing the expression tree is just a matter of using
getvalue()and doing one “unwrap”. Obviously, sophisticated libraries can readily implement this sort of support to get the equivalent of changing the order of evaluation to match when it is actually needed, such as when writing an HTTP response. But then how do these libraries compose?getvalueprovides a convenient protocol. -
Lazy usage, such as the
struct_logtag function I mentioned, without requiring every expression to be wrapped by the developer (not likely in logging). -
Very interesting metaprogramming, as I showed in the rewrite example. These are fun experiments, but I’m not convinced of their utility.