为什么f =(+)不需要类型注释?

时间:2015-02-11 15:02:47

标签: haskell functional-programming

我的意思是,例如,

f :: (Enum a) => a -> a --without this line, there would be an error
f = succ

这是因为succ需要其参数可枚举(succ :: (Enum a) => a -> a

但适用于(+)

f = (+) --ok

虽然(+)的声明是(+) :: (Num a) => a –> a –> a

我的意思是,为什么我不需要将f声明为f :: (Num a) => a –> a –> a

3 个答案:

答案 0 :(得分:14)

因为违约。 Num是一个'defaultable'类型类,这意味着如果你让它不受约束,编译器会对你打算使用它的类型进行一些智能猜测。尝试将该定义放在模块中,然后运行

:t f
<{1>}中的

;它应该告诉你(IIRC)ghci。编译器不知道您要使用哪个f :: Integer -> Integer -> Integer,因此它猜到了a;从那以后,就可以猜到了。

为什么不推断Integer的多态类型?由于可怕的[1]单态性限制。当编译器看到

f

它认为'f = (+) 是一个值',这意味着它需要一个(单态)类型。 Eta-将定义扩展为

f

您将获得多态类型

f x = (+) x

并且类似地,如果你eta-扩展你的第一个定义

f :: Num a => a -> a -> a

您不再需要类型签名了。

[1] GHC文件中的实际名称!

答案 1 :(得分:7)

  

我的意思是,为什么我不需要将f声明为(+) :: (Num a) => a –> a –> a

如果您完全声明f的签名,则需要这样做。但是如果你不这样做,编译器就会“猜测”签名本身 - 在这种情况下,这并非显着,因为它基本上只能复制和粘贴(+)的签名。而这正是它将要做的事情。

......或者至少它应该做什么。如果你有-XNoMonomorphism标志,它确实如此。否则,dreaded monomorphism restriction步骤ConstantApplicativeForm = Value,因为f的定义属于{{3}}形状;这使编译器将签名变为可以找到的下一个最佳非多态类型,即Integer -> Integer -> Integer。为了防止这种情况,您应该为所有顶级功能手动提供正确的签名。这也可以防止很多混乱,许多错误变得不那么混乱。

单态限制是原因

f = succ

不能独立工作:因为它也具有这种CAF形状,编译器不会尝试推断出正确的多态类型,而是试图找到一些具体的实例来制作单态签名。但与Num不同,Enum类不提供默认实例。

可能的解决方案,按优先顺序排列:

  1. 始终添加签名。你真的应该。
  2. 启用-XNoMonomorphismRestriction
  3. f a = succ af a b = a+b的形式编写您的函数定义。因为有明确提到的论点,这些不具备CAF的资格,因此单态限制不会起作用。

答案 2 :(得分:0)

Haskell默认Num约束到IntInteger,我忘记了。