Подтвердить что ты не робот

Scala Макросы: "невозможно создать TypeTag из типа T, имеющего нерешенные параметры типа"

Я играю с новыми макросами Scalas и нашел это gist из akshaal. Поскольку это швы, я не совсем понял. Учитывая следующий признак (поляMacro более или менее такие же, как в акшаальном примере)

case class Field[I <: AnyRef](name: String, get: I => Any)

type Fields[I <: AnyRef] = List[Field[I]]

trait FieldAccess {
import FieldMacors._
    import Field._
    import language.experimental.macros

    def fields[T <: AnyRef]: Fields[T] = macro fieldsMacro[T]

    def field[T <: AnyRef](name: String): Fields[T] = fields[T].headOption <-- does not work!
                                                             ^
}
 object FieldMacors {

import language.experimental.macros
import Field._

def fields[T <: AnyRef]: Fields[T] = macro fieldsMacro[T]

/**
 * Get a list of fiels
 */
def fieldsMacro[T <: AnyRef: c.TypeTag](c: Context): c.Expr[Fields[T]] = {
  import c.universe._
  val instanceT = c.typeOf[T]

  val fields = instanceT.members.filter(member => member.isTerm && !member.isMethod)

  // transform an iterable of expr in a expr of list.
  def foldIntoListExpr[T: c.TypeTag](exprs: Iterable[c.Expr[T]]): c.Expr[List[T]] =
    exprs.foldLeft(reify { Nil: List[T] }) {
      (accumExpr, expr) =>
        reify { expr.splice :: accumExpr.splice }
    }

  val fieldAccessores = for (field <- fields) yield {
    val name = field.name.toString.trim // Why is there a space at the end of field name?!
    val nameExpr = c literal name

    // Construct expression (x : $I) => x.$name
    val getFunArgTree = ValDef(Modifiers(), newTermName("x"), TypeTree(instanceT), EmptyTree)
    val getFunBodyTree = Select(Ident(newTermName("x")), newTermName(name))
    val getFunExpr = c.Expr[T => Any](Function(List(getFunArgTree), getFunBodyTree))
    reify {
      Field[T](name = nameExpr.splice, get = getFunExpr.splice)
    }
  }
  foldIntoListExpr(fieldAccessores)
}
}

компилятор жалуется на "Невозможно создать TypeTag из типа T с параметрами неразрешенного типа"

Как мне получить T в макросе или я должен реализовать другой макрос, который использует поляMacro

4b9b3361

Ответ 1

T: TypeTag контекст, связанный для параметра типа T, означает, что вам требуются аргументы типа, предоставленные вместо этого параметра, чтобы быть конкретными (т.е. не содержат ссылок на немаркированные параметры типа или элементы абстрактного типа). В противном случае возникает ошибка.

Примеры:

scala> val ru = scala.reflect.runtime.universe
ru @ 6d657803: scala.reflect.api.JavaUniverse = [email protected]

scala> def foo[T: ru.TypeTag] = implicitly[ru.TypeTag[T]]
foo: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.TypeTag[T]

scala> foo[Int]
res0 @ 7eeb8007: reflect.runtime.universe.TypeTag[Int] = TypeTag[Int]

scala> foo[List[Int]]
res1 @ 7d53ccbe: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]

scala> def bar[T] = foo[T] // T is not a concrete type here, hence the error
<console>:26: error: No TypeTag available for T
       def bar[T] = foo[T]
                       ^

scala> def bar[T] = foo[List[T]] // T being not concrete renders 
                                 // the entire compound type not concrete
<console>:26: error: No TypeTag available for List[T]
       def bar[T] = foo[List[T]]
                       ^

scala> def bar[T: TypeTag] = foo[T] // to the contrast T is concrete here
                                    // because it bound by a concrete tag bound
bar: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.TypeTag[T]

scala> bar[Int]
res2 @ 7eeb8007: reflect.runtime.universe.TypeTag[Int] = TypeTag[Int]

scala> def bar[T: TypeTag] = foo[List[T]]
bar: [T](implicit evidence$1: reflect.runtime.universe.TypeTag[T])reflect.runtime.universe.TypeTag[List[T]]

scala> bar[Int]
res3 @ 1a108c98: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]

scala> bar[List[Int]]
res4 @ 76d5989c: reflect.runtime.universe.TypeTag[List[List[Int]]] = TypeTag[scala.List[scala.List[Int]]]

Признание конкретных типов, которые могут быть применены во время компиляции, полезно. Наличие конкретных меток по умолчанию полезно, а также описано в https://issues.scala-lang.org/browse/SI-5884.

Однако, как вы видели себя, теги конкретного типа в макросах могут быть источником путаницы, потому что обычно макросы должны работать как для бетонных, так и для бетонных типов. Поэтому всегда следует использовать c.AbsTypeTag. По этой причине мы больше не разрешаем c.TypeTag границы контекста в 2.10.0-M7: https://github.com/scala/scala/commit/788478d3ab.

Edit. В 2.10.0-RC1 некоторая AbsTypeTag была переименована в WeakTypeTag. Все остальное о теге типа остается неизменным.