【问题标题】:Is non-local type inference in Haskell or OCaml really useful? [closed]Haskell 或 OCaml 中的非本地类型推断真的有用吗? [关闭]
【发布时间】:2011-12-30 20:55:52
【问题描述】:

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

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

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

将算作本地类型推断——也就是说,这里的类型推断是函数事实的本地。 Scala 不允许这样的类型推断;还是让我们把它算作本地吧。

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

    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# 编写。请指出,编译器确实有足够的信息来推断类型,但由于语言规范或 Scala 或 C# 中类型推断的仅限本地性质,无法推断类型。

// 再次,请随时纠正我的英语。

【问题讨论】:

  • 我经常对 C# 中类型推断的 local-only 特性感到恼火。例如,如果我想将一个 LINQ 查询重构为一个函数,我经常不得不做一堆附带的编码(例如提出一个一次性的类型来表示查询投影)来实现这一点。跨度>
  • 这似乎是一个奇怪的问题。正如 Daniel Pratt 所说,其好处与任何其他类型推理相同,并且只有 局部推理是一种虚假的限制,会妨碍重构。为什么你需要一个具体的例子来说明“为什么不削弱一个有用的功能”?
  • @user1123502:是的,一些语言特性使得完整的类型推断变得不可能,并且在这些特性的存在下削弱类型推断可能是明智的。这并不能解释为什么您希望有理由对有用的功能进行人为限制。这就像在问“在函数名中允许字母 Q 真的有用吗?”。
  • 此外,推理与注释的争论是无关紧要的。非本地类型推断不会禁止您使用注解,它只是让更多注解成为可选项。
  • 首先,我认为您应该明确说明本地类型推断的含义,否则很难告诉您什么时候可以方便地拥有更多。

标签: haskell ocaml type-inference


【解决方案1】:

我不完全确定哪些示例适合您,因为您提到了非局部性和相互递归,我不明白仅展示非局部性的示例是否足够。

我会说,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 的正则表达式库就是一个实际的例子。该接口使用这种模式,因此,不是有一堆不同的正则表达式匹配函数返回不同的类型,而是有一个正则表达式匹配运算符,它在返回类型上是多态的,如上所示,因此调用者的类型推断控制返回的内容.

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

【讨论】:

    【解决方案2】:

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

    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 代码更加冗长。然而,在重构时推理真的很出色。它让一切变得如此简单。

    【讨论】:

    • 我在您的个人资料中没有看到网站;你的 felix 编译器的 url 是什么?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多