Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

F# comparing discriminated unions' by case identifier

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
like image 362
Grozz Avatar asked Dec 20 '22 14:12

Grozz


1 Answers

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)
like image 125
jyoung Avatar answered Jan 06 '23 11:01

jyoung