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

你应该可以做类似的事情。