刚开始学习Haskell,我试图实现一个max函数来递归地查找列表的最大值
max' :: (Num b) => [a] -> b
max' [] = 0
max' (x:xs)
| x > max' xs = x
| otherwise = max' xs
但在尝试编译时遇到错误
无法将预期类型'b'与实际类型'a'匹配'a''是由类型签名绑定的刚性类型变量:max':: forall b a . (Num b,Num a)=> [a] - > b at implementationFunctions.hs:5:1-34'b'是由类型签名绑定的刚性类型变量:max':: forall b a . (Num b,Num a)=> [a] - > b at implementationFunctions.hs:5:1-34
任何人都可以帮我理解什么是错的?
2 回答
max'
作为输入(基于签名)[a]
:a
的列表 . 但是你返回b
. 这意味着你已经写了 - 无论列表中的元素类型如何 - 我们可以选择任何类型b
作为我们想要的输出,只要它是Num b
. 但这没有意义 . 如果我们输入一个字符串列表,我们当然可以计算"largest string"(按字典顺序排列),但我们不能将其作为Num
返回 .另一个问题是你使用
(>) :: Ord a => a -> a -> Bool
函数(作为一个守卫) . 但是,您没有在函数签名中指定输入元素的类型必须是Ord
类型类的实例 . 所以你无法比较这些元素 .最小的修复是将输入类型限制为
b
:话虽如此,如果我们提供一个空列表,返回
0
没有多大意义 . 这将导致max []
实际上大于max [-1]
的奇怪事实:通常我们期望超集的最大值大于或等于集合的最大值 .因此,
max'
函数可能最好被视为非总函数:不是每个输入都会导致输出的函数 . 在这种情况下,空列表不会 .我们可以将其重写为错误:
所以现在有三种模式:(1)空列表,(2)单元列表,以及(3)具有至少两个元素的列表 .
然而,编写错误并不总是处理非总函数的好方法,因为在类型签名中看不到函数是非总函数 . 另一个想做的就是使用 Maybe b 作为返回类型 . 如果没有最大值,这将是
Nothing
,如果有最大值,则为Just x
:或更短:
例如:
你的函数会获取一些东西并返回其中一个东西 . 但函数的类型签名表示它需要一个
[a]
的列表并返回一个完全不同的东西b
. 这困扰了编译器 . 它无法将声明的类型签名与实际实现(也就是"typechecking")进行协调 .要解决此问题,请使类型签名与实现匹配: