F# 区分联合类型问题
F# Discriminated Union Type Issue
我在让 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"))
我不明白为什么它允许我创建一个 "Error" 如果我分两步做,但是当我在一行上做同样的事情时,我得到一个值限制错误。
我错过了什么?
谢谢
更新
查看@kvb 的post,每次我需要创建一个错误时添加类型信息似乎有点冗长,所以我将它包装到一个额外的方法中,该方法创建一个错误并且有点更简洁。
// 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"))
我不确定用 'unit' 指定错误是否会产生我尚未预见的任何后果。
TryResult<unit,_> = Error(err)
你可以这样做:
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 有何相似之处,而 Lists 则不同?这是它们的 可变性 。当您尝试使用在单个步骤中可变的类型创建 TryResult 时,值限制会打扰您。
现在至于为什么两步过程解决了这个问题,这是因为构造函数使整个函数不可泛化,因为您正在将函数应用于构造函数。但是把它分成两步就可以解决这个问题。类似于案例2 here on MSDN.
您可以在上面的 MSDN 文章中阅读更多相关信息以及 this more indepth blog post.
中发生这种情况的原因
考虑这个细微的变化:
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"))
只要构造函数没有副作用就可以了。
我在让 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"))
我不明白为什么它允许我创建一个 "Error" 如果我分两步做,但是当我在一行上做同样的事情时,我得到一个值限制错误。
我错过了什么?
谢谢
更新
查看@kvb 的post,每次我需要创建一个错误时添加类型信息似乎有点冗长,所以我将它包装到一个额外的方法中,该方法创建一个错误并且有点更简洁。
// 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"))
我不确定用 'unit' 指定错误是否会产生我尚未预见的任何后果。
TryResult<unit,_> = Error(err)
你可以这样做:
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 有何相似之处,而 Lists 则不同?这是它们的 可变性 。当您尝试使用在单个步骤中可变的类型创建 TryResult 时,值限制会打扰您。
现在至于为什么两步过程解决了这个问题,这是因为构造函数使整个函数不可泛化,因为您正在将函数应用于构造函数。但是把它分成两步就可以解决这个问题。类似于案例2 here on MSDN.
您可以在上面的 MSDN 文章中阅读更多相关信息以及 this more indepth blog post.
中发生这种情况的原因考虑这个细微的变化:
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"))
只要构造函数没有副作用就可以了。