как определить псевдоним типа для более высокого типа с помощью аннотации макроса scala (предпочтительно с использованием квазикавычек)

Я разрабатываю аннотацию макроса scala, которая обогащает объекты различными определениями (см. макрос формы воспроизведения). Среди прочего я хочу, чтобы объект содержал псевдоним типа

type WFS = FS[_, _, _, _]

для различного количества подстановочных аргументов.

Я уже пытался извлечь значение одного типа подстановочного знака с помощью

q"type WFS = FS[_]" match { q"type WFS = FS[$t]" => t }

и надеялся использовать извлеченное значение в списке параметров типа (например, q"type WFS = FS[..$tplist]"). Тем не менее, приведенное выше утверждение приводит к ошибке:

scala> q"type WFS = FS[_]" match { case q"type WFS = FS[$t]" => t }
scala.MatchError: type WFS = FS[_$1] forSome { 
  <synthetic> type _$1 >: _root_.scala.Nothing <: _root_.scala.Any
} (of class scala.reflect.internal.Trees$TypeDef)
    at .<init>(<console>:15)
    at .<clinit>(<console>)
    at .<init>(<console>:7)
    at .<clinit>(<console>)
    at $print(<console>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43

Есть ли другой, может быть, более простой способ построения нужного дерева?


person Stefan    schedule 04.02.2014    source источник


Ответы (1)


Ошибка печатает фактическую форму значения, с которым вы сопоставляетесь, и, по-видимому, вам нужно использовать ту же форму в соответствующей квазицитате:

scala> q"type WFS = FS[_]" match { case q"type WFS = FS[$a] forSome { $b }" => println(s"$a --- $b") }
_$1 --- <synthetic> type _$1 >: _root_.scala.Nothing <: _root_.scala.Any

Если вы хотите разрешить больше параметров, а не только один, вам лучше использовать "..$x" для этого:

scala> q"type WFS = FS[_]" match { case q"type WFS = FS[..$a] forSome { ..$b }" => println(s"$a --- $b") }
List(_$1) --- List(<synthetic> type _$1 >: _root_.scala.Nothing <: _root_.scala.Any)

Подстановочные знаки преобразуются в экзистенциальные — FS[_] означает FS[T] forSome { type T } (подробнее об экзистенциальных типах см. Спецификацию языка Scala, раздел 3.2.10, в разделе «Синтаксис заполнителя для экзистенциальных типов»). Когда вы пишете FS[$a] в соответствующей квазикавычке, это означает: «Я ожидаю здесь применения конструктора типа к одному аргументу типа». Но FS[T] forSome { type T } — это экзистенциальный тип, который содержит применение конструктора типа к аргументу, поэтому шаблон не будет совпадать.

Чтобы лучше понять это и узнать, как отлаживать подобные проблемы, полезно посмотреть с помощью showRaw на деревья, сгенерированные квазицитатами — поскольку это типы, нам нужны квазицитаты типов, то есть tq"...":

scala> showRaw(tq"FS[_]")
res15: String = ExistentialTypeTree(AppliedTypeTree(Ident(newTypeName("FS")), List(Ident(newTypeName("_$1")))), List(TypeDef(Modifiers(DEFERRED | SYNTHETIC), newTypeName("_$1"), List(), TypeBoundsTree(Select(Select(Ident(nme.ROOTPKG), newTermName("scala")), newTypeName("Nothing")), Select(Select(Ident(nme.ROOTPKG), newTermName("scala")), newTypeName("Any"))))))

scala> showRaw(tq"FS[T]")
res16: String = AppliedTypeTree(Ident(newTypeName("FS")), List(Ident(newTypeName("T"))))

Насколько я понимаю, сопоставление с квазикавычками означает сопоставление с соответствующим деревом. Таким образом, приведенное выше демонстрирует проблему, которую я описал.

К сожалению, квазикавычки не скрывают такие детали системы типов Scala. Это может быть или не быть ошибкой квазикавычек, но я не могу это комментировать — я думаю, было бы круто, если бы не нужно было иметь дело с

person Blaisorblade    schedule 04.02.2014
comment
Спасибо - это было хорошее наблюдение! Теперь я попытался создать тип WFS с помощью q"type WFS = FS[..$typeParams] forSome { ..$typeDefs }", где каждый typeParam был построен с помощью tq"${newTypeName(s"C$index")}", а каждый typeDef с помощью q"type $typeParam". К сожалению, это приводит к исключению компилятора: no-symbol не имеет владельца в scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49) в scala.tools.nsc.Global.abort(Global.scala:254) в scala.reflect.internal.Symbols$NoSymbol.owner(Symbols.scala:3248) - person Stefan; 05.02.2014
comment
Ой, это эквивалент исключения NullPointerException для символов. Обычно компилятор заполняет символы в деревьях, чтобы сказать, на какое определение (символ) указывает имя. Я не понимаю (пока) аннотаций макросов настолько, чтобы дать хорошо продуманный комментарий — я подозреваю, что вам лучше задать дополнительный вопрос с исходным кодом. - person Blaisorblade; 06.02.2014