2012-12-02 133 views
38

所以我有這個宏:靜態返回類型宏

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,當我撥打foo時,仍然會獲得靜態類型Bar

其次,這種行爲在某處指定了嗎?這似乎是一個相當初級的問題,但我一直無法找到明確的解釋或討論。

+2

@ som-snytt:但我仍然會預期'foo'上的返回類型有最後一個字(儘管我也很高興它沒有)。 –

+2

'FooExample.foo'上的返回類型註釋在這裏非常奇怪。否則,我會期待宏的行爲。 – drstevens

+0

@drstevens:同意。 –

回答

19

此行爲未指定,但有意,儘管它可能會出現混淆。我們打算詳細說明返回類型在宏特徵中的作用,但目前我覺得靈活性是一件好事。

也有時行爲不一致,例如,當宏在類型推斷中被捕獲時,將使用它的靜態簽名(例如在你的示例中爲Foo),而不是實際擴展的類型。這是因爲宏擴展是故意延遲的,直到類型推斷完成(所以宏實現可以看到推斷類型,而不是類型變量)。這是一種權衡,不一定是最好的,所以我們打算很快重新審視它:https://issues.scala-lang.org/browse/SI-6755

這個部門的另一個問題是隱含的宏。當隱式宏的返回類型是通用的並且需要從所請求的隱式值類型中推斷時,會發生不好的事情。這使得目前不可能使用宏來生成類型標籤:https://issues.scala-lang.org/browse/SI-5923

+0

這是否意味着不可能在隱式搜索中使用實際擴展的類型,除非該搜索是由宏本身明確完成的? –

+1

現在不可能,以後可能不可能。否則,隱式搜索將不得不急切地擴展範圍內的所有隱式宏。你有沒有特別的用例? –

+0

不,我只是想形成一個關於事情如何工作的心理圖景;我想,在大多數情況下,c.inferImplicitValue將會訣竅。 –