为什么在这两个定义上推断出的F#类型在IComparable和比较方面有所不同?

时间:2018-10-06 04:45:26

标签: f#

使用F#并无法理解以下两个项目之间的区别:

type A<'k, 'v when 'v : comparison and 'k : comparison> = 
    { Keys: Map<'v, 'k> } with 
        member this.length = Map.count this.Keys
module A =
    let empty = { Keys = Map.empty }

type B<'k, 'v when 'v : comparison and 'k : comparison>(keys: Map<'v, 'k>) =
    member __.length = Map.count keys
module B =
    let empty = new B<'k, 'v>(Map.empty)

如果我看一下A.empty和B.empty的推断类型,则会得到以下信息:

val empty : A<'a,'b> (requires comparison and comparison)
val empty : B<System.IComparable,System.IComparable>

为什么它们不同? 'k和'v都被分配给F#Map,并且以相同的方式使用。唯一的区别是B上的“ new”关键字,但是为什么这会更改为Map推断的类型?

1 个答案:

答案 0 :(得分:1)

在您的B.empty情况下,您有一个让界值,在右边引入类型参数'k'v而不在左边引用它们。这样,类型推断机制就无法自动概括empty,而是为其分配了特定类型。

如果尝试在不同的实例中使用它,则会看到第二个失败:

let t1 : B<int, string> = B.empty 
let t2 : B<string, int> = B.empty // <- getting a type mismatch here.

这是由于类型推断机制的一种特性,即值限制。 This blog post对其进行了很好的深入描述,包括您正在查看的情况,为通用容器类型实现了empty

简而言之-B.empty在这里不能一概而论,因为右侧的表达式不会产生易于识别为不可变的值,因此编译器会在警告和警告方面犯错。决定不概括empty的类型。

另一方面,右侧的A.empty有一个表达式,用于创建不可变记录的实例,这是满足自动概括要求的罕见情况之一。

一些避免它的方法:

  • 使empty具有功能:

    let empty () = new B<'k, 'v>(Map.empty)
    
  • 在其上放置显式类型参数:

    let empty<'k, 'v when 'v : comparison and 'k : comparison> = new B<'k, 'v>(Map.empty)