F# 如何将通用判别并集转换为obj

F# 如何将通用判别并集转换为obj,f#,F#,给一个像杜邦一样的 type Result<'a, 'b> = Ok of 'a | Error of 'b 迄今为止的所有尝试都将'a和'b的类型限制为obj let toObjResult (x:obj) = match x with | :? Result<'a, 'b> as r -> match r with | Ok a -> Ok (box a) | Error b -> Error (box

给一个像杜邦一样的

type Result<'a, 'b> = Ok of 'a | Error of 'b
迄今为止的所有尝试都将
'a
'b
的类型限制为
obj

let toObjResult (x:obj) =
  match x with
  | :? Result<'a, 'b> as r ->
      match r with
      | Ok a -> Ok (box a)
      | Error b -> Error (box b)
  | _ -> Error <| (exn "Invalid type" |> box)

您必须在匹配表达式中匹配结果类型的确切泛型类型参数

let matchR r = 
     match r with
     | Ok a -> Ok (box a)
     | Error b -> Error (box b)

 let toObjResult (x:obj) =
      match x with
      | :? Result<bool, _> as r -> matchR r
      | :? Result<string, int> as r -> matchR r
      | :? Result<_, Exception> as r -> matchR r
      | _ -> Error (box "Invalid type" )
let matchR=
匹配
|确定a->确定(框a)
|错误b->错误(框b)
让我们看看结果(x:obj)=
将x与
| :? 结果为r->matchR r
| :? 结果为r->matchR r
| :? 结果为r->matchR r
|->错误(框“无效类型”)

遗憾的是,您无法在未实现的类型参数上进行匹配(这非常糟糕)

如果不使用反射、枚举所有类型或修改类型,就无法进行匹配

使用反射可能很慢,但可以让你做你想做的事情(参见[the
GenericType
active pattern]),来自@robkuz的答案通过列出你想涵盖的所有案例来说明如何做到这一点——问题是,这不能很好地扩展

最后,如果您愿意修改
结果
类型,您可以添加一个非通用接口,让您将值作为装箱值获取:

type IBoxedResult =
  abstract Boxed : Result<obj, obj>

and Result<'a, 'b> = 
  | Ok of 'a 
  | Error of 'b
  interface IBoxedResult with
    member x.Boxed =  
      match x with
      | Ok v -> Ok (box v)
      | Error v -> Error (box v)

对有关匹配泛型的问题的回答可能会有所帮助。这样做似乎不可取,因为它会将编译器错误替换为运行时错误。为什么要这样做?不会有运行时错误,因为类型保证为
Result
。许多函数通过http调用,每个函数都将返回
Result
。如果是
Ok
,则返回200和值。如果是
Error
,则返回400和错误消息。问题是如何获得相应的
'a
'b
值。模式匹配将不起作用,因为类型未知,因此将其转换为
obj
的方法解决了该问题。有趣的是我不能只匹配未实现的类型参数。我唯一能保证的是
b'
是一个例外。对于
a'
,它可以是任何值,因此不可能对所有值进行模式匹配。结果用
IBoxedResult
扩展。更简单的解决方案。
let toObjResult (x:obj) =
  match x with
  | :? Result<'a, 'b> as r ->
      match r with
      | Ok a -> Ok (box a)
      | Error b -> Error (box b)
  | _ -> Error <| (exn "Invalid type" |> box)
System.InvalidCastException: Unable to cast object of type 'Ok[System.Boolean,System.Object]' to type 'Result`2[System.Object,System.Object]'.
let matchR r = 
     match r with
     | Ok a -> Ok (box a)
     | Error b -> Error (box b)

 let toObjResult (x:obj) =
      match x with
      | :? Result<bool, _> as r -> matchR r
      | :? Result<string, int> as r -> matchR r
      | :? Result<_, Exception> as r -> matchR r
      | _ -> Error (box "Invalid type" )
type IBoxedResult =
  abstract Boxed : Result<obj, obj>

and Result<'a, 'b> = 
  | Ok of 'a 
  | Error of 'b
  interface IBoxedResult with
    member x.Boxed =  
      match x with
      | Ok v -> Ok (box v)
      | Error v -> Error (box v)
[ box (Ok true)
  box (Ok 1) ]
|> List.map (fun o -> (o :?> IBoxedResult).Boxed)