无法演绎(a〜[a])

时间:2013-01-08 20:13:13

标签: list haskell reverse signature type-deduction

我尝试编写一个函数,它接受一个子列表列表,反转子列表并返回连接的反向子列表。这是我的尝试:

conrev :: Ord a => [[a]] -> [a]
conrev [[]] = []
conrev [[a]] = reverse [a]
conrev [(x:xs)] = reverse x ++ conrev [xs]

main = putStrLn (show (conrev [[1,2],[],[3,4]]))

我收到此错误:

3.hs:4:27:
    Could not deduce (a ~ [a])
    from the context (Ord a)
      bound by the type signature for conrev :: Ord a => [[a]] -> [a]
      at 3.hs:1:11-31
      `a' is a rigid type variable bound by
      the type signature for conrev :: Ord a => [[a]] -> [a] at 3.hs:1:11
    In the first argument of `reverse', namely `x'
    In the first argument of `(++)', namely `reverse x'
    In the expression: reverse x ++ conrev [xs]

我做错了什么?第二个问题是 - 类型签名可能更通用吗?我必须写得尽可能通用。

2 个答案:

答案 0 :(得分:6)

在等式中

conrev [(x:xs)] = reverse x ++ conrev [xs]

匹配包含单个元素的列表,该列表是非空列表x:xs。所以,给定类型

conrev :: Ord a => [[a]] -> [a]

列表x:xs必须包含[a]类型,因此x :: a

现在,您拨打reverse x,这意味着x必须是列表x :: [b]。然后你连接

reverse x :: [b]

conrev [xs] :: [a]

开始,b必须与a的类型相同。但早先确定a ~ [b]。总而言之,这个等式需要a ~ [a]

如果您没有编写(不必要的)Ord a约束,那么您会得到不太透明的

Couldn't construct infinite type a = [a]

错误。

如果您移除了一些外部[]

,您的实施将会有效
conrev :: Ord a => [[a]] -> [a]
conrev [] = []
conrev [a] = reverse a
conrev (x:xs) = reverse x ++ conrev xs

但更好的实施将是

conrev = concat . map reverse

答案 1 :(得分:3)

你的第二种模式与你想要的不匹配,看起来你错误地认为了价值结构的类型结构。

[[a]]作为类型表示“某种类型的列表列表a

[[a]]作为模式意味着“匹配包含单个列表的列表,其中包含单个元素,该元素将绑定到名称< / em> a

编辑: 如果我理解你要做的事情,那么中间案例实际上是多余的。第三种情况将处理非空列表,第一种情况将处理空列表。为单身人士名单制作另一个案例是不必要的。

编辑2:

第三种情况的实施存在进一步的问题。

conrev :: Ord a => [[a]] -> [a]
conrev [(x:xs)] = reverse x ++ conrev [xs]

根据您看到的类型x必须是[a]类型而xs必须是[[a]]类型。因此,写conrev [xs]会将[[[a]]]类型的值传递给conrev。这是您的类型错误来自的地方。您通过调用[a]隐含地声明aconvrev [xs]的类型相同。