Structural Equality – or is it?

· .NET, F#

I was recently presented with a conundrum. We had constrained data valid for the domain in a record type. Sadly this record type contained a reference datatype, so built-in structural equality broke down as the reference type never was equal in the way we thought would make sense.

This gave me the opportunity to learn how you override the implementation of Equals and GetHashCode in F# which I was previously unfamiliar with.

This is the finished implementation of the record type, or one like it, rather:

 [<CustomEquality>]
 [<CustomComparison>]
type Structure =
    { 
        Name: StructureName
        Status: StructureStatus
        Format: Regex
    }
    with 
        interface IComparable with
            member this.CompareTo { Name = name; Status = status; Format = format } =
        compare ( this.Name, this.Status, this.Format.ToString() ) (name, status, format.ToString())
        interface IComparable with
            member this.CompareTo obj =
                 match obj with 
                     | null                  -> 1
                     | :? Structure as other -> (this :> IComparable<_>).CompareTo other                
                     | _                     -> invalidArg "obj" "not a Structure"
    override this.Equals (o: obj) =         
        match o with              
            |  :? Structure as os ->                 
                (this.Name, this.Status, this.Format.ToString()) =
                    (os.Name, os.Status, os.Format.ToString())
            | _ -> false
    override this.GetHashCode() =
        (this.Name, this.Status, thus.Format.ToString()).GetHashCode()

So yeah, ujse of pattern matching to determine data types in the non-generic functions and extensive use the built-in structural equality in tuples.

Very nice. With thanks to TeaDrivenDev and Isaac Abraham on Twitter (and this StackOverflow response)