为什么GHC在这里推断出单态类型,即使禁用了MonomorphismRestriction?

问题描述 投票:16回答:1

这是由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 recursion type-inference parametric-polymorphism
1个回答
19
投票

多态递归(函数调用自身的类型与调用它的类型不同)总是需要类型签名。完整的解释是在Haskell 2010报告的Section 4.4.1中:

如果定义了变量f而未提供相应的类型签名声明,那么在其自己的声明组(请参阅f)之外的每个Section 4.5的使用都被视为具有相应的推断或主体类型。但是,为了确保仍然可以进行类型推断,f在其声明组中的定义出现和所有使用必须具有相同的单态类型(通过泛化获得主要类型,如Section 4.5.2中所述)。

后面的同一部分介绍了类型签名支持的多态递归示例。

我的理解是,在存在多态递归的情况下,无辅助类型推断通常是不可判定的,所以Haskell甚至都没有尝试过。

在这种情况下,类型检查器以

w :: a

其中a是一个元变量。由于fromInteger在其自己的声明中被称为w作为参数(因此在其声明组中),因此类型检查器将aInteger统一起来。没有变量可以概括。

由于同样的原因,对程序进行略微修改会产生不同的结果:

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)
© www.soinside.com 2019 - 2024. All rights reserved.