我的意思是,例如,
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
?
答案 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
类不提供默认实例。
可能的解决方案,按优先顺序排列:
-XNoMonomorphismRestriction
。f a = succ a
,f a b = a+b
的形式编写您的函数定义。因为有明确提到的论点,这些不具备CAF的资格,因此单态限制不会起作用。答案 2 :(得分:0)
Haskell默认Num
约束到Int
或Integer
,我忘记了。