Kurren
Kurren

Reputation: 837

F# equality/comparible on a generic type

I have a record type like this:

type Node<'T> = { doStuff: 'T -> unit ; id: int }

How can I implement equality/comparible so that only the id is equated/compared?

Note: This needs to work between nodes of different types. So equate/compare a Node<int> and a Node<string>. It doesn't seem trivial when generics are involved.

Upvotes: 2

Views: 576

Answers (2)

Vandroiy
Vandroiy

Reputation: 6223

It could be done via a common base type:

type IHaveId =
    abstract Id : int
    inherit System.IComparable

type [<CustomEquality; CustomComparison>] Node<'T> =
    { DoStuff: 'T -> unit ; Id: int }

    interface IHaveId with
        member n.Id = n.Id

    override n.Equals n' =
        match n' with
        | :? IHaveId as n' -> n.Id = n'.Id
        | _ -> invalidArg "n'" "Tried to equate incompatible types."

    override n.GetHashCode () = n.Id.GetHashCode()

    interface System.IComparable with
        member n.CompareTo n' =
            match n' with
            | :? IHaveId as n' -> n.Id.CompareTo n'.Id
            | _ -> invalidArg "n'" "Tried to compare incompatible types."

Note that this isn't very fast. Also, be careful: With public construction access to Node<'T>, it would be possible to construct nodes that do something different but are treated as equals.

Using an interface for DoStuff would allow structural comparison. If the objects and their IDs are centrally constructed, I'd consider [<ReferenceEquality>] and explicitly passing a comparison function to collections that require it (such as Set<_> or Map<_>).

Upvotes: 2

Bartek Kobyłecki
Bartek Kobyłecki

Reputation: 2395

I would guess that writing a static member for that type will be some solution

static member compareNodes (n1:Node<_>) (n2:Node<_>) =
    n1.id.CompareTo(n2.id)

Upvotes: 1

Related Questions