I came across a situation where I'd like compare a discriminated union that has one case which isn't comparable directly and that I have no need to use in (custom) comparison. For that reason, I wrote CustomEquality
and CustomComparison
functions. Alas I get a stackoverflow and in general I'm uncertain how to handle this kind of a equality override situation in F#. I read Equality and Comparison Constraints in F# by Don Syme, Chris Smith's F# Language Details (Gotchas).
<edit: As pointed out in the comments and as for context, what should be done with the Funky
case? I was entertaining a similar idea to How do I create a job queue using a MailboxProcessor? wherein I'd queue DUs that could be used to perform functions or to control a state machine.
Looking at that other post (I refrain from producing the code here), which uses elegantly MailboxProcessor
I see it has a similar problem and somewhat similar idea. I'm not sure if the design could be better or is there a way to compare functions and in any event I have some more questions regarding this (with a C# background).
Questions:
- How to avoid the stackoverflow in the
Equals
? (Probably something simple, but...) - Should the generic
IComparable
implemented? - Should
static member (=)
etc. be overloaded as per Chris' blog post? - What else should be overloaded in DUs in similar situation? Should
IEquatable
be implemented in case there are performance implementations?
I think some of these could be answered by looking at the IL (e.g. if the compiler already generates IEquatable
for DUs), but just to make sure I haven't overlooked anything.
The code
[<CustomEquality; CustomComparison>]
type FunkyUnion =
| Case1
| Case2
| Funky of (unit -> unit)
override x.Equals(obj) =
//This here will cause a stackoverflow to occur...
match obj with
| :? FunkyUnion as y -> (x = y)
| _ -> false
override x.GetHashCode() =
match x with
| Case1 -> 1
| Case2 -> 2
| _ -> 3
interface System.IComparable with
member x.CompareTo yobj =
match yobj with
| :? FunkyUnion as y -> compare x y
| _ -> invalidArg "yobj" "cannot compare value of different types"
interface System.IComparable<FunkyUnion> with
member x.CompareTo(y) =
compare x y
[<EntryPoint>]
let main argv =
let funky1 = FunkyUnion.Case1
let funky2 = FunkyUnion.Case2
if funky1 = funky2 then
printfn "!!!"
printfn "!"
0