Как мне добавить конструктор без аргументов к классу дел Scala с макросовой аннотацией?
Я пытаюсь ответить на этот вопрос.
Вместо того чтобы писать:
case class Person(name: String, age: Int) {
def this() = this("",1)
}
Я думал, что буду использовать макроаннотации, чтобы расширить его от:
@Annotation
case class Person(name: String, age: Int)
Поэтому я попытался добавить новый конструктор как старый DefDef
использование квазицитат в значении макроаннотации, например:
val newCtor = q"""def this() = this("", 1)"""
val newBody = body :+ newCtor
q"$mods class $name[..$tparams](..$first)(...$rest) extends ..$parents { $self => ..$newBody }"
Но это возвращает ошибку: called constructor's definition must precede calling constructor's definition
Есть ли способ это исправить? Что я упустил?
Спасибо, что посмотрели, -Джулиан
1 ответ
Оказывается, что очень естественное намерение генерировать вторичный конструктор в макроаннотации выявило две разные проблемы.
1) Первая проблема ( https://issues.scala-lang.org/browse/SI-8451) о квазиквотах, испускающих неправильные формы дерева для вторичных конструкторов. Это было исправлено в 2.11.0-RC4 (еще не выпущено, в настоящее время доступно как 2.11.0-SNAPSHOT) и в раю 2.0.0-M6 для 2.10.x (выпущено вчера).
2) Второй вопрос - о неназначенных позициях, наносящих ущерб во время проверки типов. Любопытно, что, когда проверка типов вызывает вызовы конструкторов, typer использует позиции, чтобы решить, являются ли эти вызовы законными или нет. Это не может быть исправлено так легко, и мы должны обойти:
val newCtor = q"""def this() = this(List(Some("")))"""
- val newBody = body :+ newCtor
+
+ // It looks like typer sometimes uses positions to decide whether stuff
+ // (secondary constructors in this case) typechecks or not (?!!):
+ // https://github.com/xeno-by/scala/blob/c74e1325ff1514b1042c959b0b268b3c6bf8d349/src/compiler/scala/tools/nsc/typechecker/Typers.scala#L2932
+ //
+ // In general, positions are important in getting error messages and debug
+ // information right, but maintaining positions is too hard, so macro writers typically don't care.
+ //
+ // This has never been a problem up until now, but here we're forced to work around
+ // by manually setting an artificial position for the secondary constructor to be greater
+ // than the position that the default constructor is going to get after macro expansion.
+ //
+ // We have a few ideas how to fix positions in a principled way in Palladium,
+ // but we'll have to see how it goes.
+ val defaultCtorPos = c.enclosingPosition
+ val newCtorPos = defaultCtorPos.withEnd(defaultCtorPos.endOrPoint + 1).withStart(defaultCtorPos.startOrPoint + 1).withPoint(defaultCtorPos. point + 1)
+ val newBody = body :+ atPos(newCtorPos)(newCtor)