I'm working on some book material on classes and interfaces in F#. My example is a latitude/longitude class:
open System type LatLon(latitude : float, longitude : float) = member __.Latitude = latitude member __.Longitude = longitude override this.GetHashCode() = hash (this.Latitude, this.Longitude) override this.Equals(thatObj) = match thatObj with | :? LatLon as that -> this.Latitude = that.Latitude && this.Longitude = that.Longitude | _ -> false interface IEquatable<LatLon> with member this.Equals(that : LatLon) = this.Equals(that) interface IComparable with member this.CompareTo(thatObj) = match thatObj with | :? LatLon as that -> compare (this.Latitude, this.Longitude) (that.Latitude, that.Longitude) | _ -> raise <| ArgumentException("Can't compare instances of different types") let landsEnd = LatLon(50.07, -5.72) let johnOGroats = LatLon(58.64, -3.07) let landsEnd2 = LatLon(50.07, -5.72) let demo() = // false printfn "%b" (landsEnd = johnOGroats) // true printfn "%b" (landsEnd = landsEnd2) // 50.070000, -5.720000 // 58.640000, -3.070000 [ landsEnd; johnOGroats; landsEnd2 ] |> Set.ofList |> Seq.iter (fun ll -> printfn "%f, %f" ll.Latitude ll.Longitude)
By the way, I'm well aware that in this case, a record type would give the necessary structural equality out of the box. But the material I want to cover with this example is specifically about classes.
My main concerns are:
The code works the same if I don't implement
IEquatable, as it uses the
Equals()override. Is it considered desirable to implement
IEquatableanyway, and/or does it have advantages I haven't spotted?
What about the (currently very-lightly-documented)
op_Equality? Should that be used as well/instead?
The code works fine (for example
Setconstruction, requiring comparison) if I implement
IComparable<'T>. Have I missed out on anything useful by not using
Obviously I'm amenable to any other feedback on the quality/style of this code.