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

问题描述:

我的意思是,例如,为什么f =(+)不需要类型注释?

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

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

由于违约。Num是一个'可违约'类型的类,这意味着如果你不加限制,编译器会对你打算使用它的类型做出一些智能猜测。尝试将该定义放入模块中,然后运行

:t f 

in ghci;它应该告诉你(IIRC)f :: Integer -> Integer -> Integer。编译器不知道你想用哪个a,所以它猜到了Integer;并且自那以后工作,它符合那个猜测。

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

f = (+) 

它认为“f是值”,这意味着它需要单个(单态)型。 ETA-扩大定义

f x = (+) x 

,你会得到的多态型

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

,同样,如果你ETA-扩大你的第一个定义

f x = succ x 

你不需要再输入一次签名。

[1] GHC文档的实际名称!

+0

何时会发生这种减少? ghci中的'f =(+)'不受限制。 – Bergi 2015-02-11 22:59:43

+1

@Bergi - 答案在这里:http://*.com/questions/28336108/why-is-22-0-double-in-a-hs-file-but-fractional-aa-in-ghci/28336620 #28336620 – 2015-02-11 23:52:53

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

如果您完全声明f的签名,您确实需要这么做。但是,如果你不这样做,编译器会“猜测”在这种情况下,签名本身–这还不是全部,以显着的,因为它可以基本上只是复制粘贴&的(+)签名。而这正是它会做的。

...或者至少它应该做什么。它的确如此,只要你有-XNoMonomorphism标志。否则,dreaded monomorphism restriction步骤因为f的定义是形状ConstantApplicativeForm = Value;这使得编译器将签名哑变为下一个最好的非多态类型,它可以找到,即Integer -> Integer -> Integer。为了防止这种情况发生,您应该事实上为所有*功能提供正确的签名。这也防止了很多混淆,并且许多错误变得不太混乱。

的单态的限制是原因

f = succ 

不会对自己的工作:因为它也有这个CAF形状,编译器不会尝试推断正确的多态类型,但试图找到一些具体实例化以形成单形签名。但与Num不同,Enum类不提供默认实例。

可能的解决办法,按优先顺序排列:

  1. 始终添加签名。你真的应该。
  2. 启用-XNoMonomorphismRestriction
  3. f a = succ af a b = a+b的形式写出你的函数定义。因为有明确提到的参数,这些没有资格作为CAF,所以单态的限制不会踢。

哈斯克尔默认Num约束IntInteger,我忘了。