是什么原因导致此 Standard-ML 类型错误?

发布于 2024-08-13 04:54:31 字数 2097 浏览 4 评论 0原文

我试图为这个非常简单的 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:list'a:list list 统一起来吗? >,使用 'b 替换 ---> '列表

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

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

I was trying to make a tail-recursive version of this very simple SML function:

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

During the course of this, I was using type annotations on the paramaters. The following code shows this, and causes a type error (given below), whereas if I simply remove the type annotations, SML accepts it with no problem, giving the whole function the same signature as the simpler function above.

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;

Error:

$ 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

There are two errors given. The latter seems to be less important here, a mismatch between the two clauses of suffixes_helper. The first is the one I don't understand. I annotate to state that the first param is of type 'a:list and that the second param is of type 'b:list. Should not the Hindley-Milner type inference algorithm, which is built of top of general unification as I understand it, be able to unify 'b:list with 'a:list list, using a substitution of 'b ---> 'a list?

EDIT: An answer suggests it may have something to do with the type inference algorithm disallowing inferred types which in some sense are more strict than the ones given by the type annotations. I would guess that such a rule would only apply to annotations on parameters and on a function as a whole. I have no idea if this is correct. In any case, I tried moving the type annotations over to the function body, and I get the same sort of error:

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

The error is now:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

最近可好 2024-08-20 04:54:31

这有效:

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 不能为 true (All a') and (All b') 同时进行。

如果没有显式类型注释,类型推断可以做正确的事情,即统一类型。实际上,SML 的类型系统非常简单(据我所知),它永远不会是不可判定的,因此显式类型注释永远都不是必要的。为什么要把它们放在这里?

This works:

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

As Joh and newacct say, 'b list is too loose. When you give the explicit type annotation

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

it is implicitly quantified as

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

and obviously 'b = 'a list cannot be true (All a') and (All b') simultaneously.

Without the explicit type annotation, type inference can do the right thing, which is to unify the types. And really, SML's type system is simple enough that (as far as I am aware) it is never undecidable, so explicit type annotations should never be necessary. Why do you want to put them in here?

晚风撩人 2024-08-20 04:54:31

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

When you use type variables like 'a and 'b, that means that 'a and 'b can be set to anything, independently. So for example it should work if I decided that 'b was int and 'a was float; but obviously that is not valid in this case because it turns out that 'b must be 'a list.

淡墨 2024-08-20 04:54:31

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

I am not sure about SML, but F#, another functional language, gives a warning in this kind of situation. Giving an error may be a bit harsh, but it makes sense: if the programmer introduces an extra type variable 'b, and if 'b must be of type 'a list, the function might not be as generic as the programmer intended, which is worth reporting.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文