Hey everyone :wave: I’m from Kotlin team, and I’m ...
# kotest
a
Hey everyone 👋 I’m from Kotlin team, and I’m trying to estimate a potential impact if Kotlin language get String Templates feature. Recently Java tried String Templates in preview version, and then it was revoked. But I’m sure in some day it will be released. In Kotlin, we can just provide compatibility with it, or we can make our own full-fledged version, taking into account the KMP and other language aspects. At the moment I’m looking for possible use-cases among test-frameworks. And if anybody has an idea how this feature could benefit Kotest - I’ll be grateful to discuss it. Here in 🧵, or feel free to reach me in DM. General assumptions, that String Templates could: • provide custom validation of string (checks for dangerous code). • perform post-processing of strings (like
"""...""".trimIndent().replace(Regex("is[A-Z][A-Za-z0-9]*:false\n?"), "").replace("\n", ""
)) • generate additional text in resulting string literals (for loggers, for example) • perform conditional formatting • maybe something else So, in theory, some ceremonies around string processing, could be incapsulated into custom String Templates, and make coding easier.
e
Not really sure, maybe it could be used for building assertion failure messages in a nicer way? Also if templates support testing "actual" strings, it could be used as an assertion (e.g. test that string
s
matches template
t
) Could perhaps be used to decorate text with terminal colors, that are only rendered if the string is rendered to a color-capable terminal? disclaimer: didn't think this through a whole lot, perhaps there's issues with the suggestions 🙂
a
In Kotest you can enhance a failed-assertion by using clues. It's really useful for adding additional context to a failed-assertion message. However, sometimes the clues are formatted awkwardly, and unless you know what to expect the console can just contain a bunch dumped data mixed in with a stacktrace and debug logs. Maybe string templates could help with that?
s
One powerful use case would be to enable spock style data testing
1
In spock you can just layout the values in a string grid. https://spockframework.org/spock/docs/1.0/data_driven_testing.html
🤔 1
o
I have tests for parallel transactions that look like this:
Copy code
test("multi-target update || read") {
        tasks(
            """
                |<<|A1|B1|a1|b1|  |>>|
                |  |  |  |<<|a0|b0|a0|b0|>>|
                |  |  |  |  |  |  |  |<<|a1|b1|>>|
            """
        )
    }
I agree that clue formatting in Kotest could improve (and I have ideas about how to achieve that). I am actually using Kotest's clues for larger outputs (`DebugTrace`s = object-attached logs). Example in this KotlinConf talk, corresponding repo here. For easier formatting of test output, I wonder if formatting options like Python's f-strings would help, although we could easily build stuff like this:
Copy code
val amount = 123.45
val precision = 1

fun Double.f(precision: Int) = "%.${precision}f".format(this)

println("${amount.f(1)}") // 123.5
Does that help?
a
Personally I don't like spock's string grids. Suppose we add one more test, one more line, and the new values are wider. We'll have to re-align all existing rows, making it difficult to review the PR, as it is not clear what exactly has changed
s
I actually agree with you but many users have asked for it over the years
Could support both
a
I concur - it would not be that hard to build.
a
Thank you folks for the suggestions. I’ve never heard about spock style data testing, but it looks interesting.
fun Double.f(precision: Int) = “%.${precision}f”.format(this)
yes, that a thing that string_templates could encapsulate. And then, having custom settings, process given strings.
d
When testing api output, some prefer receiving json as a string, and comparing that with an expected json string, it could be useful for validating those jsons in case the expected value has a mistake in it...
1
gratitude thank you 1
a
@Anton Yalyshev [JB] I built a Kotlin compiler plugin that does String Templates the same way that Scala does. It takes the "dollar $sign variables" and puts them into parts/params list:
Copy code
Interpolate("dollar $sign variables")
---
parts: ["dollar ", " variables"]
params: [sign]
The system is also typesafe because you define the type that the params need to be. https://github.com/deusaquilus/Terpal
I built several libraries on top of this plugin including terpal-sql which allows you to do string-interpolation with SQL in a injection-proof way. This is similar to what libraries like doobie and zio-jdbc do in Scala. https://github.com/deusaquilus/terpal-sql
👀 1
It would be really, really nice if this was a core feature of the language.
a
Very interesting, thank you!
a
(Disclaimer. The Terpal-SQL library is very new. I just wrote it.)
👌 1
n
i’d love to see this. #doodle has a StyledText type that is used for rendering text with various paint and decoration attributes. this is a perfect use case for templates; assuming they work like the Java approach and allows the template parser to generate any type from the template.
gratitude thank you 1
👀 1
a
@Nick I can definitely build you something like this using the Terpal compiler-plugin
Copy code
val text = styledText("${bold("Lorem Ipsum")} is simply ${Yellow("dummy text", target = Background)} of the printing and typesetting industry. It has been the industry's standard dummy text ${decoration("ever since the 1500s")}, when an unknown printer took a galley of type and scrambled it to make a type specimen book.")
It would only be possible on JVM for now but I can eventually change that too.
c
#cup-presentations also has something similar for styled text. It seems to be a common use-case for string templates.
a
@Nick I tried it out and using Terpal this worked:
Copy code
object Styled: Interpolator<StyledText, StyledText> {
  override fun interpolate(parts: () -> List<String>, params: () -> List<StyledText>): StyledText {
    var output = StyledText("")
    val parts = parts().iterator()
    val params = params().iterator()
    while (parts.hasNext()) {
      output = output..parts.next()
      if (params.hasNext()) {
        output = output..params.next()
      }
    }
    return output
  }
}
Then I could do:
Copy code
val text = Styled("${bold("Lorem Ipsum")} is simply ${Yellow("dummy text", target = Background)} of the printing and typesetting industry. It has been the industry's standard dummy text ${decoration("ever since the 1500s")}, when an unknown printer took a galley of type and scrambled it to make a type specimen book.")
👍 1