Scala has a great feature that allows for adding custom string interpolation handlers, signaled by adding a prefix in front of the opening quote. They have built-in versions s"" and f"" (the latter which allows adding %.2f style formatting strings after the ${}).
But they also allow custom prefixes, which my company has used to very good effect for building a powerful i18n framework and SQL layer. There are a number of ways to expose it. Here is one suggestion:
Allow adding a function in a specific spot, e.g.:
fun templateHandler(prefix: String, parts: Array<String>, args: Array<Any>): String
Where prefix is the thing that comes before the opening quote, parts are the string literals between interpolated expressions, and args are the interpolated expressions.
If the template starts or ends with an interpolated expression, parts should respectively start or end with an empty String so that the every-other pattern of the parts and args can be relied upon.
This would allow building things like:
- json"a: $a" (where a can be an array and will get formatted correctly)
- sql"select âŠ" (and do proper escaping of arguments)
- tr"You have a balance of $count credits remaining. " (and get translated)
Because the existing string interpolation functionality does not have a prefix concept, it should be possible to add this in a backwards-compatible way and without impacting performance for the existing approach.
It is essential that the handler function could be non-static so that it can access and mutate relevant state in an object instance (i.e., access the this pointer), and so that different handlers can be implemented in different classes.
There is a tension between requiring a class to implement an interface to enable this vs. allowing static implementations. Ideally it would be possible to do both, but allowing access to instance state is very important for the use-cases that we have encountered at my company.
And it should not be required that the handler return a String, as many use cases have side-effects but do not return anything.
(Back-story: we are frustrated with Scala compile times and are considering a switch to Kotlin; this is a must-have feature to enable the switch.)