解决此类问题的一种方法(我可以推荐的一种方法)是将您的函数定义提供给编译器,而无需附带类型签名,然后使用交互式环境检查编译器为函数推断出的类型。
在您的情况下,如果我们定义
versioncheck [] [] = True
versioncheck (x:xs) []
|x /= 0 = False
|otherwise = versioncheck xs []
versioncheck (x:xs) (y:ys)
| x /= y = False
| otherwise = versioncheck xs ys
然后,在 GHCi 中,查询
> :type versioncheck
它给了我们
versioncheck :: (Eq a, Num a) => [a] -> [a] -> Bool
明确表明您的函数在两个列表上运行,其中元素来自同一类型a
,并且该类型a
应该属于Eq
具有可以测试相等性的值的类型类Num
和数字类型类。
这些类约束从何而来?第一个 (for Eq
) 是通过将两个输入列表的元素与 进行比较来引入的/=
。第二个 (for Num
) 通过将第一个列表的元素与数值 ( 0
) 进行比较。
现在,您提供的类型签名为String -> String -> Bool
. 由于字符串只是字符列表,因此扩展为[Char] -> [Char] -> Bool
. 将此与推断的签名进行比较,您的签名将类型变量实例a
化为Char
. 就Eq
-constraint ona
而言,这是可以的,因为Char
它确实是Eq
. 但是,Char
根据需要,它不是一个实例Num
(因此编译器不知道如何执行测试x /= 0
);因此,类型错误。
一种解决方案是使用推断的签名,但如果它确实是您想要处理的字符列表,您将不得不摆脱测试x /= 0
并x
与字符值进行比较。例如:
versioncheck :: String -> String -> Bool
versioncheck [] [] = True
versioncheck (x:xs) []
|x /= '0' = False
|otherwise = versioncheck xs []
versioncheck (x:xs) (y:ys)
| x /= y = False
| otherwise = versioncheck xs ys
(注意 中的引号'0'
)编译得很好。