天天看點

d推導屬性的想法

​​原文​​​ 許多人要求更多​

​自動推導​

​屬性.

推導屬性

今天​

​D​

​​為​

​auto​

​​傳回及​

​模闆​

​推導屬性:

auto foo() { }

void bar() {}

pragma(msg, typeof(foo)); // pure nothrow @nogc @safe void()
pragma(msg, typeof(bar)); // void()      

注意,不用寫,​

​foo​

​​自動得到​

​pure nothrow @nogc @safe​

​​,這就是​

​推導屬性​

​​.

​​

​rikki cattermole​

​​提議擴充它,來友善關注​

​屬性​

​的使用者.我喜歡,但要注意:

​運作時​

​​排程

函數文檔

​​

​相容​

​​合同

​​

​依參​

​​屬性

​​

​ABI​

​​和​

​.di​

​檔案相容性

運作時排程

先要知道這些是​

​靜态屬性​

​​,除非強制,與​

​運作時排程​

​不一緻.

interface Foo {
    void func();
}

class Bar : Foo {
    void func() {}
}      

示例中,顯然無法推導​

​Foo.func​

​​,因為沒有可分析主體.可繼承它并幹活;​

​接口​

​​不做保證(當然,除非接口明确指定了​

​一些屬性​

​​,迫使它的​

​所有實作​

​​至少也如此嚴格).

但是​​

​Bar.func​

​​确實有一個主體.可推導出來嗎?好吧,如果它是​

​final​

​​,我會說​

​是的​

​​:在​

​子類​

​​實作上使用比​

​父接口​

​​要求更嚴格的​

​屬性​

​​.是以與父接口的關系不是障礙;可按需​

​嚴格​

​的推導,并且仍可實作.

但是,由于​

​Bar.func​

​​不是​

​final​

​​,是以​

​調用​

​​它時,并不知道​

​實際​

​調用的函數體.考慮:

class Baz : Bar {
    override void func() { some_global ~= x; }
}

void main() {
    Bar b = new Baz();
    b.func();
}      

此時,即使是選擇調用的​

​Bar.func​

​​接口可能已經可使用它,​

​主函數​

​​也不能是​

​@nogc​

​​或​

​pure​

​​.因為​

​override​

​​導緻運作時替換​

​函數體​

​.

教訓是:可推導​

​終(final)方法​

​​屬性,但不能​

​推導​

​​虛方法,因為​

​運作時​

​​排程允許實作的​

​正式接口​

​​替換主體.必須用​

​顯式屬性​

​​限制​

​子類​

​​,否則會破壞​

​替換​

​.

除非标記​

​Baz.func​

​​為​

​final​

​​并確定通過該接口調用它,否則​

​推導​

​​失敗會傳播到​

​main​

​:

class Baz : Bar {
    final override void func() { some_global ~= x; }
}

void main() @safe {
    //這很好,仍可在`Baz`上推導出`@safe`,因為它是`final`,是以知道,實際使用的函數體.
    Baz b = new Baz();
    b.func();
}      

函數文檔

​自動生成​

​​文檔傾向于檢視​

​靜态注解​

​​,但了解​

​推導出的内容​

​​可能很有用.​

​docgen​

​​希望編譯器告訴​

​docgen​

​​推導内容,但是​

​docgen​

​​應該指出它是​

​推導​

​​出來的.因為​

​相容性合同​

​,這很重要.

相容性合同

如果庫在​

​未來版本​

​​中推導出的​

​屬性​

​​變化了,這是重大更改嗎?好吧,(就像定義​

​重大更改​

​​一樣),我的回答是"得根據你的​

​承諾​

​​".文檔定義了這些​

​承諾​

​​,是以​

​文檔​

​​要指出​

​靜态保證​

​​和​

​推導​

​間的差別.

如果我寫:

Color parseColor(string name) @safe @nogc { ... }      

則我保證它是​

​@safe​

​​和​

​@nogc​

​​.如,如果要釋出有​

​配置設定​

​​的​

​新版本​

​​,則是​

​重大變化​

​​.

但是,同時,很可能推導它為​​

​pure​

​​,然後就可在​

​純函數​

​​中使用它.它會​

​編譯​

​​,但由于我列舉​

​pure​

​​,我不保證未來保持​

​純​

​​.

當然,很有可能!我可能在​​

​未來​

​​版本中添加​

​pure​

​​來擴充保證.但是,如果将來更改為,如,從​

​全局變量​

​​中檢查使用者​

​本地​

​​,則推導不再有​

​pure​

​​.而且由于我沒有列舉​

​純​

​​,我可叫它​

​新功能​

​​(次要版本加1),而不是​

​破壞性更改​

​(主要版本加1).

這樣就​

​很好​

​.

依參屬性

因為​

​實作​

​​而改變的​

​推導屬性​

​​是一回事,但如果它因傳遞​

​不同參數​

​​而改變呢?

這在今天适合模闆,但​​

​代價​

​​是另一個​

​執行個體化​

​:

void forward(Dg)(Dg dg) {
    dg();
}

void main() {
    void delegate() notSafe;
    void delegate() @safe yesSafe;

    //調用`forward`時,自動用參數類型執行個體化它.
    //是以調用`forward(notSafe);`它會這樣:
    pragma(msg, typeof(forward!(typeof(notSafe))));
    //注意用`notSafe`建立時是,`system void(void delegate()dg)`
    //而調用`forward(yesSafe);`它這樣:
    pragma(msg, typeof(forward!(typeof(yesSafe))));
    //但當用`yesSafe`建立時,它是`@safe void(void delegate()@safe dg)`
    //注意一個是`@system`,另一個是`@safe`.
    //它從參數中推導出不同的類型.
    //但請注意它是不同的類型和不同的位址:
    assert(&forward!(typeof(notSafe)) != &forward!(typeof(yesSafe)));
    //因為它們是不同的執行個體化!
}      

模闆幹的是把​

​給定參數​

​​粘貼到​

​新函數體(a)​

​​中,然後通過​

​推導屬性系統​

​​傳遞​

​a​

​​.

常見模式是使用​​

​帶注解​

​​的​

​單元測試​

​​來說明,它有時可能有​

​該屬性​

​​,測試提供了一例,這樣編譯器在​

​編譯​

​測試時檢查它.

但是,如果推導​

​非模闆函數體​

​​,則隻有​

​一個主體​

​​,是以要推導​

​參數​

​​,必須使​

​該部分​

​​成為單個類型,而不是依賴傳入的​

​多個​

​類型.

有一個​​依參屬性dip​​​,該概念簡稱為​

​inout屬性​

​​,該想法使​

​函數​

​​的屬性依賴于​

​參數​

​​屬性.使函數​

​其餘​

​​部分比​

​屬性​

​​要求更嚴格.但推遲最終​

​結果​

​​到​

​傳遞内容​

​​.隻需要​

​一個函數體​

​​就可完成,不必​

​一個函數,多個副本​

​.

ABI和.di檔案相容性

​d​

​​作者擔心,​

​屬性​

​​成為​

​混雜名​

​​的一部分,如果實作​

​調整​

​​更改了​

​推導​

​​屬性,也會​

​更改​

​​連結器看到名字.如果​

​綁定​

​​不比對,會導緻​

​"未定義引用"​

​錯誤.

它放在最後,因為這是我最不擔心的;​

​沃爾特​

​​擔心它.​

​更改代碼​

​​可能會​

​破壞​

​​接口,并不奇怪,但它與​

​更改函數體​

​​,并改名和改原型或改連結沒啥差別.

可這樣緩解:​​

​1)​

​​D中單獨​

​綁定​

​​相對較少,最好​

​簡單​

​​使用​

​源碼​

​​,及​

​2)​

​​可用​

​dmd-H​

​​自動生成它們.現在,​

​dmd-H​

​​很糟糕(我想修複它,編譯器應該可使它更好工作,另外生成​

​.di​

​​或可能包含​

​推導​

​​值的​

​dmdjson​

​​輸出可能是​

​docgen​

​​的​

​良好資料源​

​),有時它确實有效.

而且當你​

​遇見​

​​連結器錯誤時,很容易​

​更新​

​​綁定,盡管回到​

​相容性合同​

​​概念時,需要​

​更新綁定​

​​可能會讓人感到意外.但是,庫作者應該提供​

​.di​

​綁定.

是以,雖然在​

​特例​

​​下會​

​加倍​

​​,但​

​庫作者​

​​是最期望的,今天修複它并不太難,以後可能會​

​更容易​

​.

編譯速度

結論

繼續閱讀