EDIT: It appears from the answer and comment added so far that I have not properly explained what I want. Here is an example:
// type not supporting any type of comparison
[<NoEquality>]
[<NoComparison>]
type blah () =
member x.huha = 0
// make a map, turns out to work whether x supports equality or not
let inline tt x =
Map.ofList [1, x]
let test () =
// maps can be compared for equality if the argument can
if (tt 1 = tt 2) then failwithf "strange"
// maps can be made no matter if the argument supports equality
if (tt (blah ())).Count <> 1 then failwithf "size"
// this does not compile
if tt (blah ()) = tt (blah ()) then ....
In short, I want my own type to behave just like map above. So it should support equality when the type argument does, and should not when the type argument doesn't. I also want the typechecker to stop me using equality when not supported, seeing as it clearly can do that for builtin types. Thanks again.
Original question: Various built-in F# types support equality if and only if some underlying type does. For example, Map<'k, 'd>
will support equality iff 'd
does (and this is detected at compile-time). Is it possible to implement this behaviour in user-code? Here is one failed attempt, and a version that compiles fine if the equality is unconditional. Many thanks.
[<NoComparison>]
type test_fails<[<EqualityConditionalOn>]'a> (content:'a) =
let eq_impl (x:test_fails<'a>) (y:obj) =
let y = y :?> test_fails<'a>
x.content = y.content
member x.content = content
override x.Equals (y:obj) =
eq_impl x y
[<NoComparison>]
type test_compiles<'a when 'a : equality> (content:'a) =
let eq_impl (x:test_compiles<'a>) (y:obj) =
let y = y :?> test_compiles<'a>
x.content = y.content
member x.content = content
override x.Equals (y:obj) =
eq_impl x y
You have part of the solution already: using [<EqualityConditionalOn>]
on the generic parameter.
The part you are missing: you need to use Unchecked.equals
instead of the normal =
operator within your map implementation (anywhere you're checking the equality of two 'a
values). Unchecked.equals
checks at run-time whether the type supports generic equality. If it does, it compares the two instances/values for equality as usual; if not, it falls back to a structural equality check or the type's implementation of the Object.Equals(obj)
method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With