【发布时间】:2015-06-15 01:47:18
【问题描述】:
我正在尝试在宏中评估 shapeless.Witness.Aux[T] 类型的隐式参数,以便使用单例类型 T 的值。这是一个最小的示例:
import shapeless.Witness
import scala.language.experimental.macros
import scala.reflect.macros.blackbox.Context
object Macro {
def foo[N](implicit aux: Witness.Aux[N]): Unit = macro fooImpl[N]
def fooImpl[N: c.WeakTypeTag](c: Context)
(aux: c.Expr[Witness.Aux[N]]): c.Expr[Unit] = {
import c.universe._
val typechecked = aux.tree
println("Typechecked tree:")
println(show(typechecked))
val untypechecked = c.untypecheck(typechecked)
println("Untypechecked tree:")
println(show(untypechecked))
def eval = c.eval(c.Expr(untypechecked))
val w = scala.util.Try(eval).getOrElse(eval)
// now use w.value
c.Expr[Unit](q"()")
}
}
但是编译这个
val w = shapeless.Witness(true)
Macro.foo[w.T]
失败并出现以下错误:
[error] overriding value value in trait Witness of type fresh$macro$2.this.T;
[error] value value has incompatible type
println(show(typechecked)) 的输出是:
{
final class fresh$macro$2 extends AnyRef with shapeless.Witness {
def <init>(): fresh$macro$2 = {
fresh$macro$2.super.<init>();
()
};
type T = Boolean(true);
private[this] val value: Boolean(true) = true;
<stable> <accessor> def value: Boolean(true) = true
};
new fresh$macro$2()
}
println(show(untypechecked)) 的输出是:
{
final class fresh$macro$2 extends AnyRef with _root_.shapeless.Witness {
def <init>() = {
super.<init>();
()
};
type T = Boolean(true);
private[this] val value: Boolean(true) = true;
<stable> <accessor> def value: Boolean = true
};
new fresh$macro$2()
}
在我看来,问题在于,在未经类型检查的树中,value 方法的类型为 Boolean,而 value 字段的类型为 Boolean(true),编译器需要两种类型相同。
任何想法如何解决这个问题?这甚至支持在宏中评估宏吗?
顺便说一句:这个项目https://github.com/fthomas/scala-macro 包含的是一个最小的项目来复制这个。
【问题讨论】:
-
我猜对了吗,您在这里尝试做的是将编译时间 value 走私到宏实现中,以便可以在编译时间计算中使用它?
-
@MilesSabin 听起来不错。这是在refined 库的上下文中出现的,我希望这个
refineLit[MatchesRegex[shapeless.Witness.`"[0-9]+"`.T], String]("123")在编译时检查字符串“123”是否与正则表达式“[0-9]+”匹配。 -
Gotcha.
Witness真正用于将单例类型映射到 runtime 值。我认为您最好直接使用您的类型参数N及其类型标记,然后在内部重用Witness的逻辑。一种相当直接的方法是将大部分Witness宏分解为一个特征,该特征可以由您自己的宏混合。 -
感谢@MilesSabin 的回答,尽管这不是我所希望的。 :-) 看来我需要深入宏观领域。使问题稍微复杂的是,有一个运行时等效于
refineLit(称为refine),它基于相同的验证机制,并且Witness可以完美运行。 -
如果您还需要一个运行时版本,那么这可能会改变一些事情。我怀疑您上面的问题是,在取消类型检查之后进行重新类型检查可以保证是身份。您是否能够以经过类型检查的形式评估树?另外,请记住,在编译时评估任意代码可能......有风险;-)
标签: scala macros scala-macros shapeless scala-2.11