如何将通用的Discriminated Union转换为obj

时间:2017-03-16 10:31:28

标签: f#

鉴于DU喜欢

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
 <script type="text/javascript">
$(document).ready(function(){
    $(window).on('scroll', function(){ 
    $('ul li').removeClass("active");
    $('ul li').addClass("deactive");
 });
   });
</script>
<style>
.active{
    color:red;
}
.deactive{
color:green;
}
</style>
</head>
<body style="height:9500px;">
<ul>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
<li class="active">a</li>
</ul>

</body>
</html>

和一些功能

type Result<'a, 'b> = Ok of 'a | Error of 'b

如何定义一个函数来转换值?

let doA () = Ok true
let doB () = Error <| exn "Fail"
let doC = function | 1 -> Ok "one" | x -> Error x

用法

toObjResult : x:obj -> Result<obj, obj> //where x is guaranteed to be Result<'a,'b>

到目前为止,所有尝试都将let data = [ doA() |> box doB() |> box docC 1 |> box docC 2 |> box ] |> List.map toObjResult '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)

2 个答案:

答案 0 :(得分:4)

您必须在匹配表达式

中匹配Result类型的确切泛型类型参数
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" )
遗憾的是,你无法匹配未实现的类型参数(这真的很糟糕)

答案 1 :(得分:4)

如果不使用反射,枚举所有类型或修改类型,则无法执行此操作。

使用反射可能很慢,但可以让你做你想做的事情(参见[GenericType活动模式from this answer),@ robkuz的答案显示了如何通过列出所有案例来做到这一点你想要涵盖的 - 问题是这不能很好地扩展。

最后,如果您乐意修改Result<'a, 'b>类型,则可以添加一个非通用接口,以便将值作为盒装值获取:

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)

现在,您可以将obj投射到IBoxedResult并使用Boxed将值设为Reslt<obj, obj>

[ box (Ok true)
  box (Ok 1) ]
|> List.map (fun o -> (o :?> IBoxedResult).Boxed)