I Know F# have the MAP, but I wanna use the .NET Dictionary. This dict have key as string and values as F# values + the dict, ie:
type ExprC =
| StrC of string
| BoolC of bool
| IntC of int32
| DecC of decimal
| ArrayC of int * array<ExprC>
| RelC of RelationC
and RelationC = Dictionary<string, ExprC>
Now, the problem I wanna solve is how provide the RelationC type with structural equality. If is required to encapsulate the actual storage, how create a container that is a replacement for Dictionary, use it for mutable operations and have structural equality?
With the current answer, this code not work (of curse the implementation is not complete, however, this not even compile):
[<CustomEquality; CustomComparison>]
type MyDict() =
inherit Dictionary<string, ExprC>()
override this.Equals x =
match x with
| :? MyDict as y -> (this = y)
| _ -> false
override this.GetHashCode () =
hash this
interface System.IComparable with
member x.CompareTo yobj =
match yobj with
| :? MyDict as y -> compare x y
| _ -> invalidArg "MyDict" "cannot compare values of different types"
and [<StructuralEquality;StructuralComparison>] ExprC =
| IntC of int
| StrC of string
| MapC of MyDict
This is the error:
Error FS0377: This type uses an invalid mix of the attributes 'NoEquality', 'ReferenceEquality', 'StructuralEquality', 'NoComparison' and 'StructuralComparison' (FS0377)
If you absolutely must use Dictionary<string, ExprC>
, you could derive from Dictionary<'k, 'v>
and override Equals
:
type MyDict() =
inherit Dictionary<string, ExprC>()
override this.Equals x =
true // real implementation goes here
override this.GetHashCode () =
0 // real implementation goes here
Here, you'd need to implement Equals
to have structural equality, and you'll need to implement GetHashCode
to match you Equals
implementation.
Another alternative, if you don't need the concrete class Dictionary<'k, 'v>
, is to define your own class that implements IDictionary<TKey, TValue>
.
While possible, this sounds like a lot of work. It'd be much easier to use a Map
, which has structural equality by default:
let m1 = Map.ofList [("foo", 1); ("bar", 2); ("baz", 3)]
let m2 = Map.ofList [("bar", 2); ("foo", 1); ("baz", 3)]
let m3 = Map.ofList [("bar", 2); ("foo", 1); ("baz", 4)]
> m1 = m2;;
val it : bool = true
> m1 = m3;;
val it : bool = false
Regarding the question at the end of the updated original post: What is the reason for "This type uses an invalid mix..."? This is a bug in the F# compiler, the error message is misleading, see Github. The solution is to simply remove all attributes from MyDict
.
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