使用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推断的类型?
答案 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)