如何在F#中声明通用异常类型

12

我该如何定义以下的异常?

exception CustomExn<'TMessage> of 'TMessage list

1
根据F#规范(第164页,8.11),我认为这是不可能的。也许可以作为提案的好候选。 - Szer
1
我本来还想找一份指南,告诉我泛型异常是不可取的,但是我没找到。我仍然认为最好避免使用它们——你可能并不真的需要在异常上有那种细节,如果你确实需要,那么你可能正在看普通控制流而不是异常路径。 - scrwtp
@scrwtp:我使用它将第三方库的单子输出转换为异常。这是一个用例示例:https://stackoverflow.com/questions/49956854/convert-railway-oriented-failure-track-to-rx-friendly-errors/49966795#49966795 - Mohsen
2个回答

9
也许你可以直接继承自System.Exception类?
type CustomExn<'TMessage> (message:'TMessage list) =
    inherit System.Exception ()  

let test =
    try
        raise (CustomExn["string"] )
    with 
    | :? CustomExn<string> -> "CustomExn of string"
    | :? CustomExn<int> -> "CustomExn of int"
    | _ ->  "Everything else"

无法工作,因为在“try with”中,“with”子句不会是通用的。 - Szer
1
嗯,在你的回答中,你做了相同的事情。当你指定关闭异常类型时,它会被try/with捕获。 - 3615
这并不比仅创建非泛型异常定义更好。 - Szer
是的,因为您需要为每个封闭类型定义非泛型 exn 定义。那么泛型的整个意义就失去了。 - 3615
在您的情况下,您必须知道您的方法可能抛出哪种精确的封闭泛型类型(这不在函数签名中),否则您将丢失消息列表。最好的方法,正如我所说的,是使用 Result<'a, 'b list> - Szer

5
不确定是否可以使用F# 异常定义 根据 规范(第164-165页),这也不是一个好的解决方案,因为在这种情况下try with只能捕获ExceptionList<string>,所以没有一个好方法使它成为通用的。
type ExceptionList<'a>(msgList: 'a list) =
    inherit Exception()
    member __.MessageList = msgList

let mock() = 
    raise <| ExceptionList(["failed"])

try
    mock() //raises ExceptionList<string>
with
    //this catch block won't fire, because it is not generic, it is ExceptionList<obj>
    | :? ExceptionList<_> as exnList -> 
        exnList.MessageList 
        |> List.iter (printfn "%A")

更好的方法是使用Result<'a,'b list>
let mock'() = 
    if DateTime.Now.Ticks % 2L = 0L 
    then Ok()
    else Error(["failed"])

let result = mock'()
match result with
| Ok _ -> printfn "Ok!"
| Error (msgList) -> 
      msgList
      |> List.iter (printfn "%A")

新增 有一种类型损失的解决方法:

type ExceptionList(msgList: obj list) =
    inherit Exception()
    member __.MessageList = msgList

// Here is F# exception definition
exception CustomException of ExceptionList

let failwithMany msgs = 
    raise <| CustomException (ExceptionList(msgs))

let mock() =
    //zero type checking here
    failwithMany[1;2;"a";[];"failed"]

try
    mock()
with
    // exnList is list of objects
    | CustomException exnList ->
        exnList.MessageList 
        |> List.iter (printfn "%A")

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接