scalastring-interpolationscala-extension

Scala StringContext, how can I intercept expressions before evaluation?


I'm trying to use the Scala extensions to extend the String context to time the execution of a costly method And I came with this:

extension(sc: StringContext)
  def timeit(args: Any*): String = {
    val startTime = System.currentTimeMillis();
    // sc.parts.evaluate??
    val result = sc.s(args*) + s.parts().mkstring("");
    result + " took " + (System.currentTimeMillis() - startTime) + " millisecs"
  }

Unfortunately I don't see a way to intercept before the sc.parts are evaluated, so there is not much sense in measuring time. Is there a way to record the startTime before the sc.parts are evaluated? Do a call by name somehow? Or some other ways?


Solution

  • You need to use inline def, but you also need to make sure that all arguments are inline as well (so that their code would be inlined, not their result):

    // code where all arguments are inlined
    extension(inline sc: StringContext)
      inline def timeit(inline args: Any*): String = {
        val startTime = System.currentTimeMillis()
        val result = sc.s(args*)
        val endTime = System.currentTimeMillis()
        s"$result took ${endTime - startTime} millisecs"
      }
    
    println(timeit"This string ${ { Thread.sleep(100); "is" } } taking ${ { Thread.sleep(250); "a while" } }")
    
    // code where arguments are NOT inlined
    extension(sc: StringContext)
      inline def timeit2(args: Any*): String = {
        val startTime = System.currentTimeMillis()
        val result = sc.s(args*)
        val endTime = System.currentTimeMillis()
        s"$result took ${endTime - startTime} millisecs"
      }
    
    println(timeit2"This string ${ { Thread.sleep(100); "is" } } taking ${ { Thread.sleep(250); "a while" } }")
    
    This string is taking a while took 354 millisecs
    This string is taking a while took 1 millisecs