12

我一生都无法弄清楚为什么以下 SML 函数会在我的作业问题中引发警告:

fun my_func f ls  = 
  case ls of 
  [] => raise MyException
  | head :: rest => case f head of 
                    SOME v => v
                    | NONE => my_func f rest

fun f a = if isSome a then a else NONE;

每当我使用以下测试函数调用 my_func 时:

my_func f [NONE, NONE];
my_func f [];

我总是收到警告:

警告:由于值限制而未泛化的类型变量被实例化为虚拟类型(X1,X2,...)

每当我传入包含至少一个 SOME 值的选项列表时,都不会抛出此警告。我知道这一定与我在函数柯里化中使用多态性这一事实有关,但我完全不知道如何摆脱这些警告。

如果您有任何想法,请提供帮助 - 在此先感谢您!

4

1 回答 1

23

警告中引用的值限制是在 SML 中更难理解的事情之一,但是我会尽力解释为什么会在这种情况下出现,并尝试向您指出一些资源以了解更多信息。

如您所知,SML 将使用类型推断来推断程序中的大多数类型。在这个程序中,类型my_func将被推断为('a -> 'b option) -> 'a list -> 'b. 正如您所指出的,它是一种多态类型。当你my_func这样打电话时

myfunc f [NONE, SOME 1, NONE];

...类型变量'aand'b被实例化为int optionand int

但是,当您在没有上述值的情况下调用它SOME 1

myfunc f [NONE, NONE];

你认为类型变量应该被实例化成什么?类型应该是多态的——类似于所有类型的't option和。但是,有一个限制可以防止像这样的值采用多态类型。't't

SML 将一些表达式定义为非扩展值,并且只有这些值可以采用多态类型。他们是:

  • 文字(常量)
  • 变量
  • 函数表达式
  • 构造函数(除了ref)应用于非扩展值
  • 带有类型注释的非扩展值
  • 每个字段都是非扩展值的元组
  • 每个字段都是非扩展值的记录
  • 列出每个字段是非扩展值的位置

所有其他表达式,特别是函数调用(这是调用的my_func内容)不能是多态的。也不能参考。您可能会好奇地看到以下内容不会引发警告:

 fn () => my_func f [NONE, NONE];

相反,推断的类型是unit -> 'a. 但是,如果您要调用此函数,您将再次收到警告。

我对这种限制原因的理解有点薄弱,但我相信潜在的根本问题是可变引用。这是我从下面链接的 MLton 站点中获取的示例:

val r: 'a option ref = ref NONE
val r1: string option ref = r
val r2: int option ref = r
val () = r1 := SOME "foo"
val v: int = valOf (!r2)

由于值限制,此程序不会在 SML 下进行类型检查。如果不是值限制,这个程序在运行时会出现类型错误。

正如我所说,我的理解是不稳定的。但是,我希望我对您遇到的问题有所了解,尽管我相信在您的情况下,您可以放心地忽略警告。如果您决定要深入挖掘,这里有一些参考资料:

http://users.cis.fiu.edu/~smithg/cop4555/valrestr.html

http://mlton.org/ValueRestriction

(顺便说一句,MLton 网站是纯金的。这里隐藏了很多东西,所以如果你想了解一些关于 SML 的奇怪之处,我强烈建议在这里搜索,因为你可能会出现比你最初想要的更多的东西)

因为看起来你实际上是在使用 SML/NJ,所以这是一个非常方便的指南,可以帮助你在编译时给出错误消息和警告:

http://flint.cs.yale.edu/cs421/smlnj/doc/errors.html

于 2013-02-03T04:43:54.097 回答