我在Haskell中定义了一个函数
func x | x > 0 = 4
| otherwise = error "Non positive number"
其推断类型是
func :: (Ord a, Num a, Num t) => a -> t
为什么它的类型不能
func :: (Ord a, Num a) => a -> a
答案 0 :(得分:6)
类型func :: (Ord a, Num a) => a -> a
表示返回的类型应该与您传入的类型匹配。但是由于您返回的是常量并且它不依赖于输入,因此其类型可以是任何类型的类{{1} }。因此,编译器会推断Num
。
答案 1 :(得分:4)
推断类型是正确的。让我们看看它是如何被推断出来的。我们只会先查看返回值:
func x | … = 4
| … = error "…"
后一个词error "…" :: a
不受约束。但是,4
的类型为Num a => a
,因此您的函数将具有类型
func :: (Num result) => ????? -> result
现在让我们看看x
:
func x | x > 0 = …
| …
由于使用了(>) :: Ord a => a -> a- > Bool
和0 :: Num a
,x
必须是Num
和Ord
的实例。所以从这个角度来看,我们已经
func :: (Num a, Ord a) => a -> ????
重要的是,4
和x
不会互动。因此func
有
func :: (Num a, Ord a, Num result) => a -> result
当然,result
和a
可以是同一类型。顺便说一下,只要您连接x
和4
,类型就会更具体:
func x | x > 0 = 4 + x - x
| …
将具有(Num a, Ord a) => a -> a
类型。
答案 2 :(得分:2)
您可能或可能不知道,Haskell’s numeric literals are polymorphic。这意味着,0
或4
等数字文字的类型为Num a => a
。您似乎已经理解了这一点,因为您期望的类型签名包含Num
约束。
事实上,您提供的类型签名是有效签名。如果你包含它,它将被编译器接受,如果它是你真正想要的类型,通过包括它。但是,它与推断类型的不同之处在于Haskell将尝试推断最通用的类型,并且推断类型比您编写的类型更通用。
问题是,为什么允许推断类型?好吧,考虑一下这个相似但不同的功能:
func' x | x > 0 = "ok"
| otherwise = error "Non positive number"
此函数的类型为(Ord a, Num a) => a -> String
。值得注意的是,除了结果是否为⊥之外,函数的输入对输出完全没有影响。出于这个原因,结果可以是任何东西,包括字符串。
然而,考虑另一个功能,再次类似但不同:
func'' x | x > 0 = x - 1
| otherwise = error "Non positive number"
此函数必须具有您描述的类型,因为它使用x
来生成新值。
但是,请再次查看原始func
定义。请注意右侧的文字4
与x
没有任何关系。该定义实际上比func'
更接近func''
,因此数字文字具有Num t => t
类型,并且它永远不需要专门化。因此,推断类型允许两个数字类型分开,并且您在问题中编写了更通用的类型。