Generic StringTemplates?

Clement Cherlin ccherlin at gmail.com
Thu Mar 7 17:31:02 UTC 2024


I can envision some use cases for StringTemplate where I would want to
restrict the type of the interpolated expressions, either for static
type safety, or for the convenience of providing an implicit static
type to interpolated lambda expressions, or both.

For example, consider a Processor that takes Suppliers as interpolated
values and returns a Supplier, allowing for lazy evaluation:

public static final StringTemplate.Processor<Supplier<String>,
RuntimeException> LAZY =
    stringTemplate -> () -> StringTemplate.interpolate(
    stringTemplate.fragments(),
    stringTemplate.values().stream()
        .map(o -> ((Supplier<?>)o).get()).toList());

Using such a processor is awkward and not type-safe, requiring both an
unchecked cast in the processor and an explicit cast in every value
expression:

final Supplier<String> lazy = LAZY."Now: \{(Supplier<Instant>) Instant::now}";

You can use various workarounds, like defining a static method that
coerces its argument to Supplier<T>, but String Templates are supposed
to reduce existing boilerplate, not create new boilerplate.

Could StringTemplate have a type argument? I envision something like

public interface StringTemplate<T> {
    List<T> values();
    public interface Processor<T, R, E extends Throwable> {
        R process(StringTemplate<? extends T> stringTemplate) throws E;
        ...
    }
    ...
}

To support creating generic StringTemplates, the following syntax
could be legal, returning a StringTemplate with static type
StringTemplate<type>.

RAW.<type>"template with \{value}s of type..."

Without a type argument, the return value would have static type
StringTemplate<Object>.

Cheers,
Clement Cherlin


More information about the amber-spec-observers mailing list