dimanche 11 décembre 2016

Insert string into quasiquote before it's interpolated

I'm trying to generate Tree for function that accepts case class value and returns value of case class parameter at given position. This is useful for extracting values of private parameters.

  import reflect.runtime.currentMirror
  import scala.reflect.runtime.universe._
  import scala.tools.reflect.ToolBox
  val tb = currentMirror.mkToolBox()

  case class A(private val first: Int)

  case class B(first: Int, private val second: Int)

  def get(tpe: Type, position: Option[Int]): Tree = {
    val pos = s"${position.map(p => s"._${p + 1}").getOrElse("")}"
    tb.parse(s"(a: $tpe) => $tpe.unapply(a).get$pos")
  }

  println(tb.eval(get(typeOf[A], None)).asInstanceOf[(A) => Int](A(1)))

  println(tb.eval(get(typeOf[B], Some(1))).asInstanceOf[(B) => Int](B(1, 2)))

Also I added following dependencies:

scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
  "org.scala-lang" % "scala-reflect" % scalaVersion.value,
  "org.scala-lang" % "scala-compiler" % scalaVersion.value
)

position is None when case class has only one parameter.

My solution is working, but how can I get rid of tb.parse(s"...") and replace it with quasiquote q"..."?

I tried that but it fails with:

Don't know how to unquote here
[error]     q"(a: $tpe) => $tpe.unapply(a).get$pos"
[error]                                        ^

As I understood I can not insert into quasiquote some string that's constructed at runtime and q"..." is parsed in compile time unlike tb.parse. Am I right?

Also is it safe to interpolate like that s"(a: $tpe) => $tpe.unapply(a).get$pos"? When using q"..." syntax quasiquote knows that $tpe is Type but string interpolation makes a string from it. I'm not sure that this will work always and in more complex and specific cases.





Aucun commentaire:

Enregistrer un commentaire