f# - 为什么 F# 无法像 C# 那样推断类型

标签 f# type-inference

来自 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选项
  3. 所以我们的 map 用法看起来像 map : ('T -> 'U) -> 'DateTime option -> 'U option
  4. 然后编译器可以尝试将 DateTime 替换为 'T 以查看它是否正确,因此我们有 (DateTime -> 'U) -> 'DateTime 选项 -> 'U 选项
  5. 然后它可以通过查看 lambda 函数的主体来推断 'U 类型,因此 'U 变为 int
  6. 我们终于有了 (DateTime -> int) -> 'DateTime option -> 'int option

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

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

最佳答案

So why F# can't do this inference?

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

例如,如果您在 OCaml 中这样做但拼错了方法名称,那么您将在稍后的代码中收到一条巨大的错误消息,其中两个推断的类类型不匹配,您将不得不遍历它以找到差异,然后回头搜索您的代码以找到错误的实际位置。

IMO,Haskell 的类型类也有同样的实际问题。

关于f# - 为什么 F# 无法像 C# 那样推断类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7116754/

相关文章:

f# - 使用 cons 运算符理解模式匹配

C# 类型推断 ("var") 从 '??' 空合并运算符赋值

c# - .NET 中的泛型方法无法推断其返回类型。为什么?

haskell - 对方法的约束取决于范围内的实例?

c# - 在 F# 中返回具有名称的元组

f# - 命名空间或模块 "Runtime"未定义

c# - 使用 FSharp.Data 将 Web 应用程序部署到 Azure 网站

java - 为什么从 try-catch block 内部返回 CompletableFuture.completedFuture(...) (似乎)会导致此错误?

内联方法调用和单独方法调用之间的 C# 编译器类型推断区别

F#中的记录列表?