Ada*_*ein 7 comparison f# icomparable
简而言之,我的问题是:
关于在需要IComparable的C#容器中存储元组(或任何具有'比较'约束的类型),我该怎么办?
这有效:
> 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)
Run Code Online (Sandbox Code Playgroud)
我原以为这会起作用:
> 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>'
Run Code Online (Sandbox Code Playgroud)
这也是:
> 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'
Run Code Online (Sandbox Code Playgroud)
编辑
我遵循F#不进行隐式向上转换的论点.但是,即使是明确的:
> (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'
Run Code Online (Sandbox Code Playgroud)
我认为这是有道理的,因为F#元组的可比性在结构上是在F#类型系统中推断的,也许.NET没有额外的信息.
以下评论似乎有一种解决方法是调用
Tuple<_,_> (1,2) ;;
Run Code Online (Sandbox Code Playgroud)
甚至
box (1, 2) :?> IComparable ;;
Run Code Online (Sandbox Code Playgroud)
F#不像C#那样进行隐式向上转换.如果你请求一个IComparable,那么你正在请求一个IComparable而不是 可以向上转换的东西IComparable
您真正想要的是请求实现的类型IComparable,但您仍在使用特定类型.
这就是为什么let x (y : 'a when 'a : comparison),看到它y是类型的'a,而'a可以静态上传comparison(如果你想访问一个成员comparison,你将不得不向上comparison使用:>)
另一方面let x (y : IComparable<int>) = y请求非常明确a IComparable<int>.但你是路过.你可以包装可比较的,但 你丢失类型信息,返回值将是一个(1,2),一个值,即可以上溯造型来IComparable.因此,如果您通过(1,2) :> IComparable<int>或甚至(1,2) :> _,编译器将能够传递该值IComparable而不再是一个int*int.
let wrapComparable value =
{
new IComparable with
member this.CompareTo other =
match other with
| :? 'a as other -> compare value other
| _ -> raise <| InvalidOperationException()
}
Run Code Online (Sandbox Code Playgroud)
此外,在这里你需要考虑,这IComparable是基于obj你可能需要考虑的情况,你的other是不同的类型.
如果您只需要IComparable<'a>代码变得更简单:
let wrapComparable value =
{
new IComparable<_> with
member this.CompareTo other = compare value other
}
Run Code Online (Sandbox Code Playgroud)
因此,根据经验,您通常希望使用类型约束创建泛型函数,而不是像在C#中那样请求接口.这是因为F#不进行自动向上转换.
关于平等和比较的非常详细的解释可以在http://lorgonblog.wordpress.com/2009/11/08/motivating-f-equality-and-comparison-constraints/和http://blogs.msdn.com找到./b/dsyme/archive/2009/11/08/equality-and-comparison-constraints-in-f-1-9-7.aspx.MSDN还说明了这一点
如果您只使用F#中的元组并且不将它们暴露给其他语言,并且如果您没有针对版本4之前的.NET Framework版本,则可以忽略此部分.
元组被编译成几个泛型类型之一的对象,所有类型都被命名为Tuple,它们在arity上重载,或者类型参数的数量.当您使用其他语言(如C#或Visual Basic)查看元组类型时,或使用不了解F#构造的工具时,元组类型将以此形式显示.在.NET Framework 4中引入了Tuple类型.如果您的目标是.NET Framework的早期版本,则编译器将使用来自F#Core Library 2.0版本的System.Tuple版本.此库中的类型仅用于面向.NET Framework的2.0,3.0和3.5版本的应用程序.类型转发用于确保.NET Framework 2.0和.NET Framework 4 F#组件之间的二进制兼容性.
所以看来,事实上,Tuples恰好是System.Tuple实际上只是一个实现细节,在这一点上,缺乏IComparison有点意义.
肯定发生了一些奇怪的事情。FWIW,如果您显式构造一个System.Tuple<_, _>,它就可以工作,因此这可能是一种解决方法:
let x (y : IComparable) = y
let t = (2, 3)
x (Tuple<_,_> t)
Run Code Online (Sandbox Code Playgroud)