Как да добавя конструктор без аргументи към клас case на Scala с макро анотация?

Опитвам се да отговоря на този въпрос.

Вместо да пише:

case class Person(name: String, age: Int) {
  def this() = this("",1)
}

Мислех, че ще използвам макро анотации, за да го разширя от:

@Annotation
case class Person(name: String, age: Int)

Така че се опитах да добавя новия конструктор като обикновен стар DefDef, използвайки квазикавички в impl на макро анотация, като:

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

Има ли начин това да се поправи? Какво пропуснах?

Благодаря, че погледна, Джулиан


person Julian Peeters    schedule 31.03.2014    source източник


Отговори (1)


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

1) Първият брой (https://issues.scala-lang.org/browse/SI-8451) е за квазикавички, излъчващи грешни дървовидни форми за вторични конструктори. Това беше поправено в 2.11.0-RC4 (предстои да бъде пуснат, в момента е наличен като 2.11.0-SNAPSHOT) и в paradise 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)
person Eugene Burmako    schedule 31.03.2014
comment
Уви, използвам paradise 2.0.0-M6 за 2.10.3. Ето минимален пример за изпълнение. И ето е грешката в M4. - person Julian Peeters; 31.03.2014
comment

Опитвам се да запиша променлива във файл и да използвам променливата като име на самия файл. Някакви идеи?

ПРИМЕР 1: Името на файла и съдържанието в него трябва да са „helloworld“

#/bin/bash
OUTPUT="helloworld"
echo $OUTPUT > ~/Desktop/directory/outputs/$OUTPUT.txt

ПРИМЕР 2: Името на файла и съдържанието във файла трябва да са "hellokitty"

#/bin/bash
OUTPUT="hellokitty"
echo $OUTPUT > ~/Desktop/directory/outputs/$OUTPUT.txt
- person Eugene Burmako; 31.03.2014
comment
Изглежда, че заобиколното решение е успешно и за двойно анотирани класове, стига @NewCtorAnnotation да предшества непосредствено анотирания. Същата грешка обаче се връща, ако @AnotherAnnotation е между тях (не е проблем за мен, само за информация). - person Julian Peeters; 07.04.2014