为什么即使禁用MonomorphismRestriction,GHC仍会在此处推断单态类型?

lef*_*out 16 recursion haskell type-inference parametric-polymorphism

解决“ f = f(<*>)pure”类型提示了这一点,该类型讨论了一个更复杂的示例,但该示例也有效。

以下定义可以毫无问题地进行编译:

w :: Integral a => a
w = fromInteger w
Run Code Online (Sandbox Code Playgroud)

...当然,它在运行时无法正常工作,但这是个问题。问题的关键是,定义w本身使用的专用版本w :: Integer。显然,这一个合适的实例,因此进行类型检查。

但是,如果我们删除签名,那么GHC不会推断上述类型,而只能推断具体类型:

w' = fromInteger w'
Run Code Online (Sandbox Code Playgroud)
GHCi> :t w
w :: Integral a => a
GHCi> :t w'
w' :: Integer
Run Code Online (Sandbox Code Playgroud)

好吧,当我看到这一点时,我相当确定这是工作中的单态性限制。众所周知,例如

i = 3
Run Code Online (Sandbox Code Playgroud)
GHCi> :t i
i :: Integer
Run Code Online (Sandbox Code Playgroud)

虽然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'
Run Code Online (Sandbox Code Playgroud)
GHCi> :t q
q :: RealFrac a => a
GHCi> :t q'
q' :: Float
Run Code Online (Sandbox Code Playgroud)

为什么不能推断多态类型?

dfe*_*uer 19

多态递归(函数以与调用函数不同的类型调用自身)始终需要类型签名。完整的解释在《 Haskell 2010报告》的第4.4.1节中:

如果在f没有提供相应类型签名声明的情况下定义了变量,则每次f在其自己的声明组之外使用(请参见第4.5节)都将被视为具有相应的推断类型或主体类型。但是,为了确保仍然可以进行类型推断,其定义f组中的定义出现及其所有使用都必须具有相同的单态类型(如4.5.2节所述,通过泛化可从中获得主体类型)。

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

我的理解是,在存在多态递归的情况下,通常无法确定无助的类型推断,因此Haskell甚至不会尝试。

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

w :: a
Run Code Online (Sandbox Code Playgroud)

a元变量在哪里。由于在其自己的声明中(因此也在其声明组中)fromInteger将with w作为参数调用,因此类型检查器a与合并Integer。没有可归纳的变量。

出于相同的原因,对程序进行轻微的修改会得到不同的结果:

v = fromIntegral v
Run Code Online (Sandbox Code Playgroud)

根据您的原始推理,Haskell会推断v :: forall a. Num a => a,默认v将RHS上的输入为Integer

v :: forall a. Num a => a
v = fromIntegral (v :: Integer)
Run Code Online (Sandbox Code Playgroud)

但是,它以开头v :: a。既然v通过了fromIntegral,就强加了Integral a。最后,概括了a。最后,程序证明是

v :: forall a. Integral a => a
v = fromIntegral (v :: a)
Run Code Online (Sandbox Code Playgroud)

  • 我的学士学位是关于Haskell子集的简单类型推断器,包括类型类和多态递归。一种非常简单的方法是将多态递归的深度限制为`k'深度。可以用非常低的深度范围(如k = 1或k = 2)来推断多态递归的最有用情况。无论如何,Haskell类型推断已经无法确定,因此这不是*不允许*的*唯一*原因。另一个原因可能是性能,它肯定会使类型推断为O(k·f(n))而不是O(f(n)),因为您可能需要重新进行k次。 (2认同)
  • @Bakuriu,我很确定Haskell 2010 *(无*)多态递归具有完整的类型推断-那时基本上是Hindley-Milner,还有类型类和默认值。您是否有其他说法?至于一些有限的递归深度:这听起来可能是有用的扩展,但是它的风格与Haskell报告趋向于完全不同。我会发现这种功能对于发现多态递归代码的正确类型签名最为有用。 (2认同)
  • 无法推断多态递归的原因恰恰是因为它不确定。这是ghc获得许多其他无法确定的功能之前的一个决定。您可以信任我,那时我是Haskell委员会的成员。:) (2认同)