使用 Scala 宏的莫名其妙的类型不匹配
Inexplicable type mismatch using Scala macros
我对宏的以下行为完全感到困惑:
问题: 我正在尝试为基于 case classes
构建的模型编写查询引擎,用户只需指定他要针对的字段希望匹配。
当前方法: 目前,我正在做一些懒惰的事情,只是利用默认的 Scala 模式匹配,使用宏来创建适当的 match
语句。代码如下:
// This is the macro code, defined in project `macros`
import scala.reflect.macros.whitebox
object QueryEngineMacros {
import scala.language.experimental.macros
def cimpl(c: whitebox.Context)(cs: c.Expr[CallSite], q: c.Expr[String]): c.Tree = {
import c.universe._
val pattern = cq"$q => true"
q"""$cs match {
case ${pattern}
case _ => false
}"""
}
def coincides(cs: CallSite, q: String): Boolean = macro cimpl
}
// This is the engine code, defined in project `queries` (depends on `macros`)
object QueryEngine {
def apply(q: String, res: ExtractionResult): Seq[CallSite] =
res.callSites.filter(cs => QueryEngineMacros.coincides(cs, q))
}
错误: 这是我第一次使用 Scala 宏,但一切似乎都很好。它也不会抱怨依赖性。但是,我收到此错误消息:
Error:(27, 66) type mismatch;
found : String
required: some.package.CallSite
res.callSites.filter(cs => QueryEngineMacros.coincides(cs, q))
有人知道这可能是什么原因吗?
编辑:错别字修复
抱歉,Scala 宏不是这样工作的。是的,Scala 宏可以生成新代码,但它们在编译时是 运行。但是您似乎想要的是根据您传递给宏的参数 q
在 运行 时间内生成一些代码。这是不可能的。特别是行
val pattern = cq"$q => true"
不是您期望的那样。您的宏实际生成的内容大致如下:
def apply(q: String, res: ExtractionResult): Seq[CallSite] =
res.callSites.filter(cs => cs match {
case q => true
case _ => false
})
因此您尝试将 CallSite
类型的 cs
与 String
类型的 q
进行匹配(而不是将 q
解释为一段代码! ) 即你有效地检查 cs
是否等于 q
并且编译器认为这个匹配永远不会为真并在错误中告诉你这个。
总结一下:如果你的q
实际上是一个编译时常量,那么这样的宏几乎没有任何好处。如果它是一个 运行 时间变量 - 这样的宏根本就需要帮助。
我对宏的以下行为完全感到困惑:
问题: 我正在尝试为基于 case classes
构建的模型编写查询引擎,用户只需指定他要针对的字段希望匹配。
当前方法: 目前,我正在做一些懒惰的事情,只是利用默认的 Scala 模式匹配,使用宏来创建适当的 match
语句。代码如下:
// This is the macro code, defined in project `macros`
import scala.reflect.macros.whitebox
object QueryEngineMacros {
import scala.language.experimental.macros
def cimpl(c: whitebox.Context)(cs: c.Expr[CallSite], q: c.Expr[String]): c.Tree = {
import c.universe._
val pattern = cq"$q => true"
q"""$cs match {
case ${pattern}
case _ => false
}"""
}
def coincides(cs: CallSite, q: String): Boolean = macro cimpl
}
// This is the engine code, defined in project `queries` (depends on `macros`)
object QueryEngine {
def apply(q: String, res: ExtractionResult): Seq[CallSite] =
res.callSites.filter(cs => QueryEngineMacros.coincides(cs, q))
}
错误: 这是我第一次使用 Scala 宏,但一切似乎都很好。它也不会抱怨依赖性。但是,我收到此错误消息:
Error:(27, 66) type mismatch;
found : String
required: some.package.CallSite
res.callSites.filter(cs => QueryEngineMacros.coincides(cs, q))
有人知道这可能是什么原因吗?
编辑:错别字修复
抱歉,Scala 宏不是这样工作的。是的,Scala 宏可以生成新代码,但它们在编译时是 运行。但是您似乎想要的是根据您传递给宏的参数 q
在 运行 时间内生成一些代码。这是不可能的。特别是行
val pattern = cq"$q => true"
不是您期望的那样。您的宏实际生成的内容大致如下:
def apply(q: String, res: ExtractionResult): Seq[CallSite] =
res.callSites.filter(cs => cs match {
case q => true
case _ => false
})
因此您尝试将 CallSite
类型的 cs
与 String
类型的 q
进行匹配(而不是将 q
解释为一段代码! ) 即你有效地检查 cs
是否等于 q
并且编译器认为这个匹配永远不会为真并在错误中告诉你这个。
总结一下:如果你的q
实际上是一个编译时常量,那么这样的宏几乎没有任何好处。如果它是一个 运行 时间变量 - 这样的宏根本就需要帮助。