String Templates Question - are expressions within a TemplateString literal evaluated immediately, or when a template is applied?

Alex Buckley alex.buckley at oracle.com
Thu Sep 22 21:01:37 UTC 2022


The JEP also notes that "... it is not possible for a template processor 
to obtain, from a TemplatedString, the exact characters which appear in 
a template in source code; only the values of the embedded expressions 
are available, not the embedded expressions themselves."

If a future Java platform allows the embedded expressions themselves to 
be represented at run time (e.g. via a dynamically-computed constant 
that denotes the AST seen at compile time), then a template processor 
could choose to (re)evaluate an embedded expression in the current 
environment, no lambda "quoting" needed. A string template expression's 
deference to a template processor during evaluation is most unusual, and 
quite magical.

Alex

On 9/22/2022 1:37 PM, John Rose wrote:
> Logging is a good use case (but not at all the only one) for this move 
> of “quoting” an expression (for later “unquoting”) with a nullary lambda 
> prefix.
> 
> I put a discussion about this topic here:
> “Java supplier lambda, the Ultimate Quote”
> http://cr.openjdk.java.net/~jrose/jls/eta-abstraction-expr-quoting.html 
> <http://cr.openjdk.java.net/~jrose/jls/eta-abstraction-expr-quoting.html>
> 
> If we had an “auto-quote” feature (see the appendix of the above doc) 
> logger classes could get the delayed execution as part of their API 
> declarations, in the way that Nathan is apparently envisioning. But in 
> practice, adding |()->| before your logger expressions is not a huge 
> burden. Yeah, you’d want to say |LOG."template \{stuff}"| instead of 
> |log(()->STR.”template \{stuff}”)|. But it’s not so terrible as to 
> motivate (all by itself) sugary elision of the |()->| quote.
> 
> For my part, I’m really really glad that string templates did not add a 
> hard-coded feature into their design for this sort of expression 
> quoting. I think such a feature deserves to be considered on its own, 
> separately for many kinds of APIs, if at all.
> 
> (Idea of the moment: Allow the string template processor |apply| method 
> to accept a |Supplier<ST>|, instead of |ST|. Then push an auto-quote 
> rule into the desugaring of |LOG.<<ST>>|, when |LOG::apply| takes a 
> supplier. This could be added compatibly to the present design, as a 
> future feature. Sort of like some proposals to add new rules for 
> enhanced-for over streams, by adjusting the types around the 
> enhanced-for. String templates could support many, many such compatible 
> additions, IMO, and they should be rolled out in a measured and cautious 
> way, if at all.)
> 
> On 22 Sep 2022, at 12:13, Brian Goetz wrote:
> 
>     We went around on this a few times, and in the end concluded that it
>     is easy to just wrap the whole thing with a lambda:
> 
>          () -> STR."Connection from \{name} at \{time}"
> 
>     which is a Supplier<String>.  Lots of logging frameworks already are
>     prepared to deal with Supplier.
> 
>     On 9/22/2022 3:09 PM, Nathan Walker wrote:
>>     That was all about what I expected.  And it is probably the option
>>     that will result in the fewest surprises for developers.  The log
>>     example you linked was very interesting.
>>
>>     Has permitting lambda/method-reference syntax to insert lazy
>>     expressions been discussed before?  I would really love to be able
>>     to do something like "The time is \{Instant::now}" or "The time is
>>     \{()->Instant.now()}"
>>
>>     I think there is a lot of potential in allowing TemplateProcessors
>>     that elect to skip processing a template for some precondition
>>     avoid the overhead of any expensive embedded expressions, and I
>>     think this would be especially true in processors that can fail by
>>     throwing a checked exception.
>>
>>     Thanks,
>>     Nathan
>>
>>     On Thu, Sep 22, 2022 at 10:17 AM Jim Laskey
>>     <james.laskey at oracle.com> wrote:
>>
>>
>>
>>>         On Sep 22, 2022, at 11:08 AM, Nathan Walker
>>>         <nathan.h.walker at gmail.com> wrote:
>>>
>>>         Hi folks,
>>>
>>>         Question regarding TemplateStrings that I could not seem to
>>>         find the answer to in the JEP write up or the Javadoc for
>>>         TemplateString:  Is the expression evaluation lazy, or immediate?
>>>
>>>         In other words, if I do something like this:
>>>
>>>              List<Integer> list=new ArrayList<>();
>>>              TemplateString ts = "size = \{list.size()}"
>>>              list.add(1);
>>>              System.out.println(ts.apply(STR));
>>>
>>>         Will it print out size = 0, or size = 1?
>>
>>         The expression is evaluated and the value is captured when
>>         creating the TemplatedString instance. There is some
>>         discussion around left to right evaluation that tries
>>         to clarify this. So the answer is “size = 0”.
>>
>>
>>>
>>>         My main reason for asking is that I would love for string
>>>         templates to result in logging interfaces with overloaded
>>>         methods that can take TemplateStrings instead of strings, and
>>>         which are only evaluated if the log level is enabled.  So
>>>         that instead of something like this:
>>>
>>>              if (logger.isEnabled(Level.DEBUG)) {
>>>                   logger.log(Level.DEBUG, "Expensive value is " +
>>>         lookup() );
>>>              }
>>>
>>>         we can just do:
>>>
>>>              log.log(Level.DEBUG, "Expensive value is \{lookup()}");
>>>
>>>         And be confident that the cost of invoking lookup() will only
>>>         be paid if DEBUG is enabled.
>>>
>>>         Probably over 80% of the string building I have seen over my
>>>         career has been related to building log messages.  And the
>>>         mistakes I have most often seen are:
>>>
>>>              1. Expensive message construction not guarded by
>>>         checking if logging is enabled at that level
>>>              2. Expensive message construction guarded, but by
>>>         checking the *wrong* logging level (e.g. check INFO, log at
>>>         ERROR)
>>>              3. Using some sort of parameterized message/string
>>>         format but getting the parameters wrong (out of order,
>>>         missing one, having too many, etc.)
>>>
>>>         If TemplateStrings evaluate their expression fragments only
>>>         when processed then every one of these problems gets
>>>         addressed, and logging code becomes *a lot* simpler to read,
>>>         write, and code review.
>>>
>>>         (Random side note: I *love* the choice of \ instead of $.  It
>>>         took some time for me to get use to the idea, but keeping the
>>>         same escape character is fantastic and I wish more languages
>>>         had done that instead of using $)
>>
>>         You can play some tricks with Suppliers or Futures + lambdas
>>         to get lazy evaluation. I have an roughed out (i.e., old)
>>         example at
>>         https://cr.openjdk.java.net/~jlaskey/templates/examples/Log.java
>>
>>         Cheers,
>>
>>         — Jim
>>
>>
>>
>>>
>>>         Thanks for your time,
>>>         Nathan
>>>         --
>>>         Nathan H. Walker
>>>         nathan.h.walker at gmail.com
>>>         (703) 987-8937
>>>
>>>
>>
>>     --
>>     Nathan H. Walker
>>     nathan.h.walker at gmail.com
>>     (703) 987-8937
>>
>>
> 


More information about the amber-dev mailing list