Custom String Templates

The only way custom interpolation/compile time parsing can work is if you can put the logic into the compiler. Given the broad range of potential formats this cannot (and should not) be hardcoded into the compiler. Instead it is where compiler plugins can come in. Unfortunately those are still on the roadmap, not stable (in terms of API stability - it works reliably, but version mixing/matching is likely to break), and not/poorly documented. The front-end API isn’t really existing yet and blocked on the new frontend as well. For this particular purpose a backend plugin would probably be enough. The way I envision it would work, would be effectively as a custom compiler intrinsic that would replace the function call with a faster implementation as well as further syntax checking.

What can be done without significant language change, in my opinion: string interpolation should not just use StringBuilder.append(...), but look for operator fun toString() for interpolated objects (dynamically or statically). Then we can write the above example without the + operator.

object SqlBuilder {
    operator fun Column.toString(): String = "\"$name\""
}
val sql = buildSql { "select $id,$user from table" }

The idea is interesting, but challenging. The problem is to do with symbol resolution. The way it works is that member methods are resolved before extension methods. As toString is defined on Any, this means that an extension toString will never be resolved. To enable this would require a very fundamental change in method resolution.

The only way that would be feasible would be to add some sort of keyword to give the extension priority. There is however one other significant issue, in that it would make extension methods (that aren’t class members) unpredictable as they may or may not be in scope and the behaviour would differ depending on that. It would be much easier to have a plugin that would override buildSQL. Btw. it would even be better to have:

val sql = buildSql { select(id, user) from table }

but rather than resolve this at runtime, resolve this at compile time. Note that from table is not even needed as the table can be determined from the column.

1 Like

That would be a great feature for Kotlin. Hasn’t it made into a KEEP?

Even JavaScript has this feature nowadays.
It allows safe custom templates within Kotlin without the need of very complex builders.

Java is getting this feature now too: JEP draft: String Templates (Preview)

I don’t know if there’s a KEEP but here’s what looks to be the canonical feature request ticket: https://youtrack.jetbrains.com/issue/KT-16366

@fvasco @Beholder @nazoking If you happen to still be interested, I created a compiler plugin specifically for this purpose. It allows the sql("SELECT * FROM people where name = $joe") style of interactions.

1 Like