【问题标题】:Evaluating an expression of type shapeless.Witness.Aux[T] in a macro fails在宏中评估 shapeless.Witness.Aux[T] 类型的表达式失败
【发布时间】: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


【解决方案1】:

由于https://github.com/milessabin/shapeless/pull/410 的变化,这个问题在shapeless 2.2.2 中消失了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多