2016-06-28 121 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