为什么F#的类型推断如此变幻无常?

时间:2010-07-01 23:29:55

标签: f# language-design type-inference

F#编译器似乎以(相当)严格的从上到下,从左到右的方式执行类型推断。这意味着您必须执行诸如将所有定义放在使用之前,文件编译顺序很重要的事情,并且您倾向于需要重新排列内容(通过|>或您拥有的内容)以避免使用显式类型注释。

使这个更灵活,并且计划用于未来版本的F#有多难?显然它可以完成,因为Haskell(例如)没有这种限制同样强大的推理。 F#的设计或意识形态是否有任何本质上不同的原因?

5 个答案:

答案 0 :(得分:54)

  

引起这种情况的F#的设计或意识形态是否有任何本质上的不同?

是。 F#使用名义而不是结构类型,因为它更简单,因此对于凡人来说更容易使用。

考虑这个F#示例:

let lengths (xss: _ [] []) = Array.map (fun xs -> xs.Length) xss

let lengths (xss: _ [] []) = xss |> Array.map (fun xs -> xs.Length)

前者无法编译,因为匿名函数中的xs类型无法推断,因为F#无法表达类型“某个类Length成员”。

相比之下,OCaml可以表达直接的等价物:

let lengths xss = Array.map (fun xs -> xs#length) xss

因为OCaml可以表达该类型(它写成<length: 'a ..>)。注意,这需要比F#或Haskell当前具有的更强大的类型推断,例如, OCaml可以推断和类型。

但是,已知此功能是可用性问题。例如,如果你搞砸了代码中的其他地方,那么编译器还没有推断xs的类型应该是一个数组,所以它可以提供的任何错误消息只能提供“某些类型的信息”。长度成员“而不是”数组“。只有稍微复杂的代码,这很快就会失控,因为你有大量的类型,有许多结构推断的成员不完全统一,导致难以理解(类似C ++ / STL)的错误消息。

答案 1 :(得分:51)

关于“Haskell同样强大的推理”,我不认为Haskell必须处理

  • OO风格的动态子类型(类型类可以做一些类似的东西,但类型类更容易输入/推断)
  • 方法重载(类型类可以做一些类似的东西,但类型类更容易输入/推断)

也就是说,我认为F#必须处理Haskell没有的一些难题。 (几乎可以肯定,Haskell必须处理F#没有的一些难题。)

正如其他答案所提到的,大多数主要的.NET语言都将Visual Studio工具作为主要的语言设计影响(参见例如LINQ如何“从...选择”而不是SQL-y“选择”。 ..来自“,从程序前缀获取intellisense的动机”。 Intellisense,错误曲线和错误信息可理解性都是F#设计的工具因素。

很可能做得更好,推断更多(不牺牲其他经验),但我不认为这是我们未来版本语言的重中之重。 (Haskellers可能会看到F#类型推断有点弱,但它们可能超过C#,他们认为F#类型推断非常强大。:))

也许很难以非破坏的方式扩展类型推断;可以在将来的版本中将非法程序更改为合法程序,但是您必须非常小心地确保以前合法的程序不会根据新的推理规则更改语义,并且可能会出现名称解析(每种语言都可怕的噩梦)以令人惊讶的方式与类型推断相互作用。

答案 2 :(得分:19)

我认为F#使用的算法有一个好处,就是很容易(至少粗略地)解释它是如何工作的,所以一旦理解了它,你就可以对结果有一些期望。

算法总会有一些限制。目前,很容易理解它们。对于更复杂的算法,这可能很困难。例如,我认为您可能会遇到这样的情况,即您认为算法应该能够推断出某些东西 - 但如果它足以涵盖整个案例,那么它将是不可判定的(例如,可以永远保持循环)。

另一个想法是从顶部到底部检查代码对应于我们如何读取代码(至少有时候)。所以,也许我们倾向于以支持类型推理的方式编写代码,这也使得代码对人们更具可读性......

答案 3 :(得分:17)

  

F#使用一次传递编译   你只能引用类型或   已定义的功能   你要么在文件的前面   目前在或出现在一个文件中   在前面指定   编译顺序。

     

我最近问Don Syme有关制作的问题   多个源传递来改进   类型推断过程。他的回答是   “是的,可以做多次传球   类型推断。还有   产生一个的单程变化   有限的约束集。

     

然而,这些方法倾向于给予   错误的错误消息和差   intellisense导致视觉   编辑“。

http://www.markhneedham.com/blog/2009/05/02/f-stuff-i-get-confused-about/#comment-16153

答案 4 :(得分:13)

简短的回答是F#基于SML和OCaml的传统,而Haskell来自米兰达,Gofer等略有不同的世界。历史传统的差异是微妙的,但却是无处不在的。这种区别在其他现代语言中也是平行的,例如ML-like Coq与Haskell-like Agda具有相同的排序限制,而不是。

这种差异与懒惰与严格评估有关。宇宙的哈斯克尔方面相信懒惰,一旦你已经相信懒惰,那么在类型推理之类的事情上添加懒惰的想法是不费脑子的。在宇宙的ML方面,只要需要懒惰或相互递归,就必须通过使用 with rec 等等。我更喜欢Haskell方法,因为它导致更少的样板代码,但是有很多人认为最好使这些事情变得明确。