2016-06-28 15 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")) 

から派生した例外の結果を持っているいずれかの新しいDUを定義しました私は2つのステップでそれを行いますが、同じ行を1行で実行すると、値の制限エラーが発生します。

何が欠けていますか?

おかげ@kvbでポストを見てみると

UPDATE

は、私はエラーを作成する必要があるタイプの情報を追加するたびに少し冗長に見えたので、私は追加のメソッドにそれを包んエラーを作成し、もう少し簡潔です。

// 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コンストラクタを呼び出す必要があります。しかし、これは副作用が2回以上発生する結果となり、驚くべきことである。Ringilが示唆するように、あなたはとにかく値として式を治療するためのコンパイラを伝えることにより、この問題を回避することができます

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

これは限りコンストラクタが副作用を持っていないようで結構です。

+0

ありがとうございます。意味あり。私は私の質問を更新し、うまく動作するように見える追加のエラー作成メソッドを追加し、それを少しきちんと保ちます。問題は、タイプがTryResultとして指定されていることだけです。これがダウンサイドになるかどうか不明です。 – Moog

2

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

EDIT:

をあなたが一歩、これは同じエラーとなることを最初の音でそれを行うことができない理由について

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

このように:

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

しかし、これは動作する:

let result4 = Result ([1;]) 

は何例外と配列についての類似のだが、ではないリスト?それはの可変性です。値の制限は、1つのステップで変更可能なタイプのTryResultを作成しようとすると気になります。

2つのステップの処理がこれを解決する理由は、コンストラクタに関数を適用しているため、コンストラクタが関数全体を一般化できないためです。しかし、それを2つのステップに分けて解決します。ケース2 here on MSDNに似ています。

上記のMSDNの記事とその理由は、this more indepth blog postに記載されています。

+0

よろしくお願いいたします。それは1つの回避策です。 2つのステップでこの追加のパラメータを必要とする理由はありませんか? – Moog

+0

私はいくつかの説明を追加しました。 – Ringil

+4

配列は変更可能ではありませんが、リストはそうではありません - それは "一般化可能な式"とみなされます。例えば、あなたが 'Result([1] @ [2])'や 'Result [|]を試してみたらどうなるか見てみましょう。 |] '。また、あなたの回避策はあまりにも良くないようです。結果の型シグニチャは 'val result3 <'a>:TryResult 'であり、これは一般的ではありません。このルートに行く場合は、タイプ注釈を追加する必要があります。 – kvb

関連する問題