Ошибка типа при использовании параметра типа в качестве аргумента типа для другого типа, передаваемого в реализацию макроса

Я пытаюсь создать макрос, который позволит мне захватить текст выражения, передаваемого конструктору. Мне нужен текст выражения для целей отладки. Реализация макроса следующая:

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 }

И это работает. Но, конечно, это совсем не тот синтаксис, к которому я стремлюсь. Это баг или я как-то не так делаю? Есть ли обходной путь?


person Nimrand    schedule 15.10.2013    source источник


Ответы (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

Как и ожидалось.


Как Майлз Сабин указывает в Твиттере, было бы лучше, если бы 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]
}

Обратите особое внимание на изменение второго аргумента applyImpl в Signal.

person Travis Brown    schedule 15.10.2013
comment
Я не уверен, почему тег Weak Type имеет T вместо Int, но это исправлено. - person Nimrand; 15.10.2013
comment
Интересная редакция. Я пытался это сделать, но ему не нравился синтаксис U[_] : WeakTypeTag, поэтому я предположил, что у вас не может быть WeakTypeTag более высокого типа. Думаю, я был неправ. Я не думал явно определять неявный параметр (не могу поверить, что только что это сказал). - person Nimrand; 16.10.2013