2016-10-27 62 views
4

在將其標記爲重複之前:我知道這個問題與使用unit作爲類型參數時有關編譯錯誤的各種問題有關。一些例子:單位是什麼條件?

這些都遇到了類似這樣的一個問題:

type Interface<'a> = 
    abstract member MyFunc : unit -> 'a 

let implementingInstance = 
    { new Interface<_> with 
     member __.MyFunc() =() } // Compiler error! 

據我所知,代碼不會編譯,因爲使用編譯單元返回函數在內部返回,這是CLI的一個額外功能,而不是一種類型。

但是!下面似乎滿足編譯器:

type RecordVersion<'a> = 
    { MyFunc : unit -> 'a } 

let recordInstance = 
    { MyFunc = ignore } 

這也適用,如果我有一個lambda或let結合的模塊功能取代ignore

對我來說,這只是另一個完全相同的事情。 (儘管與F#設計準則不一致,後者建議優先使用接口而不是功能執行記錄類型。)

我對設計API的用戶指定使用的行爲和類型感興趣。因此,我想避免出現意外和令人困惑的編譯器錯誤的情況。但我不太確定該怎麼做。它看起來像F#的「功能」功能作爲一種類型的處理單元。

單位有這樣的虛假錯誤的確切條件是什麼?我可以通過打破設計準則並使用函數的記錄而不是接口來避免它們出現在我的API中嗎? (我不介意,但我不確定它是否能解決問題。)

+1

absurd :: Void - > a(Haskell)我認爲這有點相關;-) –

+1

因爲這是關於通用API的設計,所以這可能是相關的:http://stackoverflow.com/a/34028711/126014 –

回答

3

我相信規則是靜態已知有一個方法返回類型unit將被編譯爲.NET方法在.NET類型系統中返回類型void(通過靜態已知,我的意思是與通用方法或使用類型參數作爲返回類型的泛型類型的方法相反)。在調用時,編譯器隱藏返回void的方法與在CLR級別返回真值unit值的方法之間的區別。

在您的示例中出現的問題是因爲正確實現通用接口實際上需要CLR級別的返回類型爲unit(並且CLR確實在意unitvoid之間的區別)。換句話說,當且僅當你想重載一個方法,該方法通過一個靜態已知的方法返回一個泛型類的類型參數,返回unit(基於unit代替該類型參數)。在這裏重寫,我的意思是在類或接口上實現抽象方法或在類上重寫非密封方法。

泰米爾人指出,解決此限制的一種方法是確保您使用F#函數而不是方法。另一個解決方法是將額外的具體類引入具有虛擬泛型類型參數(稱爲T<'unit>)的層次結構中,並在任何會導致問題的位置返回Unchecked.defaultof<'unit>而不是()。然後你可以從T<unit>得到一個額外的非通用具體類T,一切都會正常工作。

+0

這解釋了它!謝謝,我有點希望你會停下來回答這個問題。 :) – Vandroiy

3

您的工作和非工作示例之間的區別在於,非工作示例是方法,在這種情況下(和AFAIK只有這種情況)F#編譯器會生成實際需要或返回void的IL代碼。在工作案例中,屬性的類型爲Microsoft.FSharp.Core.FSharpFunc<unit, unit>(又名unit -> unit),它沒有對獲取或返回void的東西進行「優化」。

所以,是的,使用記錄確實解決了問題。另一種可能性是,使與屬性的接口:

type Interface<'a> = 
    // Note the parentheses to make this member a property rather than a method 
    abstract member MyFunc : (unit -> 'a) 

let implementingInstance = 
    { new Interface<_> with 
     // Must use an explicit `fun() ->` instead of a method arg list 
     member __.MyFunc = fun() ->() } 

如果你的問題是與以單位作爲參數,則可以通過編寫需要(())作爲參數(即明確unit的方法來解決。值,而不是一個空的參數列表)。但對於返回值我不認爲有任何方法可以使方法的工作:/

+0

看來,這個問題只發生在實現抽象成員方法時?但爲什麼除了泛型和方法之外,問題還是繼承/接口?比方說,我用類型爲'unit - >'a'的非抽象成員創建了一個'T <'a>'類型。一個實例'T '似乎工作!在這種情況下,成員是不是也編譯爲一種方法,並且不應該因此失敗?我想確定問題的確切條件,因爲實際使用案例比問題中的示例更復雜。 – Vandroiy