Search code examples
f#comparisonicomparable

F# comparison vs C# IComparable


My problem, in a nutshell, is this:

What can I do about storing a tuple (or any type with a constraint of 'comparison') in a C# container that requires an IComparable?

This works:

> let x (y : 'a when 'a : comparison) = y ;;
val x : y:'a -> 'a when 'a : comparison

> x (1,2) ;;
val it : int * int = (1, 2)

I would have thought this would work:

> let x (y : IComparable<int>) = y ;;

val x : y:IComparable<int> -> IComparable<int>

> x (1,2) ;;

  x (1,2) ;;
  ---^^^

stdin(28,4): error FS0001: The type ''a * 'b' is not compatible with the type 'IComparable<int>'

And this as well:

> let x (y : IComparable) = y ;;

val x : y:IComparable -> IComparable

> x (1,2) ;;

  x (1,2) ;;
  ---^^^

stdin(30,4): error FS0001: The type ''a * 'b' is not compatible with the type 'IComparable'

EDIT

I follow the argument that F# doesn't do implicit upcasting. However, even explicitly:

> (1, 2) :> IComparable ;;

  (1, 2) :> IComparable ;;
  ^^^^^^^^^^^^^^^^^^^^^

stdin(43,1): error FS0193: Type constraint mismatch. The type 
    int * int    
is not compatible with type
    IComparable    
The type 'int * int' is not compatible with the type 'IComparable'

I suppose this makes sense as the comparability of a F# tuple is inferred structurally within the F# type system, and perhaps that extra information is not available to .NET.

It seems one workaround per a comment below is invoking

Tuple<_,_> (1,2) ;;

Or even

box (1, 2) :?> IComparable ;;

Solution

  • Definitely some weirdness going on. FWIW, it works if you construct a System.Tuple<_, _> explicitly, so that might be a workaround:

    let x (y : IComparable) = y
    let t = (2, 3)
    
    x (Tuple<_,_> t)