实际上你在问题中给出的函数编译得很好。你会得到你引用的错误,如果你有的是:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| b == [] = []
| g == [] = []
| otherwise = (a, z) : myzip b g
有一个明确的类型签名表明myzip 适用于任何 类型a 和b 的列表。但是您使用过b == [] 和g == []。等式运算符没有在任何类型上定义,仅在属于Eq 类型类成员的类型上定义,因此您编写的代码与您提供的类型不匹配。
这就是错误消息直截了当地说的内容,但如果您只是在学习并且还没有开始输入类,那么它有点不清楚。
如果您将myzip 的类型签名更改为说a 和b 需要是Eq 类型类的成员,那么您提供的代码将起作用:
myzip :: (Eq a, Eq b) => [a] -> [b] -> [(a, b)]
或者,如果您完全关闭类型签名(就像您在问题中所做的那样),GHC 实际上会根据您使用 == 运算符这一事实推断出这种类型,并且代码只是按原样编译。
但是,检查列表是否为空可以不使用== 运算符来完成,因此您可以编写myzip,以便它确实可以对任何类型a 和b 进行操作。一种方法是使用null 函数:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:b) (z:g)
| null b = []
| null g = []
| otherwise = (a, z) : myzip b g
但更常见的方法是简单地使用多个方程式来定义 myzip,基本情况与模式 [] 匹配,而主要情况则假设列表非空:
myzip :: [a] -> [b] -> [(a, b)]
myzip (a:[]) _ = []
myzip _ (z:[]) = []
myzip (a:b) (z:g) = (a, z) : myzip b g
请注意,这种风格也表明您的实现中存在错误。您正在丢弃最后一个 a 或 z,并且列表完全为空时没有任何情况!
当您的等式显示 myzip (a:b) (z:g) 并且 然后 对照空列表检查 b 和 g 时,实际上检查错误的东西太晚了。您不需要检查b 是否为[],您需要检查整个 列表是否为空。但是您已经假设它不是空的并将其分解为a:b。这会导致您的代码 (a) 返回错误的结果,因为它丢弃了应该压缩的最后一对元素,并且 (b) 当其中一个参数是空列表时产生错误。
列表上的递归通常看起来更像这样:
myzip :: [a] -> [b] -> [(a, b)]
myzip [] _ = []
myzip _ [] = []
myzip (a:b) (z:g) = (a, z) : myzip b g
这行为正确。