Статический возвращаемый тип макросов Scala
Итак, у меня есть этот макрос:
import language.experimental.macros
import scala.reflect.macros.Context
class Foo
class Bar extends Foo { def launchMissiles = "launching" }
object FooExample {
def foo: Foo = macro foo_impl
def foo_impl(c: Context): c.Expr[Foo] =
c.Expr[Foo](c.universe.reify(new Bar).tree)
}
Я сказал три раза, что хочу foo
вернуть foo
, и все же я могу сделать следующее (в 2.10.0-RC3):
scala> FooExample.foo
res0: Bar = [email protected]
scala> res0.launchMissiles
res1: String = launching
То же самое происходит, если я удаляю параметры типа на c.Expr
. Если я действительно хочу удостовериться, что тот, кто называет foo
, не может видеть, что они получают Bar
, мне нужно добавить типу типа в самом дереве.
Это на самом деле довольно хорошо - это означает, например, что я могу указать макрос в какой-либо схеме и создать анонимный подкласс некоторого класса Vocabulary
с помощью методов-членов, представляющих термины в словаре, и они будут доступны на возвращаемом объекте.
Я хотел бы точно понять, что я делаю, поэтому у меня есть несколько вопросов. Во-первых, каков тип возврата для метода foo
на самом деле? Доступно ли это для (необязательной) документации? Он явно ограничивает тип возврата (например, я не могу изменить его на Int
в этом случае), и если я полностью его удалю, то получаю следующее:
scala> FooExample.foo
<console>:8: error: type mismatch;
found : Bar
required: Nothing
FooExample.foo
^
Но я могу изменить его на Any
и все равно получить статически типизированный Bar
при вызове foo
.
Во-вторых, такое поведение указано где-то? Это похоже на довольно элементарный набор проблем, но я не смог найти четкое объяснение или обсуждение.
Ответы
Ответ 1
Это поведение недоказано, но предназначено, хотя оно может показаться запутанным. Мы планируем рассказать о роли возвращаемого типа в макроподписях, но в настоящий момент я чувствую, что гибкость - это хорошо.
Также временами поведение является непоследовательным, например. когда макрос попадает в середину вывода типа, его статическая подпись будет использоваться (т.е. Foo
в вашем примере), а не тип фактического расширения. Это потому, что макрораспределение намеренно задерживается до тех пор, пока не будет сделан вывод типа (так что реализации макросов получат вид предполагаемых типов, а не тип vars). Это компромисс, а не самый лучший, поэтому мы планируем скоро его пересмотреть: https://issues.scala-lang.org/browse/SI-6755.
Другая проблема в этом отделе связана с неявными макросами. Когда тип возврата неявного макроса является общим и должен быть выведен из запрашиваемого типа неявного значения, происходят плохие вещи. Это делает невозможным использование макросов для генерации тегов типов: https://issues.scala-lang.org/browse/SI-5923.