【问题标题】:Why F# fails to infer types as C# would do为什么 F# 无法像 C# 那样推断类型
【发布时间】:2011-08-19 03:50:58
【问题描述】:

the book by Tomas Petricek 开始,以下代码不起作用,因为编译器无法推断dt 参数的类型:

> Option.map (fun dt -> dt.Year) (Some(DateTime.Now));;
error FS0072: Lookup on object of indeterminate type.

如果我们明确指定类型,一切正常:

> Option.map (fun (dt:DateTime) -> dt.Year) (Some(DateTime.Now));;
val it : int option = Some(2008)

或者我们可以使用流水线操作符来“帮助”编译器推断类型:

> Some(DateTime.Now) |> Option.map (fun dt -> dt.Year);;
val it : int option = Some(2008)

问题是为什么 F# 编译器不能推断出dt 参数的类型?在这种特殊情况下,推断dt 的类型看起来很容易。

其背后的逻辑可以是:

  1. Option.map 的签名是map : ('T -> 'U) -> 'T option -> 'U option
  2. 最后一个参数的类型是DateTime option
  3. 所以我们的map 用法看起来像map : ('T -> 'U) -> 'DateTime option -> 'U option
  4. 然后编译器可以尝试将DateTime 替换为'T 以查看它是否正确,所以我们有(DateTime -> 'U) -> 'DateTime option -> 'U option
  5. 然后它可以通过查看 lambda 函数的主体来推断 'U 类型,因此 'U 变为 int
  6. 我们终于有了(DateTime -> int) -> 'DateTime option -> 'int option

那么为什么 F# 不能进行这种推断呢? Tomas 在他的书中提到 F# 通过从第一个参数到最后一个参数来推断类型,这就是为什么参数的顺序很重要。这就是为什么 F# 无法推断第一个示例中的类型的原因。但是为什么 F# 不能像 C# 那样表现,即尝试从已知的内容开始逐步推断类型?

在大多数情况下,当谈到类型推断时,F# 的功能要强大得多……这不是我有点困惑的原因。

【问题讨论】:

  • F# 的类型推断是严格从上到下/从左到右的。见stackoverflow.com/questions/3162387/…
  • 流水线版本还是比较惯用的
  • @dahlbyk 管道是惯用的,正是因为它是解决此问题的一种方法。

标签: f# type-inference


【解决方案1】:

那么为什么 F# 不能进行这种推理呢?

F# 可以像 OCaml 那样做到这一点。这种更复杂的推理的缺点是错误消息的混淆。 OCaml 告诉我们,结果产生了如此难以理解的错误,以至于在实践中,您总是求助于注释类型以防止编译器被引导到类型推理花园路径。因此,几乎没有动力在 F# 中实现这一点,因为 OCaml 已经表明它不是很实用。

例如,如果您在 OCaml 中执行此操作但拼写错误的方法名称,那么您将在稍后的代码中收到一条巨大的错误消息,其中两个推断的类类型不匹配,您将不得不通过它来寻找差异,然后搜索您的代码以找到错误的实际位置。

IMO,Haskell 的类型类也存在等效的实际问题。

【讨论】:

    【解决方案2】:

    F# 可以做 C# 的类型推断可以做的所有事情……甚至更多。 AFAIK,C# 类型推断的范围是根据赋值的右侧自动键入变量。

    var x = new Dictionary<string, int>();
    

    等效的 F# 将是:

    let x = Dictionary()
    

    let x = Dictionary<_,_>()
    

    let x = Dictionary<string,_>()
    

    let x = Dictionary<string,int>()
    

    您可以根据需要提供尽可能多或尽可能少的类型信息,但您几乎不会声明x 的类型。因此,即使在这种简单的情况下,F# 的类型推断显然要强大得多。 Hindley-Milner type inference 键入整个程序,统一所有涉及的表达式。据我所知,C# 类型推断仅限于单个表达式,即赋值。

    【讨论】:

    • 我并不是说 F# 类型推断更弱,我坚信它要强大得多,我在我的问题中提到了它。但我的问题是,为什么 F# 不能从 Tomas 的书中推断出特定示例中的类型,因为它有能力做到这一点。实际上,您提供的链接回答了我的问题,谢谢!看起来 F# 语言设计者已决定不让类型推断过于复杂以避免可能的混淆。
    猜你喜欢
    • 1970-01-01
    • 2019-10-11
    • 1970-01-01
    • 2011-03-10
    • 2022-08-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多