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 theEquals()
override. Is it considered desirable to implementIEquatable
anyway, 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
Set
construction, requiring comparison) if I implementIComparable
but notIComparable<'T>
. Have I missed out on anything useful by not usingIComparable<'T>
?
Obviously I'm amenable to any other feedback on the quality/style of this code.
Many thanks!