Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How make .NET Mutable Dictionary<T, T> with StructuralComparison & Equality in F#

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)

like image 497
mamcx Avatar asked Jan 24 '16 21:01

mamcx


2 Answers

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
like image 158
Mark Seemann Avatar answered Nov 15 '22 12:11

Mark Seemann


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.

like image 41
Anton Schwaighofer Avatar answered Nov 15 '22 13:11

Anton Schwaighofer