scalastring-interpolationscala-quasiquotes

Storing long quasiquote matchers in a variable


I'm trying to avoid repetition of long quasiquotes in matches. So, I'd like to convert this:

def appendTree(clazz: ClassDef, tree: Tree): ClassDef =
  clazz match {
    case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
      q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats; ..$tree }"
  }

to something like this:

val clazzQuote = "$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"

def appendTree(clazz: ClassDef, tree: Tree): ClassDef =
  clazz match {
    case q"$clazzQuote" =>
      q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats; ..$tree }"
  }

A comparable example of what I'm trying to do with string interpolation:

val msg = "hello $name"

"hello world" match {
  case s"$msg" => println(name) // I want this to output "world"
}

This example doesn't work either.

How can I do this? (or can I?)


Solution

  • You can't write

    val msg = s"hello $name" // name doesn't make sense here
    

    or

    val clazzQuote = "$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }"
    // mods, tpname, tparams, ... do not make sense here
    

    That's not how pattern matching work in Scala.

    You can write either

    clazz match {
      case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
        q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats; $tree }"
    }
    

    or

    clazz match {
      case ClassDef(mods, name, tparams, Template(parents, self, body)) =>
        ClassDef(mods, name, tparams, Template(parents, self, body :+ tree))
    }
    

    or

    clazz match {
      case c: ClassDef =>
        val i = c.impl
        ClassDef(c.mods, c.name, c.tparams, Template(i.parents, i.self, i.body :+ tree))
    }
    

    If you need not all parameters you can use underscores

    clazz match {
      case q"$_ class $tpname[..$_] $_(...$_) extends $_" =>
        println(tpname)
    }
    

    or

    clazz match {
      case q"$_ class $_[..$_] $_(...$_) extends { ..$_ } with ..$parents { $_ => ..$_ }" =>
        println(parents)
    }
    

    In complex cases you can use custom extractor objects.

    If you often add a tree to class body you can introduce helper method

    def modifyBody(clazz: ClassDef, f: List[Tree] => List[Tree]): ClassDef =
      clazz match {
        case q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..$stats }" =>
          q"$mods class $tpname[..$tparams] $ctorMods(...$paramss) extends { ..$earlydefns } with ..$parents { $self => ..${f(stats)} }"
      }
    
    def appendTree(clazz: ClassDef, tree: Tree): ClassDef = {
      clazz match {
        case c => modifyBody(c, stats => stats :+ tree)
    }