这是由Resolving the type of `f = f (<*>) pure`提出的,它讨论了一个更复杂的例子,但这个也有效。
以下定义编译没有问题:
w :: Integral a => a
w = fromInteger w
......当然,它在运行时并不起作用,但这不是问题所在。关键是w
本身的定义使用w :: Integer
的专门版本。显然,这是一个合适的实例,因此是一个类型。
但是,如果我们删除签名,那么GHC不会推断出上述类型,而只会推断具体的类型:
w' = fromInteger w'
GHCi> :t w
w :: Integral a => a
GHCi> :t w'
w' :: Integer
好吧,当我看到这个时,我很确定这是工作中的单态限制。众所周知,例如
i = 3
GHCi> :t i
i :: Integer
虽然i :: Num p => p
是完全可能的。事实上,如果i :: Num p => p
处于活动状态,即如果禁用单态限制,则推断-XNoMonomorphismRestriction
。
但是,在w'
的情况下,即使禁用单态限制,也只推断出类型Integer
。
要指出这与默认有关:
fromFloat :: RealFrac a => Float -> a
q :: RealFrac a => a
q = fromFloat q
q' = fromFloat q'
GHCi> :t q
q :: RealFrac a => a
GHCi> :t q'
q' :: Float
为什么不推断出多态类型?
多态递归(函数调用自身的类型与调用它的类型不同)总是需要类型签名。完整的解释是在Haskell 2010报告的Section 4.4.1中:
如果定义了变量
f
而未提供相应的类型签名声明,那么在其自己的声明组(请参阅f
)之外的每个Section 4.5的使用都被视为具有相应的推断或主体类型。但是,为了确保仍然可以进行类型推断,f
在其声明组中的定义出现和所有使用必须具有相同的单态类型(通过泛化获得主要类型,如Section 4.5.2中所述)。
后面的同一部分介绍了类型签名支持的多态递归示例。
我的理解是,在存在多态递归的情况下,无辅助类型推断通常是不可判定的,所以Haskell甚至都没有尝试过。
在这种情况下,类型检查器以
w :: a
其中a
是一个元变量。由于fromInteger
在其自己的声明中被称为w
作为参数(因此在其声明组中),因此类型检查器将a
与Integer
统一起来。没有变量可以概括。
由于同样的原因,对程序进行略微修改会产生不同的结果:
v = fromIntegral v
根据你的原始推理,Haskell推断v :: forall a. Num a => a
,在RHS上默认v
键入Integer
:
v :: forall a. Num a => a
v = fromIntegral (v :: Integer)
但相反,它始于v :: a
。由于v
被传递给fromIntegral
,它强加Integral a
。最后,它概括了a
。最终,程序结果是
v :: forall a. Integral a => a
v = fromIntegral (v :: a)