如果我定义
fun id x = x
然后自然id
的类型为'a -> 'a
当然,id 0
评估为0
,这非常有意义。
由于这很有意义,我应该能够通过函数封装它:
fun applyToZero (f: 'a -> 'a) = f 0
希望applyToZero
将('a -> 'a) -> int
类型和applyToZero id
评估为0
但是当我尝试如上定义applyToZero
时,SML / NJ会给出一条奇怪的错误消息:
unexpected exception (bug?) in SML/NJ: Match [nonexhaustive match failure]
raised at: ../compiler/Elaborator/types/unify.sml:84.37
这几乎看起来像编译器本身的一个错误。很奇怪,但可能。
但是PolyML也不喜欢它(虽然它的错误信息不那么奇怪):
> fun applyToZero (f: 'a -> 'a) = f 0;
poly: : error: Type error in function application.
Function: f : 'a -> 'a
Argument: 0 : int
Reason: Can't unify int to 'a (Cannot unify with explicit type variable)
Found near f 0
以下工作正常:
fun ignoreF (f: 'a -> 'a) = 1
使用推断类型('a -> 'a) -> int
。这表明创建这种类型的高阶函数是不可能的。
为什么SML不接受我对applyToZero
的定义?是否有任何解决方法可以让我定义它以使其类型为('a -> 'a) -> int
?
动机:在我试图解决this question中的谜题时,我能够定义类型tofun
的函数int -> 'a -> 'a
和另一个具有所需属性的函数fromfun
对于所有整数fromfun (tofun n) = n
n
。但是,我的工作fromfun
的推断类型是('int -> 'int) -> 'int)
。我尝试添加类型注释以便SML将其作为('a -> 'a) -> int
接受的所有尝试都失败了。我不想显示我对fromfun
的定义,因为提出该问题的人可能仍在处理该问题,但applyToZero
的定义会触发完全相同的错误消息。
答案 0 :(得分:4)
它不能像普通的Hindley-Milner那样完成,就像SML一样,因为它不支持所谓的排名较高的或一流的多态性。类型注释'a -> 'a
和类型('a -> 'a) -> int
并不代表您的想法。
如果我们明确地为类型变量创建绑定器,那就更清楚了。
fun ignoreF (f: 'a -> 'a) = 1
实际上意味着
fun 'a ignoreF (f: 'a -> 'a) = 1
也就是说,'a
是整个函数ignoreF
的参数,而不是参数f
。因此,函数的类型是
ignoreF : ∀ 'a. (('a -> 'a) -> int)
在这里,我将类型'a
中的绑定器显式化为通用量词。这就是你如何在类型理论中编写这样的类型,而SML将所有量词隐含在其语法中。现在你认为这个类型会被写成
ignoreF : (∀ 'a. ('a -> 'a)) -> int
注意区别:在第一个版本中,ignoreF
的调用者可以选择实例化'a
的方式,因此它可以是任何东西,并且该函数不能假设其int
(这就是applyToZero
不进行类型检查的原因。在第二种类型中,参数的调用者可以选择,即ignoreF
。
但Hindley-Milner并不支持这种类型。它只支持所谓的 prenex多态性(或秩0多态),其中所有∀都在最外层 - 这就是为什么它可以保持隐含,因为在这个限制下没有歧义。排名较高的多态性的问题在于它的类型推断是不可判定的。
所以你的applyToZero
不能拥有你想要的SML类型。实现类似功能的唯一方法是使用模块系统及其仿函数:
functor ApplyToZero (val f : 'a -> 'a) = struct val it = f 0 end
顺便说一句,您从SML / NJ引用的错误消息不可能由您显示的代码引起。你必须做其他事情。
答案 1 :(得分:1)
如果我们在fun applyToZero f = f 0
上使用Hindley-Milner类型推理算法,由于术语f : int -> 'a
,我们将获得f 0
。
显然,f
是一个函数f : 'b -> 'a
。我们将此函数应用于0
,因此'b = int
。因此,显式类型注释f : 'a -> 'a
会产生您观察到的错误。
顺便说一句,SML / NJ v110.80在我的机器上正常工作并打印以下错误信息:
stdIn:2.39-2.42 Error: operator and operand don't agree [overload - user bound tyvar]
operator domain: 'a
operand: [int ty]
in expression:
f 0