Haskell或OCaml中的非本地类型推断真的有用吗?

时间:2011-12-30 20:55:53

标签: haskell ocaml type-inference

首先,让我们假设本地类型推断是Scala和C#中的类型推断。这里解释了Scala本地类型推断:http://www.scala-lang.org/node/127

另外,让我们假设一个定义,如

     fact 0 = 1
     fact n = n * fact(n-1)

将被视为本地类型推断 - 也就是说,此处的类型推断是函数事实的本地。斯卡拉不允许这种类型的推论;仍然让我们把它当作本地人。

那么,问题是,是否有人有一个实际的例子,至少有两个相互递归的函数(或你自行决定的任何其他非局部性)从类型推断中获得一些好处?请不要发布愚蠢的例子,如:

    odd 0 = false
    odd n = even(n-1)

    even 0 = true
    even n = odd(n-1)

我怀疑在解析中会出现非愚蠢的实际例子。另外,请您解释程序员从非本地类型推断的使用中获得的好处吗?

更新:

我感谢任何地方类型推断不足的例子以及对完整类型推理的需要。

  1. 您的Haskell或OCaml示例可能90%正确,因为您只有90%理解术语“非本地类型推断”。但是,您必须了解Haskell(或OCaml)类型推断。

  2. 您的示例可能是用Scala或C#编写的。请指出compliler确实有足够的信息来推断类型,但由于语言规范或由于Scala或C#中类型推断的仅本地性质而无法推断类型。

  3. //再次,随意纠正我的英语。

2 个答案:

答案 0 :(得分:14)

我不完全确定哪些例子会对你有用,因为你提到了非局部性和相互递归,我不明白一个只展示非局部性的例子是否足够。

我会说Haskell中的一种常见技术是编写函数,其返回类型是参数类型中未提及的类约束类型变量。例如,像这样:

foo :: (Result a) => String -> a
foo = toResult . transform  -- transform :: String -> String

class Result a where
   toResult :: String -> a

-- Example implementation of Result class—with this, callers that 
-- expect foo to return an Integer will get the length of the result
-- of transform.
instance Result Integer where
   toResult = length

在这种情况下,对foo的任何调用的结果的具体类型由调用站点的类型推断确定。即,foo的任何调用的返回类型都是根据foo定义中没有的信息推断出来的。

这方面的一个实际例子是Haskell的正则表达式库。接口使用这种模式,因此,如上所示,有一个regexp匹配操作符,它在返回类型上是多态的,而不是有一堆返回不同类型的不同regexp匹配函数,因此调用者的类型推断控制返回的内容

因此,例如,如果在调用上下文中进行regexp匹配,其中推断的返回类型是整数,则返回匹配数。如果调用上下文需要布尔值,则在有任何匹配时获得True。如果调用上下文需要字符串列表,则会获得与正则表达式匹配的子字符串列表。定义了许多其他返回类型特定的行为 - 您可以通过为结果实现库的类型类实例来为任意返回类型定义自己的行为。

答案 1 :(得分:3)

FFS你想要什么?奇/偶的例子很好。只是用你的想象力。你真的希望我发布使用flx_lookup.ml的5502行相互递归的Ocaml函数,在我的Felix编译器中使用吗? [我会给出一个链接,但网络服务器此刻正在崩溃; [

let rec trclose state bsym_table rs sr fs = ...
and resolve_inherits state bsym_table rs sr x = ...
and inner_lookup_name_in_env state bsym_table env rs sr name 
 : entry_set_t = ...
and lookup_qn_in_env2'
  state
 (bsym_table:Flx_bsym_table.t)
 (env:env_t)
  (rs:recstop)
  (qn: qualified_name_t)
  : entry_set_t * typecode_t list
= ...
and lookup_qn_in_env'
  (state:lookup_state_t)
  bsym_table
  (env:env_t) rs
  (qn: qualified_name_t)
  : entry_kind_t * typecode_t list
= ...
and inner_bind_type state (bsym_table:Flx_bsym_table.t) env sr rs t = ...
... lots more ...

您会注意到某些参数的注释,这是因为类型推断在您要求获益的情况下找到类型错误时会很糟糕。注释足以限制推理,以帮助编译器在实际包含错误的行上出错。 Ocaml编译器不够智能,无法追踪在检测到冲突时它是如何推断类型的:这是推理的缺点(追踪推断的来源对于报告类型错误至关重要,但它似乎非常难以且它不是'要明确的是,即使信息可用,也可以以合适的方式报告。)

我个人不喜欢推理,特别是因为它有一些非常糟糕的属性:它在Ocaml中在多态变体存在时不能正常工作,难以扩展以支持重载,很难扩展以支持多态递归,以及不一定会终止。它使代码难以阅读,因为类型没有被命名,读者必须有效地复制推理过程。

好处是让代码看起来更干净。添加类型注释以查找错误后,在查找错误时,我经常删除注释。

如果将Ocaml函数定义与不提供推理的Felix中的函数定义进行比较,您将立即看到Felix代码更详细。然而,在重构时,推理真的很棒。它让它变得如此简单。