Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In F#, is it possible to check if two values have the same constructor?

Assuming I have a big discriminated union type:

type Thing =
    | One of int
    | Two of string
    | Three of bool option
      ...

And I have this function:

let comp a b = match a, b with
    | One _, One _ -> true
    | Two _, Two _ -> true
      ...
    | _, _ -> false

Is there a way to write the function in a neater, shorter way that doesn't require me to list every single constructor?

like image 409
user2649762 Avatar asked Mar 09 '23 04:03

user2649762


2 Answers

Basically, this is not possible. Even if you could get the constructors for your values, they are not comparable because they are functions. There's a bit of boilerplate involved but you could define tag values and a function to map to the tags:

let thingCase thing =
    match thing with
    | One _ -> 1
    | Two _ -> 2
    | Three _ -> 3

let comp a b = thingCase a = thingCase b

This is flexible enough to work on sequences too:

let compSeq things =
    things
    |> Seq.map thingCase
    |> Seq.pairwise
    |> Seq.forall (fun (a, b) -> a = b)

Note: you could also do this with reflection but it's generally best to avoid.

like image 100
TheQuickBrownFox Avatar answered Mar 10 '23 16:03

TheQuickBrownFox


I'm not quite sure how good it is performance-wise, but it is possible to do this using FSharp.Reflection.

open FSharp.Reflection

type Thing =
    | One of int
    | Two of string
    | Three of bool option

let tagOfThing = FSharpValue.PreComputeUnionTagReader(typeof<Thing>)
// val tagOfThing : obj -> int

let isSameThingCase (a: Thing) (b: Thing) =
    tagOfThing a = tagOfThing b

Use:

> isSameThingCase (One 1) (One 2);;
val it : bool = true
> isSameThingCase (Two "test") (Three None);;
val it : bool = false
like image 43
Tarmil Avatar answered Mar 10 '23 18:03

Tarmil