----- Original Message -----
> From: "Brian Goetz" <brian.go...@oracle.com>
> To: "Remi Forax" <fo...@univ-mlv.fr>, "amber-spec-experts" 
> <amber-spec-experts@openjdk.java.net>
> Sent: Lundi 18 Octobre 2021 18:47:00
> Subject: Re: Templated String and template policies, why the current design 
> is bad

> This seems a very strange argument to me.
> 
> Templates are by their nature dynamic -- a template has an unknown
> number of holes, and the holes are filled with arbitrary expressions.
> People like templates because they're easy to use, and they're easy to
> use because they're flexible.  Consider String::format:
> 
>     String format(String formatString, Object... values)
> 
> There are many dynamic conditions that are not statically checked here;
> that the format string is well-formed, that the number of holes matches
> the number of values provided, that the types of the values are suitable
> for filling the holes, etc.  Every templating policy will carry their
> own private interpretation of these requirements, which would require
> much more complex type systems to capture.

There is a lot of structured text that ask for specific types in a specific 
order.

By example, if a text that starts with a date and then some values

  new DatedTextTemplatePolicy()."""
    // Date \(LocalDate.now())
    \(key1) : \(value1)
    \(key2) : \(value2)
  """; 

If i can declare the parameters like in JavaScript, i can write
  String apply(TemplatedString template, LocalDate date, Object... pairs) { ... 
}


It also make all the constructs that are target typing, unusable.
By example, how to use lambdas/method references that will be used as 
projection functions for several record instances.

  List<Person> persons = ...

  // generate all mails
  new MailGeneratorTemplatePolicy(persons)."""
    Dear \(Person::title) \(Person::lastName),
    i hope you enjoy ...
    ... 
  """;

As you know, you can not write this kind of code if the arguments are all typed 
Object.


Another example is there grammar example of John,
https://github.com/forax/java-interpolation/blob/master/src/test/java/com/github/forax/interpolator/GrammarTemplatePolicyTest.java#L22

Here you want all the arguments to be either a terminal or a non-terminal.
It should be a compile time error if a user uses something else.


> 
> When the templating policy is a well-known constant, such as
> java.lang.String.FMT, IDEs will be able to provide better checking based
> on the specification of the formatter, but that's a bonus.
> 
> You're saying here that what we should reify is not format+values, but
> format+types.  This is not an unreasonable choice (but, doesn't rise to
> the bar you've set by "the current design is bad"), but I think your
> argument is an implementation preference dressed up in theoretical
> garb.  You want the abstraction to serve the implementation (a
> bootstrap), so you want to shape it like what a bootstrap wants to consume.

Nope, i want compile time safety when it's possible, Object.. should be a 
possible descriptor for the types of the parameters not the only descriptor.

[...]

> 
>> The other issue with the proposed design is that there is no way to declare 
>> the
>> template policy as a static method, it has to be an instance method
>> implementing an interface despite the fact that both JavaScript and Scala*
>> support function first and lets the user adds supplementary arguments as a
>> secondary mechanism (using currying in Scala and by adding a property on the
>> function itself in JavaScript).
> 
> The Template policy is a SAM interface, so any static method of the
> right shape can be turned into a template policy with a method reference.

Yes, that why i call it a glorified Supplier, but i don't see how it helps.

In term of writing the code, in an IDE, i can not take a type Pattern." + 
CTRL-SPACE.

As a JDK maintainer, you can cheat and say, do this import static on that class 
and all template policies you need are now available, but this approach does 
not scale.

Otherwise, you have to memorize that FMT is in fact FormatTemplatePolicy.FMT, 
that PATTERN is in Fact PatternTemplatePolicy.PATTERN, etc.


> 
> I suspect what you mean by "no way", is "no way to access the
> super-optimized implementation strategy"?  And I'll say again the two
> answers I've already given to that: (a) many such formatters will not
> benefit from the low-level implementation strategy anyway, and (b) we
> should design the API to serve the users, not the implementors.  THere
> are many more users.

It's a false dichotomy, i want both, an API easy to use and efficient.

But in this thread i would like us to focus on the type checking part, the 
efficiency can be discussed in another thread.

Rémi

> 
> 
> 
> 
> 
> On 10/17/2021 4:54 PM, Remi Forax wrote:
>> I've recently proposed another way to implement the templated string/template
>> policies but i may not have made it clear why i think the current proposal 
>> [1]
>> is bad.
>>
>> First, some vocabulary, a templated string is a string with some unnamed
>> parameters that are filled with the result of expressions
>> by example, if we use ${ expr } as escape sequence to introduce an expression
>> the code
>>
>>    var a = 3;
>>    var b = 4;
>>    "sum ${ a } + ${ b } = ${ a + b }"
>>
>> can be decomposed into
>>
>>    - a string template that can be seen either as a string "sum @ + @ = @" 
>> with a
>>    special character (here '@') denoting a hole for each parameter
>>      or an array of strings ["sum ", " + ", " = ", ""] indicating the 
>> strings in
>>      between holes.
>>    - 3 parameters, param0, param1 and param2 initialized respectively with 
>> the
>>    results of the expressions a, b and a + b
>>
>> Before talking about the current proposal, let's take a look to the way both
>> JavaScript and Scala, implement the string interpolation.
>>
>> For JavaScript [2], you define a function that the template as an array and 
>> as
>> many parameters you need
>>    function foo(templateParts, param0, param1, param2) {
>>      ...
>>    }
>>
>> JavaScript uses backticks `` to delimit the templated strings and ${} as 
>> escape
>> sequence
>> so
>>    var a = 3;
>>    var b = 4;
>>    foo.`sum ${ a } + ${ b } = ${ a + b }`
>>
>> is equivalent to
>>
>>    foo(["sum ", " + ", " = ", ""], a, b, a + b)
>>
>>
>> In Scala, this mostly works the same way, there is a class StringContext that
>> correspond to a templated string and you define the function foo as a method 
>> of
>> StringContext that takes the parameters (in Scala, you can add methods on an
>> already existing class using (abusing of) the implicit keyword).
>>
>>    implicit class FooHelper(val template: StringContext) {  // adds the 
>> following
>>    methods to StringContext
>>      def foo(param0: Any, param1: Any, param2: Any) {
>>        ...
>>      }
>>    }
>>
>> Scala uses quotes "" to delimit the templated string and ${} as escape 
>> sequence
>> so
>>    val a = 3;
>>    val b = 4;
>>    foo."sum ${ a } + ${ b } = ${ a + b }"
>>
>> is equivalent to
>>    new StringContext("sum ", " + ", " = ", "").foo(a, b, a + b)
>>
>>
>>
>> In summary, for both JavaScript and Scala, the generalization of string
>> interpolation is a function call which takes the templates string parts as
>> first argument and the parameters of the templated string as the other
>> parameters.
>>
>> So in Java, you would assume that
>> - there is an object that represents a templated string with the holes
>> - there is a method that takes the templated string as first parameter and 
>> the
>> parameters of the templated string
>>
>> But this is not how the proposed design works.
>>
>> The TemplateString does not represent a string with some holes, it represents
>> the string with some holes plus the values of the holes, as if the arguments 
>> of
>> the parameters were partially applied. The TemplateString acts as a closure 
>> on
>> the arguments, a glorified Supplier if you prefer.
>>
>> Because the arguments are already inside the TemplatedString, the
>> TemplatePolicy, the function that should take the template and the parameters
>> does not declare the types of the parameters.
>> Which means that there is no way for someone that creates a TemplatePolicy to
>> declare the types of the parameters, any parameters is always valid, so there
>> is no type safety.
>>
>> This design is not unknown, this is the GString [4] of Groovy. While it makes
>> sense for a dynamic language like Groovy to not have to declare the type of 
>> the
>> parameters, it makes no sense for a language like Java which is statically
>> typed to not have a way to declare the types of the parameters like Scala or
>> TypeScript/JavaScript do.
>>
>> The other issue with the proposed design is that there is no way to declare 
>> the
>> template policy as a static method, it has to be an instance method
>> implementing an interface despite the fact that both JavaScript and Scala*
>> support function first and lets the user adds supplementary arguments as a
>> secondary mechanism (using currying in Scala and by adding a property on the
>> function itself in JavaScript).
>>
>> There is a good reason to support static methods in Java, a lot of use-cases
>> does not requires the template policy to have additional arguments (storing
>> them in an instance is not necessary) so forcing the template policy to be
>> defined as an instance method means a lot of boilerplate for no good reason.
>>
>> I hope i've convinced you that the current proposal for string interpolation 
>> in
>> Java is not the right one.
>>
>> regards,
>> Rémi
>>
>> * for Scala, it's a method on StringContext that acts as a function that 
>> takes a
>> StringContext as first parameter.
>>
>> [1] https://bugs.openjdk.java.net/browse/JDK-8273943
>> [3]
>> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
>> [4] https://docs.scala-lang.org/overviews/core/string-interpolation.html
>> [2] 
>> https://docs.groovy-lang.org/docs/latest/html/api/groovy/lang/GString.html

Reply via email to