Scala: получить значение параметра по умолчанию: невозможно получить доступ к `apply$default$i` внутри объекта-компаньона

Я пишу макрос, который должен получить значение параметра конструктора по умолчанию. Этот ответ показывает, что это можно сделать, обратившись к сгенерированному компилятором методу apply$default$i в сопутствующем объекте, где apply — имя конструктора а i — это индекс параметра, основанный на 1.

Однако это не работает, если макрос вызывается из самого объекта-компаньона. Предположительно, проверка типов кода в сопутствующем объекте происходит до того, как компилятор сгенерирует метод apply$default$i.

Этот код работает (будь то написан вручную или сгенерирован макросом):

case class C(i: Int = 1)
object C 
def x: Int = C.apply$default$1

Но это не так:

case class C(i: Int = 1)
object C {
  def x: Int = C.apply$default$1
}

scalac жалуется, что value apply$default$1 is not a member of object C.

Мне нужно вызвать макрос из объекта-компаньона, потому что макрос определяет неявный экземпляр класса типов.

Я мог бы сгенерировать код, который во время выполнения будет использовать отражение для доступа к методу apply$default$i. Но это неэлегантно. Если я знаю, что компилятор сгенерирует определенный метод, как я могу получить к нему доступ в скомпилированном коде?


person danarmak    schedule 25.02.2015    source источник
comment
Кажется, эта проблема исчезла с Scala 2.11.8 - теперь работает доступ к параметрам по умолчанию из внутреннего вызова макроса в сопутствующем объекте.   -  person Rogach    schedule 13.11.2016


Ответы (2)


Вот соответствующий комментарий.

  // Default getters of constructors are added to the companion object in the
  // typeCompleter of the constructor (methodSig).

Возможно, ваш макрос может заменить ваш собственный имя во вложении, чтобы одновременно указать дополнительные методы.

person som-snytt    schedule 27.02.2015

Что ж, очевидный обходной путь — сгенерировать метод, вызывающий apply$default$1 вне объекта-компаньона и метод, вызывающий этот метод внутри:

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class C(i: Int = 1)
object C { def y: Int = x }
def x: Int = C.apply$default$1

// Exiting paste mode, now interpreting.

defined class C
defined module C
x: Int

scala> C.y
res6: Int = 1
person Alexey Romanov    schedule 26.02.2015
comment
Как я сказал в вопросе: мне нужно, чтобы макрос вызывался из объекта-компаньона, потому что он создает неявный экземпляр класса типов. И макрос не может генерировать код, который будет помещен в другое место, откуда он был вызван. - person danarmak; 26.02.2015
comment
Мне нужно, чтобы макрос вызывался из объекта-компаньона, потому что он создает неявный экземпляр класса типов. Я по-прежнему не вижу проблемы: y возвращает то, что делает x, что может быть экземпляром класса типов (но это будет проблемой, если ему нужен доступ к закрытым членам класса), а y может быть неявным. И макрос не может генерировать код, который будет помещен в другое место, откуда он был вызван. Да, поэтому вам нужно либо реализовать x только с помощью макроса, либо использовать аннотацию макроса с достаточно широкой областью действия. - person Alexey Romanov; 27.02.2015
comment
Весь смысл использования макроса заключается в том, что пользователям не нужно также вручную писать методы, которые (в данном случае) раскрывают свои аргументы по умолчанию. И я не хочу использовать макросы аннотаций, потому что они требуют плагина рая, а я пишу библиотеку. - person danarmak; 27.02.2015