F# does not do implicit upcasting as C# does. If you request an IComparable
, then you are requesting an IComparable
and not something which can be upcast to IComparable
What you really want, is requesting a type, that happens to implement IComparable
, but you are still working with the specific type.
That's why let x (y : 'a when 'a : comparison)
, see that y
is of type 'a
, whereas 'a
can be statically upcast to comparison
(if you want to access a member of comparison
, you will have to upcast to comparison
using :>
first)
On the other hand let x (y : IComparable<int>) = y
requests very explicitly a IComparable<int>
. But you are passing (1,2)
, a value, that can be upcast to IComparable
. So if you pass (1,2) :> IComparable<int>
or even (1,2) :> _
, the compiler will be able to pass the value. You can wrap up the comparable, but you lose type information, the return value will be a IComparable
and no longer an int*int
.
let wrapComparable value =
{
new IComparable with
member this.CompareTo other =
match other with
| :? 'a as other -> compare value other
| _ -> raise <| InvalidOperationException()
}
Also, here you need to consider, that IComparable
is based on obj
so you probably need to consider the case, where your other
is of a different type.
In case, you only need IComparable<'a>
the code becomes simpler:
let wrapComparable value =
{
new IComparable<_> with
member this.CompareTo other = compare value other
}
As such, as a rule of thumb, you usually want to make a generic function with type constraints, rather than requesting an interface, as you would in C#. This is due to the fact, that F# does not do automatic upcasting.
A very detailed explanation about equality and comparisons can be found in http://lorgonblog.wordpress.com/2009/11/08/motivating-f-equality-and-comparison-constraints/ and http://blogs.msdn.com/b/dsyme/archive/2009/11/08/equality-and-comparison-constraints-in-f-1-9-7.aspx. Also the MSDN states, that
If you are only using tuples from F# and not exposing them to other languages, and if you are not targeting a version of the .NET Framework that preceded version 4, you can ignore this section.
Tuples are compiled into objects of one of several generic types, all named Tuple, that are overloaded on the arity, or number of type parameters. Tuple types appear in this form when you view them from another language, such as C# or Visual Basic, or when you are using a tool that is not aware of F# constructs. The Tuple types were introduced in .NET Framework 4. If you are targeting an earlier version of the .NET Framework, the compiler uses versions of System.Tuple from the 2.0 version of the F# Core Library. The types in this library are used only for applications that target the 2.0, 3.0, and 3.5 versions of the .NET Framework. Type forwarding is used to ensure binary compatibility between .NET Framework 2.0 and .NET Framework 4 F# components.
So it seems, that the fact, that Tuples, happen to be System.Tuple is really just an implementation detail at which point, the lack of IComparison
makes somewhat sense.
int*int
implements,IComparable
(you can check this with(1,2).GetType().GetInterfaces()
), but the compiler won't let you down cast. This may be some sort of weird F# restriction. I imagine that you could write a C# method to do the type cast though. – Moderato