【发布时间】: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 的类型看起来很容易。
其背后的逻辑可以是:
-
Option.map的签名是map : ('T -> 'U) -> 'T option -> 'U option - 最后一个参数的类型是
DateTime option - 所以我们的
map用法看起来像map : ('T -> 'U) -> 'DateTime option -> 'U option - 然后编译器可以尝试将
DateTime替换为'T以查看它是否正确,所以我们有(DateTime -> 'U) -> 'DateTime option -> 'U option - 然后它可以通过查看 lambda 函数的主体来推断
'U类型,因此'U变为int - 我们终于有了
(DateTime -> int) -> 'DateTime option -> 'int option
那么为什么 F# 不能进行这种推断呢? Tomas 在他的书中提到 F# 通过从第一个参数到最后一个参数来推断类型,这就是为什么参数的顺序很重要。这就是为什么 F# 无法推断第一个示例中的类型的原因。但是为什么 F# 不能像 C# 那样表现,即尝试从已知的内容开始逐步推断类型?
在大多数情况下,当谈到类型推断时,F# 的功能要强大得多……这不是我有点困惑的原因。
【问题讨论】:
-
F# 的类型推断是严格从上到下/从左到右的。见stackoverflow.com/questions/3162387/…
-
流水线版本还是比较惯用的
-
@dahlbyk 管道是惯用的,正是因为它是解决此问题的一种方法。
标签: f# type-inference