来自 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
的类型看起来很容易。
其背后的逻辑可以是这样的:
Option.map
的签名是map : ('T -> 'U) -> 'T option -> 'U option
- 最后一个参数的类型是
DateTime选项
- 所以我们的
map
用法看起来像map : ('T -> 'U) -> 'DateTime option -> 'U option
- 然后编译器可以尝试将
DateTime
替换为'T
以查看它是否正确,因此我们有(DateTime -> 'U) -> 'DateTime 选项 -> 'U 选项
- 然后它可以通过查看 lambda 函数的主体来推断
'U
类型,因此'U
变为int
- 我们终于有了
(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/