我在让DU工作正常时遇到问题。我已经定义了一个新的DU,它的结果类型为<' a>或从System.Exception派生的任何异常
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"))
我无法理解为什么它允许我创建一个&#34;错误&#34;如果我分两步完成,但是当我在一行上做同样的事情时,我会得到一个价值限制错误。
我缺少什么?
由于
更新
通过@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"))
我不确定是否用&#39;单位指定错误&#39;会有任何后果,我还没预见到。
TryResult<unit,_> = Error(err)
答案 0 :(得分: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类型系统不支持通用值,它只支持具体类型的值。因此,每次使用MyOtherException
时都需要调用result3
构造函数;然而,这会导致任何副作用不止一次发生,这将是令人惊讶的。正如Ringil建议的那样,您可以通过告诉编译器将表达式视为值来解决这个问题:
[<GeneralizableValue>]
let result3<'a> : TryResult<'a,_> = Error(new MyOtherException("Some Error"))
只要构造函数没有副作用,这很好。
答案 1 :(得分:2)
你可以这样做:
let result3<'a> = Error (new MyOtherException("Some Error"))
编辑:
至于为什么你不能一步完成,首先请注意这会导致同样的错误:
let result4 = Result (new MyOtherException("Some Error"))
就像这样:
let result4 = Result ([|1;|])
但这有效:
let result4 = Result ([1;])
与Exception和Arrays有什么相似之处,但不是列表?它的可变性。当您尝试使用可在一个步骤中变为可变的类型创建TryResult时,值限制将打扰您。
现在关于为什么两步过程解决了这个问题,因为构造函数使得整个函数不能推广,因为你将一个函数应用于构造函数。但是把它分成两个步骤可以解决这个问题。它类似于案例2 here on MSDN。
您可以在上面的MSDN文章中了解更多相关信息,以及this more indepth blog post中发生这种情况的原因。