F# comparing discriminated unions' by case identifier

﹥>﹥吖頭↗ 提交于 2019-12-23 15:43:20

问题


Is there a way to compare discriminated unions by their case-identifiers in F#?

type MyUnion =
| MyString of string
| MyInt of int

let x = MyString("hello")
let y = MyString("bye")
let z = MyInt(25)

let compareCases a b =
// compareCases x y = true
// compareCases x z = false
// compareCases y z = false

How do I implement compareCases function in a generic way?

I.e. something like the following, but more generic (reflection is ok):

let compareCases a b =
  match a with
  | MyString(_) -> match b with | MyString(_) -> true | _ -> false
  | MyInt(_) -> match b with | MyInt(_) -> true | _ -> false

回答1:


The problem with using GetType() is that it fails if you have 2 'dataless' cases.

Here is one way to do it: (Edited because the previous UnionTagReader was not being cached)

type MyDU =
    | Case1
    | Case2
    | Case3 of int
    | Case4 of int

type TagReader<'T>() =
    let tr = 
        assert FSharpType.IsUnion(typeof<'T>)
        FSharpValue.PreComputeUnionTagReader(typeof<'T>, System.Reflection.BindingFlags.Public)

    member this.compareCase (x:'T) (y:'T) =
        (tr x) = (tr y)

let tr = TagReader<MyDU>()

let c1 = Case1
let c2 = Case2
let c3 = Case3(0)
let c3' = Case3(1)
let c4 = Case4(0)

assert (c1.GetType() = c2.GetType() )  //this is why you can not use GetType()

assert tr.compareCase c1 c1
assert not (tr.compareCase c1 c2)
assert tr.compareCase c3 c3'
assert not (tr.compareCase c3 c4)



回答2:


First of all, you can improve your example like so:

let compare = function
| MyString _, MyString _, | MyInt _, MyInt _ -> true
| _ -> false

But here's the best way (with minimal reflection!):

let compare a b = a.GetType () = b.GetType ()



回答3:


This should do the trick

open Microsoft.FSharp.Reflection

type MyUnion =
    | MyString of string
    | MyInt of int

let x = MyString("hello")
let y = MyString("bye")
let z = MyInt(25)

let compareCases a b =
    FSharpValue.GetUnionFields (a, a.GetType()) |> fst
        = (FSharpValue.GetUnionFields (b, b.GetType()) |> fst)

though, in order to do anything with the values, you will still need to pattern match, so I do not quite see the point, to be honest.




回答4:


let compareCases (a : MyUnion) (b : MyUnion) =
    a.GetType().Name = b.GetType().Name


来源:https://stackoverflow.com/questions/23796457/f-comparing-discriminated-unions-by-case-identifier

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!