为什么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
。
我的意思是,我为什么不需要声明f
为f :: (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文档的实际名称!
我的意思是,为什么我不需要声明
f
为(+) :: (Num a) => a –> a –> a
?
如果您完全声明f
的签名,您确实需要这么做。但是,如果你不这样做,编译器会“猜测”在这种情况下,签名本身–这还不是全部,以显着的,因为它可以基本上只是复制粘贴&的(+)
签名。而这正是它会做的。
...或者至少它应该做什么。它的确如此,只要你有-XNoMonomorphism
标志。否则,dreaded monomorphism restriction步骤因为f
的定义是形状ConstantApplicativeForm = Value;这使得编译器将签名哑变为下一个最好的非多态类型,它可以找到,即Integer -> Integer -> Integer
。为了防止这种情况发生,您应该事实上为所有顶级功能提供正确的签名。这也防止了很多混淆,并且许多错误变得不太混乱。
的单态的限制是原因
f = succ
不会对自己的工作:因为它也有这个CAF形状,编译器不会尝试推断正确的多态类型,但试图找到一些具体实例化以形成单形签名。但与Num
不同,Enum
类不提供默认实例。
可能的解决办法,按优先顺序排列:
- 始终添加签名。你真的应该。
- 启用
-XNoMonomorphismRestriction
。 - 用
f a = succ a
,f a b = a+b
的形式写出你的函数定义。因为有明确提到的参数,这些没有资格作为CAF,所以单态的限制不会踢。
哈斯克尔默认Num
约束Int
或Integer
,我忘了。
何时会发生这种减少? ghci中的'f =(+)'不受限制。 – Bergi 2015-02-11 22:59:43
@Bergi - 答案在这里:http://stackoverflow.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