在插入之前将字符串插入准引号(运行时准引号?)
Insert string into quasiquote before it's interpolated (runtime quasiquote?)
我正在尝试为在给定位置接受 case class 值和 case class 参数的 returns 值的函数生成 Tree
。这对于提取私有参数的值很有用。
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)))
我还添加了以下依赖项:
scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scala-lang" % "scala-compiler" % scalaVersion.value
)
当 case class 只有一个参数时,position
是 None
。
我的解决方案有效,但我怎样才能摆脱 tb.parse(s"...")
并 将其替换为 quasiquote q"..."
?
我试过了,但失败了:
Don't know how to unquote here
[error] q"(a: $tpe) => $tpe.unapply(a).get$pos"
[error] ^
据我所知,我无法将一些在运行时构造的字符串插入到准引用中,并且 q"..."
在编译时被解析,这与 tb.parse
不同。 我说得对吗?
这样插值安全吗s"(a: $tpe) => $tpe.unapply(a).get$pos"
?当使用 q"..."
语法时,quasiquote 知道 $tpe
是 Type
但字符串插值会从中生成一个字符串。我不确定这在更复杂和特定的情况下是否始终有效。
你不是对的。 Quasiquotes 不能只插入普通字符串,它们必须插入其他 AST 片段。如果您自己构建了一些 AST 片段,那么您可以将它们与 q
一起使用。因此,你可以这样写:
def getElem(tpe: Type, pos: Option[Int]): Tree = {
// Make something like TermName("_N")
val tupleAccess = pos.map("_" + _).map(TermName.apply)
val tupleExpr = {
val plain = q"${tpe.typeSymbol.companion}.unapply(a).get"
// Build into $companion.unapply(a).get(._N)
tupleAccess.foldLeft(plain)(Select.apply)
}
q"(a: $tpe) => $tupleExpr"
}
瞧瞧!
object A { object B { object C { case class D(private val x: Int, val y: String) } } }
val all = tb.eval(getElem(typeOf[A.B.C.D], None)).asInstanceOf[A.B.C.D => (Int, String)]
val int = tb.eval(getElem(typeOf[A.B.C.D], Some(1))).asInstanceOf[A.B.C.D => Int]
val str = tb.eval(getElem(typeOf[A.B.C.D], Some(2))).asInstanceOf[A.B.C.D => String]
val ds = {
val D = A.B.C.D
List(D(1, "one"), D(2, "two"), D(3, "three"))
}
ds.map(all) == List((1, "one"), (2, "two"), (3, "three"))
ds.map(int) == List(1, 2, 3)
ds.map(str) == List("one", "two", "three")
我正在尝试为在给定位置接受 case class 值和 case class 参数的 returns 值的函数生成 Tree
。这对于提取私有参数的值很有用。
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)))
我还添加了以下依赖项:
scalaVersion := "2.11.8"
libraryDependencies ++= Seq(
"org.scala-lang" % "scala-reflect" % scalaVersion.value,
"org.scala-lang" % "scala-compiler" % scalaVersion.value
)
当 case class 只有一个参数时,position
是 None
。
我的解决方案有效,但我怎样才能摆脱 tb.parse(s"...")
并 将其替换为 quasiquote q"..."
?
我试过了,但失败了:
Don't know how to unquote here
[error] q"(a: $tpe) => $tpe.unapply(a).get$pos"
[error] ^
据我所知,我无法将一些在运行时构造的字符串插入到准引用中,并且 q"..."
在编译时被解析,这与 tb.parse
不同。 我说得对吗?
这样插值安全吗s"(a: $tpe) => $tpe.unapply(a).get$pos"
?当使用 q"..."
语法时,quasiquote 知道 $tpe
是 Type
但字符串插值会从中生成一个字符串。我不确定这在更复杂和特定的情况下是否始终有效。
你不是对的。 Quasiquotes 不能只插入普通字符串,它们必须插入其他 AST 片段。如果您自己构建了一些 AST 片段,那么您可以将它们与 q
一起使用。因此,你可以这样写:
def getElem(tpe: Type, pos: Option[Int]): Tree = {
// Make something like TermName("_N")
val tupleAccess = pos.map("_" + _).map(TermName.apply)
val tupleExpr = {
val plain = q"${tpe.typeSymbol.companion}.unapply(a).get"
// Build into $companion.unapply(a).get(._N)
tupleAccess.foldLeft(plain)(Select.apply)
}
q"(a: $tpe) => $tupleExpr"
}
瞧瞧!
object A { object B { object C { case class D(private val x: Int, val y: String) } } }
val all = tb.eval(getElem(typeOf[A.B.C.D], None)).asInstanceOf[A.B.C.D => (Int, String)]
val int = tb.eval(getElem(typeOf[A.B.C.D], Some(1))).asInstanceOf[A.B.C.D => Int]
val str = tb.eval(getElem(typeOf[A.B.C.D], Some(2))).asInstanceOf[A.B.C.D => String]
val ds = {
val D = A.B.C.D
List(D(1, "one"), D(2, "two"), D(3, "three"))
}
ds.map(all) == List((1, "one"), (2, "two"), (3, "three"))
ds.map(int) == List(1, 2, 3)
ds.map(str) == List("one", "two", "three")