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