Scala 中的准引号和泛型
Quasiquotes and Generics in Scala
我正在使用 Play Framework 并尝试编写一个可以解析 protobuf 请求的操作,如下所示:
def AsyncProtoAction(block: ProtoRequestA => Future[Result]): Action[AnyContent] = {
Action.async { request =>
request.contentType match {
case Some("application/protobuf") => request.body.asRaw match {
case Some(raw) => raw.asBytes(1024 * 1024) match {
case Some(rawBytes) =>
val proto = ProtoRequestA.parseFrom(rawBytes)
block(proto)
case _ => ...
}
}
}
}
}
这里,ProtoRequestA
是使用ScalaPB
生成的对象。它有效,但是,我有很多 protobuf 请求对象。然后我尝试使用宏重写它:
object Actions {
def AsyncProtoAction[T](block: T => Future[Result]):
Action[AnyContent] = macro asyncProtoAction_impl[T]
def asyncProtoAction_impl[T: c.WeakTypeTag](c: blackbox.Context)
(block: c.Tree) = { import c.universe._
val protoType = weakTypeOf[T]
q"""import play.api.mvc._
Action.async { request =>
request.contentType match {
case Some("application/protobuf") =>
request.body.asRaw match {
case Some(raw) =>
raw.asBytes(1024 * 1024) match {
case Some(rawBytes) =>
val proto = $protoType.parseFrom(rawBytes)
$block(proto)
case _ => ...
}
}
}
}"""
}
}
这行不通。编译错误为value parseFrom is not a member of packageName.ProtoRequestA
.
我在 REPL 中尝试 showRaw(tq"$protoType")
,输出 String = TypeTree()
。我猜正确的输出应该是String = Select(Ident(TermName("packageName")), TypeName("ProtoRequestA"))
。那我该怎么办呢?
您已经确定了问题所在,即当您对 ProtoRequestA
类型进行插值时,您得到的东西与 ProtoRequestA
伴生对象不同。一种简单的解决方案是插入伴随对象的符号,您可以从类型的符号中获取它。这是一个简化的例子:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def companionObjectBarImpl[A: c.WeakTypeTag](c: Context): c.Tree = {
import c.universe._
q"${ symbolOf[A].companion }.bar"
}
def companionObjectBar[A]: Int = macro companionObjectBarImpl[A]
这个宏将适用于任何类型的伴生对象,伴生对象具有 bar
方法 returns 和 Int
。例如:
scala> class Foo; object Foo { def bar = 10 }
defined class Foo
defined object Foo
scala> companionObjectBar[Foo]
res0: Int = 10
你应该可以做类似的事情。
我正在使用 Play Framework 并尝试编写一个可以解析 protobuf 请求的操作,如下所示:
def AsyncProtoAction(block: ProtoRequestA => Future[Result]): Action[AnyContent] = {
Action.async { request =>
request.contentType match {
case Some("application/protobuf") => request.body.asRaw match {
case Some(raw) => raw.asBytes(1024 * 1024) match {
case Some(rawBytes) =>
val proto = ProtoRequestA.parseFrom(rawBytes)
block(proto)
case _ => ...
}
}
}
}
}
这里,ProtoRequestA
是使用ScalaPB
生成的对象。它有效,但是,我有很多 protobuf 请求对象。然后我尝试使用宏重写它:
object Actions {
def AsyncProtoAction[T](block: T => Future[Result]):
Action[AnyContent] = macro asyncProtoAction_impl[T]
def asyncProtoAction_impl[T: c.WeakTypeTag](c: blackbox.Context)
(block: c.Tree) = { import c.universe._
val protoType = weakTypeOf[T]
q"""import play.api.mvc._
Action.async { request =>
request.contentType match {
case Some("application/protobuf") =>
request.body.asRaw match {
case Some(raw) =>
raw.asBytes(1024 * 1024) match {
case Some(rawBytes) =>
val proto = $protoType.parseFrom(rawBytes)
$block(proto)
case _ => ...
}
}
}
}"""
}
}
这行不通。编译错误为value parseFrom is not a member of packageName.ProtoRequestA
.
我在 REPL 中尝试 showRaw(tq"$protoType")
,输出 String = TypeTree()
。我猜正确的输出应该是String = Select(Ident(TermName("packageName")), TypeName("ProtoRequestA"))
。那我该怎么办呢?
您已经确定了问题所在,即当您对 ProtoRequestA
类型进行插值时,您得到的东西与 ProtoRequestA
伴生对象不同。一种简单的解决方案是插入伴随对象的符号,您可以从类型的符号中获取它。这是一个简化的例子:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
def companionObjectBarImpl[A: c.WeakTypeTag](c: Context): c.Tree = {
import c.universe._
q"${ symbolOf[A].companion }.bar"
}
def companionObjectBar[A]: Int = macro companionObjectBarImpl[A]
这个宏将适用于任何类型的伴生对象,伴生对象具有 bar
方法 returns 和 Int
。例如:
scala> class Foo; object Foo { def bar = 10 }
defined class Foo
defined object Foo
scala> companionObjectBar[Foo]
res0: Int = 10
你应该可以做类似的事情。