所以我有这个宏:
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 = Bar@4118f8dd
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
。
其次,这个行为是在某处指定的吗?这似乎是一个相当基本的问题,但我无法找到明确的解释或讨论。
答案 0 :(得分:21)
此行为未指定但有意,但可能看起来令人困惑。我们计划详细说明返回类型在宏签名中的作用,但目前我觉得灵活性是一件好事。
有时行为也不一致,例如当宏在类型推断的中间被捕获时,将使用其静态签名(例如,在您的示例中为Foo
),而不是实际扩展的类型。这是因为宏扩展被有意地延迟,直到完成类型推断(这样宏实现才能看到推断类型,而不是类型变量)。这是一个权衡,不一定是最好的,因此我们计划很快重新审视它:https://issues.scala-lang.org/browse/SI-6755。
这个部门的另一个问题是隐式宏。当隐式宏的返回类型是通用的并且需要从所请求的隐式值的类型推断时,会发生不好的事情。这使得目前无法使用宏来生成类型标记:https://issues.scala-lang.org/browse/SI-5923。