创建一个Haskell类型类,可以定义一个计算两个值出现次数的函数

时间:2018-03-11 00:42:57

标签: haskell typeclass

我正在尝试创建一个定义两个函数的类型类。一个叫做numMatching。它需要两个值并返回第二个值中第一个值的出现次数。例如:numMatching 1 1 = 1,因为第一个值1在第二个值1中出现一次.numMatching 1 0 = 0,因为第一个值永远不会出现在第二个值0中。

现在我已经定义了这个类:

class Counter a where
    numMatching :: a -> a -> Int
    numNonMatching :: a -> a -> Int 

我正在尝试为Int类型创建此类的实例,但我确实如此 困惑。

instance Counter Int where
    numMatching a b =
        if a == b
            then 1
            else 0

我尝试通过输入numMatching 1在ghci中运行此函数。它应返回值1但我收到错误说我需要一个类型注释来指定a和b是什么。

错误:enter image description here

任何帮助将不胜感激:)

1 个答案:

答案 0 :(得分:1)

首先,1的类型不是Int,而是:

GHCi> :t 1
1 :: Num p => p

这意味着1文字可以代表Num实例的任何类型的值,具体类型取决于您如何使用它。这就是为什么1 + 2 :: Integer1 + 2.5 :: Double工作的原因:1在一个案例中是Integer,在另一个案例中是Double(+)的类型是......

GHCi> :t (+)
(+) :: Num a => a -> a -> a

...因此,如果您说1 + 2的结果应该是Integer,那么GHC会推断12的类型应该是Integer 1}},因为(+)的结果与其参数的类型相同。

现在,我们来看看numMatching的类型:

GHCi> :t numMatching
numMatching :: Counter a => a -> a -> Int

我们知道numMatching的结果是Int,但与(+)的结果不同,它不会告诉我们参数的类型。因此,GHC没有足够的信息来决定应该选择Num的哪个实例作为1numMatching 1 1的类型。这就是“模糊类型变量[...]阻止约束[...]被解决”的意思。无法为1选择类型的后果之一是无法选择Counter的实例,甚至无法确定是否存在实例(例如,您已为Int定义了一个{1}},但不适用于Double)。这就是为什么你单独提到Counter的错误。

要在保留实例的同时避免此问题(即保留Int实例而不必添加任何其他实例),请向1中的任意一个添加显式类型注释。你转到numMatching

GHCi> numMatching (1 :: Int) 1
1

请注意,如果其中一个参数已经具有具体类型,则不需要额外的注释:

GHCi> :{
GHCi| foo :: Int
GHCi| foo = 1
GHCi| :}
GHCi> :t foo
foo :: Int
GHCi> numMatching foo 1
1

为了对比起见:

GHCi> numMatching (1 :: Double) 1

<interactive>:64:1: error:
    * No instance for (Counter Double)
        arising from a use of `numMatching'
    * In the expression: numMatching (1 :: Double) 1
      In an equation for `it': it = numMatching (1 :: Double) 1