我试图制作这个非常简单的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
给出了两个错误。后者似乎在这里不那么重要,后缀的两个子句之间的不匹配。第一个是我不明白的。我注释要声明第一个参数是'a:list
类型,第二个参数是'b:list
类型。根据我的理解,建立在一般统一之上的Hindley-Milner类型推理算法不应该使用'b:list
的替换'a:list list
与'b ---> 'a list
统一。 / p>
编辑:答案表明它可能与类型推断算法有关,不允许推断类型,在某种意义上,它们比类型注释给出的更严格。我猜这样的规则只适用于参数注释和整个函数。我不知道这是否正确。无论如何,我尝试将类型注释移动到函数体,我得到了同样的错误:
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
答案 0 :(得分:3)
这有效:
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的类型系统非常简单(据我所知)它永远不会是不可判定的,因此永远不需要显式类型注释。你为什么要把它们放在这里?
答案 1 :(得分:2)
当您使用'a
和'b
等类型变量时,这意味着'a
和'b
可以设置为任何,独立地。例如,如果我确定'b
是int
而'a
是float
,那么它应该有用;但显然在这种情况下这是无效的,因为事实证明'b
必须是'a list
。
答案 2 :(得分:1)
我不确定SML,但另一种功能语言F#在这种情况下会发出警告。给出一个错误可能有点苛刻,但它是有道理的:如果程序员引入了一个额外的类型变量'b,并且如果'b'必须是'list'类型,那么该函数可能不像程序员想要的那样通用,值得报道。