【问题标题】:What causes this Standard-ML type error?是什么导致了这个 Standard-ML 类型错误?
【发布时间】:2009-12-05 22:34:05
【问题描述】:

我试图制作这个非常简单的 SML 函数的尾递归版本:

fun suffixes [] = [[]]
  | suffixes (x::xs) = (x::xs) :: suffixes xs;

在此过程中,我在参数上使用了类型注释。下面的代码显示了这一点,并导致类型错误(如下所示),而如果我只是删除类型注释,SML 会毫无问题地接受它,为整个函数提供与上面更简单的函数相同的签名。

fun suffixes_tail xs =
    let
        fun suffixes_helper [] acc = []::acc
          | suffixes_helper (x::xs:'a list) (acc:'b list) =
                suffixes_helper xs ((x::xs)::acc)
    in
        suffixes_helper xs []
    end;

错误:

$ sml typeerror.sml 
Standard ML of New Jersey v110.71 [built: Thu Sep 17 16:48:42 2009]
[opening typeerror.sml]
val suffixes = fn : 'a list -> 'a list list
typeerror.sml:17.81-17.93 Error: operator and operand don't agree [UBOUND match]
  operator domain: 'a list * 'a list list
  operand:         'a list * 'b list
  in expression:
    (x :: xs) :: acc
typeerror.sml:16.13-17.94 Error: types of rules don't agree [UBOUND match]
  earlier rule(s): 'a list * 'Z list list -> 'Z list list
  this rule: 'a list * 'b list -> 'Y
  in rule:
    (x :: xs : 'a list,acc : 'b list) =>
      (suffixes_helper xs) ((x :: xs) :: acc)
/usr/local/smlnj-110.71/bin/sml: Fatal error -- Uncaught exception Error with 0
 raised at ../compiler/TopLevel/interact/evalloop.sml:66.19-66.27

给出了两个错误。后者在这里似乎不太重要,suffixes_helper 的两个子句不匹配。第一个是我不明白的。我注释说第一个参数是'a:list 类型,第二个参数是'b:list 类型。 Hindley-Milner 类型推理算法(根据我的理解建立在一般统一之上)是否应该能够使用 'b ---> 'a list 的替换将 'b:list'a:list list 统一起来?

编辑:答案表明它可能与不允许推断类型的类型推断算法有关,从某种意义上说,推断类型比类型注释给出的类型更严格。我猜想这样的规则只适用于参数和整个函数的注释。我不知道这是否正确。无论如何,我尝试将类型注释移到函数体,我得到了同样的错误:

fun suffixes_helper [] acc = []::acc
    | suffixes_helper (x::xs) acc =
          suffixes_helper (xs:'a list) (((x::xs)::acc):'b list);

现在的错误是:

typeerror.sml:5.67-5.89 Error: expression doesn't match constraint [UBOUND match]
  expression: 'a list list
  constraint: 'b list
  in expression:
    (x :: xs) :: acc: 'b list

【问题讨论】:

    标签: functional-programming type-inference sml hindley-milner


    【解决方案1】:

    这行得通:

    fun suffixes_tail xs =
        let
            fun suffixes_helper [] acc = []::acc
              | suffixes_helper (x::xs:'a list) (acc:'a list list) =
                    suffixes_helper xs ((x::xs)::acc)
        in
            suffixes_helper xs []
        end
    

    正如 Joh 和 newacct 所说,'b list 太松散了。当你给出显式类型注释时

    fun suffixes_helper (_ : 'a list) (_ : 'b list) = ...
    

    它被隐式量化为

    fun suffixes_helper (_ : (All 'a).'a list) (_ : (All 'b).'b list) = ...
    

    显然'b = 'a list 不可能同时为真(All a') (All b')

    没有显式类型注解,类型推断可以做正确的事,那就是统一类型。确实,SML 的类型系统非常简单,以至于(据我所知)它永远是不可判定的,因此永远不需要显式类型注释。为什么要把它们放在这里?

    【讨论】:

    • 谢谢,三个答案基本上都说明了相同的原因,我接受这就是解释。我最初的理解(因此对 SML 的行为感到惊讶)是类型注释在与任何推断类型相同的基础上添加了类型信息,因此它们可以统一“向上”(更一般)和“向下”(更具体)。
    • 并回答你的问题,为什么我首先需要类型注释:虽然我也理解 SML 永远不需要类型注释,但它们可能会增加可读性,并且(在我的情况下更合适)它们可能有助于缩小编译错误的范围。
    【解决方案2】:

    当您使用'a'b 之类的类型变量时,这意味着'a'b 可以设置为任何东西独立。因此,例如,如果我决定 'bint 并且 'afloat ,它应该可以工作;但显然这在这种情况下是无效的,因为事实证明'b 必须是'a list

    【讨论】:

      【解决方案3】:

      我不确定 SML,但另一种函数式语言 F# 在这种情况下会发出警告。给出错误可能有点苛刻,但这是有道理的:如果程序员引入了一个额外的类型变量 'b,并且如果 'b 必须是 'a 列表类型,那么函数可能不像程序员想要的那样通用,这值得报道。

      【讨论】:

      • 谢谢,这是我没有考虑过的:当在参数中给出类型注释时,可以实现类型推断以禁止比给定参数更严格的推断类型。但是,如果我将类型注释放在函数体中,我会得到同样的错误。我将编辑我的问题以显示这一点。
      • 你仍然在注释一个没有推断类型严格的类型
      猜你喜欢
      • 1970-01-01
      • 2011-08-13
      • 2012-10-06
      • 2012-09-21
      • 2010-10-12
      • 2013-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多