【问题标题】:Type error when using a type parameter as a type argument for another type being passed to a macro implementation将类型参数用作传递给宏实现的另一种类型的类型参数时出现类型错误
【发布时间】:2013-10-15 20:10:35
【问题描述】:

我正在尝试创建一个宏,它允许我捕获传递给构造函数的表达式的文本。我想要表达式的文本用于调试目的。宏实现如下:

package nimrandsLibrary.react.macroImpl

object Macros {
    def applyImpl[T : context.WeakTypeTag, U : context.WeakTypeTag](context : scala.reflect.macros.Context) (expression : context.Expr[T]) : context.Expr[U] = {
        import context.universe._
        context.Expr[U](context.universe.New(context.universe.weakTypeOf[U], expression.tree, context.universe.Literal(context.universe.Constant(expression.tree.toString()))))
    }
}

定义如下:

class Signal(expression : => T, expressionText : String) {
    ...
}

object Signal {
    def apply[T](expression : T) = macro nimrandsLibrary.react.macroImpl.Macros.applyImpl[T, Signal[T]]
}

但是,无论我如何称呼它,如下所示,我都会收到错误消息。

val mySignal = Signal{ 2 }  //type mismatch; found : Int required : T

但是,当然,T 的类型是 Int,所以这个错误没有意义。

似乎编译器在宏扩展中不知何故忘记将 Signal[T] 替换为 Signal[Int]。作为实验,我尝试更改定义站点,以便必须提供两种类型,如下所示:

def apply[T, U](expression : T) = macro nimrandsLibrary.react.macroImpl.Macros.applyImpl[T, U]

那么,我这样称呼它:

Signal[Int, Signal[Int]]{ 2 }

而且,这行得通。但是,当然,这根本不是我想要的语法。这是一个错误,还是我做错了?有解决办法吗?

【问题讨论】:

    标签: scala scala-macros


    【解决方案1】:

    问题在于宏实现中U 的弱类型标记的参数本质上是符号T,而不是Int

    以下将起作用(请注意,为了清楚起见,我缩短了一些名称并替换了不推荐使用的方法):

    import scala.language.experimental.macros
    import scala.reflect.macros.Context
    
    object Macros {
      def applyImpl[
        T: c.WeakTypeTag,
        U: c.WeakTypeTag
      ](c: Context)(e: c.Expr[T]): c.Expr[U] = {
        import c.universe._
    
        c.Expr[U](
          Apply(
            Select(
              New(
                TypeTree(
                  appliedType(weakTypeOf[U].typeConstructor, weakTypeOf[T] :: Nil)
                )
              ),
              nme.CONSTRUCTOR
            ),
            List(e.tree, Literal(Constant(e.tree.toString)))
          )
        )
      }
    }
    
    class Signal[T](val expression: T, val expressionText: String)
    
    object Signal {
      def apply[T](e: T) = macro Macros.applyImpl[T, Signal[T]]
    }
    

    然后:

    scala> Signal(1).expressionText
    res0: String = 1
    

    正如预期的那样。


    作为 Miles Sabin points out on Twitter,让 U 成为类型构造函数会更好,因为在上面的版本中,宏实现对 U 做了一些假设,这些假设没有在类型中捕获。在这方面,以下是更安全的方法:

    import scala.language.experimental.macros
    import scala.language.higherKinds
    import scala.reflect.macros.Context
    
    object Macros {
      def applyImpl[
        T: c.WeakTypeTag,
        U[_]
      ](c: Context)(e: c.Expr[T])(implicit u: c.WeakTypeTag[U[_]]): c.Expr[U[T]] = {
        import c.universe._
    
        c.Expr[U[T]](
          Apply(
            Select(
              New(
                TypeTree(
                  appliedType(u.tpe.typeConstructor, weakTypeOf[T] :: Nil)
                )
              ),
              nme.CONSTRUCTOR
            ),
            List(e.tree, Literal(Constant(e.tree.toString)))
          )
        )
      }
    }
    
    class Signal[T](val expression: T, val expressionText: String)
    
    object Signal {
      def apply[T](e: T) = macro Macros.applyImpl[T, Signal]
    }
    

    特别注意SignalapplyImpl的第二个参数的变化。

    【讨论】:

    • 我不知道为什么弱类型标签有 T 而不是 Int,但这解决了它。
    • 有趣的编辑。我曾尝试这样做,但它不喜欢 U[_] 的语法:WeakTypeTag,所以我认为您不能拥有更高种类的 WeakTypeTag。我想我错了。我没想到要显式定义隐式参数(我不敢相信我刚才这么说)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-09
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 2021-02-13
    • 1970-01-01
    相关资源
    最近更新 更多