I'd really want better support for string templates


#1

What I want is something to allow string template syntax, but without actually performing concatenation. Example: logger.debug("a = $a"). This is bad code, because a.toString() called even if debug level is disabled. Alternative is something like logger.debug { "a = $a" }, but it looks strange (imagine that you need to pass exception too: logger.debug({ "a = $a" }, e) or logger.debug(e) { "a = $a" })) and does not leverage support from logging libraries (better way is to change string to "a = {}" and pass it along with a reference).

Now imagine some class like StringTemplate. It’s a parsed, but not executed representation of string template (basically all substrings between ${...} parts and either references or lambda expressions for every ${...} part). If function argument has type StringTemplate, then Kotlin compiler just assembles that object without actually calling toString, etc. Of course if function argument is of String type, everything works like now.

Also it would be handy to extend string template syntax to support additional qualifiers, e.g. print("a = ${a:.2f}). In this case qualifier .2f would be passed to print function which will interpret it in printf-like fashion (of course qualifier syntax is arbitrary). It’ll allow what I wanted to do for years: handy syntax for SQL: executeQuery("select a from b where n = ${x:varchar}").

So there are three potential usages: logging, printf-like formatting and SQL. I’m sure that there will be other use cases for that feature.


#2

I don’t know about the 2,3 use cases but the first one can be covered by a lib and actually exists in kotlin-logging: https://github.com/MicroUtils/kotlin-logging


#3

That’s just a workaround, not a good one IMO. What if you want to log just string? You have to write in the other way or you still have to wrap that string into meaningless callback. Also generating a different class for every log line does not look very optimal (though it could be avoided with inline functions).


#4

If I understand you right, you want to write foo("a: $a") . Then the language should somehow pass the string and all the arguments of it to the function and only concatenate them if the StringTemplate is used? But that would mean that the compiler would need to generate a different object for each log line.

Ok, you could still optimize this with inlining, but now all we have is another way to generate a very specific lambda.


I don’t think this is a good idea. This would just make kotlin more complicated without adding anything to the language. The only possible advantage is that unlike with lambdas right now, you could pass multiple StringTemplates to a function (you can pass multiple lambdas as well, but the syntax is messy).

I have not yet seen a good argument why this is necessary.


I agree. Having some printf like formatting options would be great.