为什么Integral约束要求fromIntegral on call?

时间:2013-08-05 15:14:39

标签: haskell ghc

我刚开始在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约束。

1 个答案:

答案 0 :(得分:9)

类型签名(Eq a, Integral c) => [a] -> [(c, a)]表示该函数适用于相应类型类中的任何类型ac。使用的实际类型在呼叫站点指定。

作为一个简单的例子,让我们看一下空列表的类型:

: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