我刚开始在Haskell编程,我正在解决99 Haskell problems,当我差不多完成第10次时,我遇到了这个问题:
-- Exercise 9
pack :: Eq a => [a] -> [[a]]
pack [] = []
pack list = let (left,right) = span (== head list) list in
left : pack right
-- Exercise 10
encode :: (Eq a, Integral c) => [a] -> [(c, a)]
encode [] = []
encode list = map (\x -> (length x, head x)) (pack list)
-- this doesn't work ^^^^^^^^
产生的错误告诉我
Could not deduce (c ~ Int)
from the context (Eq a, Integral c)
bound by the type signature for
encode :: (Eq a, Integral c) => [a] -> [(c, a)]
at C:\fakepath\ex.hs:6:11-47
`c' is a rigid type variable bound by
the type signature for
encode :: (Eq a, Integral c) => [a] -> [(c, a)]
at C:\fakepath\ex.hs:6:11
In the return type of a call of `length'
In the expression: length x
In the expression: (length x, head x)
我设法通过在Learn you a Haskell fromIntegral
中插入我已阅读过的函数来解决这个问题。
encode list = map (\x -> (fromIntegral $ length x, head x)) (pack list)
所以,我的问题是,为什么需要?
我已经运行:t length
并获得了[a] -> Int
,这对我来说是一个非常明确的类型,应该满足Integral c
约束。
答案 0 :(得分:9)
类型签名(Eq a, Integral c) => [a] -> [(c, a)]
表示该函数适用于相应类型类中的任何类型a
和c
。使用的实际类型在呼叫站点指定。
作为一个简单的例子,让我们看一下空列表的类型:
:t []
[a]
这意味着[]
代表String
的空列表,以及Int
的空列表,Maybe [Maybe Bool]
的空列表以及您可以想象的任何其他类型。我们可以想象用普通的标识符包装它:
empty :: [a]
empty = []
empty
显然与[]
的工作方式相同。所以你可以看到以下定义没有意义:
empty :: [a]
empty = [True]
毕竟,[True]
永远不会是[Int]
或[String]
或您想要的任何其他空列表。
这里的想法是一样的,除了我们对变量也有类型类约束。例如,您可以使用encode
返回[(Integer, String)]
列表,因为Integer
也在Integral
类中。
所以你必须返回可以任何 Integral
的多态性 - 正是fromIntegral
所做的。如果您刚刚返回Int
,则encode
仅可用作Int
而不是Integral
。