2016-06-28 120 views
6

我遇到了讓DU按預期工作的問題。我已經定義了一個新的杜其或者具有<類型「從System.ExceptionF#歧視聯盟類型問題

open System 

// New exceptions. 
type MyException(msg : string) = inherit Exception(msg) 
type MyOtherException(msg : string) = inherit MyException(msg) 

// DU to store result or an exception. 
type TryResult<'a, 't> = 
    | Result of 'a 
    | Error of 't :> Exception 

//This is fine. 
let result = Result "Test" 

// This works, doing it in 2 steps 
let ex = new MyOtherException("Some Error") 
let result2 = Error ex 

// This doesn't work. Gives "Value Restriction" error. 
let result3 = Error (new MyOtherException("Some Error")) 

衍生>或任何異常的結果,我不明白爲什麼它讓我創造一個‘錯誤’,如果我分兩步做,但是當我在一行上做同樣的事情時,我得到一個值限制錯誤。

我在想什麼?

由於

UPDATE

綜觀發佈者@kvb,每次我需要創建一個錯誤顯得有點冗長加法型信息,所以我裹捲到一個額外的方法,其創建一個錯誤,並且更簡潔一點。

// New function to return a Result 
let asResult res : TryResult<_,Exception> = Result res 

// New function to return an Error 
let asError (err : Exception) : TryResult<unit,_> = Error(err) 

// This works (as before) 
let myResult = Result 100 

// This also is fine.. 
let myResult2 = asResult 100 

// Using 'asError' now works and doesn't require any explicit type information here. 
let myError = asError (new MyException("Some Error")) 

我不確定指定'單元'的錯誤是否會產生任何後果,我還沒有預料到。

TryResult<unit,_> = Error(err) 

回答

5

考慮這個微小的變化:

type MyOtherException(msg : string) = 
    inherit MyException(msg) 
    do printfn "%s" msg 

let ex = new MyOtherException("Some Error") // clearly, side effect occurs here 
let result2 = Error ex // no side effect here, but generalized value 

let intResults = [Result 1; result2] 
let stringResults = [Result "one"; result2] // can use result2 at either type, since it's a generalized value 

let result3 = Error (MyOtherException("Some Error")) // result would be of type TryResult<'a, MyOtherException> for any 'a 

// In some other module in a different compilation unit 
let intResults2 = [Result 1; result3]  // why would side effect happen here? just using a generic value... 
let stringResults2 = [Result "one"; result3] // likewise here... 

的問題是,它看起來像result3是一種價值,但.NET類型系統不支持通用的值,它僅支持具體類型的值。因此,每次使用result3時,需要調用MyOtherException構造函數;然而,這會導致任何副作用發生超過一次,這將是令人驚訝的。作爲Ringil建議,您可以解決此告訴編譯器把表達式作爲值反正:

[<GeneralizableValue>] 
let result3<'a> : TryResult<'a,_> = Error(new MyOtherException("Some Error")) 

這是罰款,只要在構造函數沒有副作用。

+0

謝謝。這是有道理的。我已經更新了我的問題,並添加了一個額外的錯誤創建方法,它似乎可以正常工作並保持一點整理。唯一的問題是,該類型現在指定爲TryResult 不知道這是否有任何不利因素。 – Moog

2

你可以這樣做:

let result3<'a> = Error (new MyOtherException("Some Error")) 

編輯:

至於爲什麼你不能做到這一點一步到位,首先要注意這導致了同樣的錯誤:

let result4 = Result (new MyOtherException("Some Error")) 

像這樣:

let result4 = Result ([|1;|]) 

但是,這個工程:

let result4 = Result ([1;]) 

關於什麼的異常和陣列相似,但並不名單?這是他們的可變性。當您嘗試使用可在單個步驟中進行修改的類型創建TryResult時,值限制會打擾您。

現在至於爲什麼兩步過程解決了這個問題,這是因爲構造函數使整個函數不可泛化,因爲您正在將函數應用於構造函數。但將其分解爲兩個步驟解決了這一問題。它與案例2 here on MSDN類似。

您可以在上面的MSDN文章中閱讀更多關於它的信息,以及this more indepth blog post中發生這種情況的原因。

+0

好的,謝謝。這是一個解決方法。任何想法爲什麼它需要這個額外的參數,而在兩個步驟中做不到? – Moog

+0

我加了一些解釋。 – Ringil

+4

並不是數組是可變的,但列表不是 - 它是關於什麼被認爲是「一般化表達式」。例如,看看如果你嘗試'Result([1] @ [2])'或者Result [| |]'。另外,你的解決方法對我來說似乎不太好;得到的類型簽名是'val result3 <'a>:TryResult ',它不像所希望的那樣通用;如果你走這條路線,你應該添加一個類型註釋。 – kvb