【问题标题】:Why are the inferred F# types on these two definitions different in regards to IComparable and comparison?为什么在 IComparable 和比较方面这两个定义上推断的 F# 类型不同?
【发布时间】:2018-10-06 04:45:26
【问题描述】:

在 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 推断的类型?

【问题讨论】:

    标签: f#


    【解决方案1】:

    在您的 B.empty 案例中,您有一个 let-bound 值,您在右侧引入类型参数 '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)
      

    【讨论】:

    • let-bound 值被分配特定类型而不是泛型类型的原因是 值限制 弹出的另一个示例:函数可以是泛型但值可以't,所以 let-bound 值被分配了一个特定的类型。有关更多详细信息,请参阅blogs.msdn.microsoft.com/mulambda/2010/05/01/…,包括为什么let empty&lt;'k, 'v&gt; = ... 示例有效(总结:它看起来仍然像一个值,但它已成为一种函数,其中类型是参数)。
    • @rmunn:谢谢,这是一篇很好的博文,我已经编辑了答案以包含它。我觉得这与值限制有关,但是当我查看 FSI 中的代码时,我没有看到永远友好的错误消息。
    猜你喜欢
    • 1970-01-01
    • 2011-11-23
    • 2019-02-16
    • 1970-01-01
    • 1970-01-01
    • 2019-10-11
    • 1970-01-01
    • 2014-02-03
    • 1970-01-01
    相关资源
    最近更新 更多