reflectionf#icomparable

Comparing two objects with default IComparable implementation with F# Reflection


Given two objects in F#, is there a way to use their IComparable method to compare them, assuming they are both of the same sub-type and that IComparable is implemented for their common sub-type.

What I am trying to achieve in pseudo-code :

    let tycompare (o1 : obj) (o2 : obj) : int option =
        let (ty1, ty2) = (o1.GetType(), o2.GetType())
        if ty1 <> ty2 then
            None
        else
            if IComparable is implemented on ty1 then
                o1.CompareTo(o2) |> Some
            else
                None

I am aware of this post but I do not think it helps answering my question directly.


Solution

  • You could also write this more tersely with Option.bind, but this is a good fit for pattern matching.

    We can define an active pattern for matching IComparable.

    let (|IsComparable|) (obj : obj) = 
        match obj with
        | :? IComparable as comparable -> Some(comparable)
        | _ ->  None
    

    F# lets you use active patterns in let bindings, so that the intent of function is more clearly conveyed.

    let compare (IsComparable o1) (IsComparable o2) =        
        match (o1, o2) with
        | (Some o1, Some o2) when 
            o1.GetType() = o2.GetType() -> Some(o1.CompareTo(o2))
        | _ -> None
    

    This can also be compacted (without the active-pattern - @kaefer) :

    let compare (o1 : obj) (o2: obj) = 
        match (o1, o2 ) with
        | (:? System.IComparable as o1), (:? System.IComparable as o2) when 
            o1.GetType() = o2.GetType() -> Some(o1.CompareTo(o2))
        | _ -> None