Haskell:`Num [a] => a` 和 `Num a => [a]` 有什么区别?

3

显然,我的类型标识有误。我已经找到了问题所在。现在,我想更多地了解关于我的打字错误上的GHCI推断签名。我试图让这段代码正常运行:

elemNum :: (Eq a, Num b) => a -> [a] -> b
elemNum e l = f e l
  where  f _ [] = []  -- this was my typo, supposed to read 0
         f e (x:xs)
             | x == e = 1 + f e xs
             | otherwise = f e xs

显然,由于上述原因,它无法工作;但是,如果我删除我的签名,则可以编译(不确定为什么,请解释一下),并且我会得到这个签名:

elemNum :: (Num [a], Eq t) => t -> [t] -> [a]

我以前从未见过类型类 Num [a],它是什么意思,它与 (Num a) => [a] 有什么区别。

2个回答

8
Num a 表示类型 a 可以被视为数字;例如,您可以将两个 a 相加得到一个新的 a,或者您可以取反一个 a 并得到一个 aIntegerDouble 属于此类别。
相应地,Num [a] 表示类型 [a] 可以被视为数字。也就是说,您可以将两个 a 的列表相加以获得一个新的 a 列表。这很不可能有意义,因为默认情况下没有列表是数字。这意味着您将列表视为数字,并导致 GHC 得出结论,即您必须希望您的列表像数字一样运作,并因此添加一个适当的约束条件。
这样的约束可能来自于以下函数:
foo (x:xs) = xs + 1
< p > xs 被匹配为列表的尾部,因此它本身也是一个列表,然后您将其作为数字处理并添加到其中。


1
我能把 [a] 设为 Num 的成员吗?如果可以,你能提供一个简单的例子吗?如果不行,为什么? - Evan Carroll
4
可以的。例如,如果你想提供多项式,但通常情况下这是一个坏主意。在这个例子中,当a是Num类型的时候,[a]也会成为Num类型。fromIntegral函数将一个数值类型的值转换为Num类型的列表。加号运算符定义了两个列表相加的规则,如果其中一个列表为空,则返回另一个列表。这里省略了其他定义。 - Edward Kmett
3
从技术上讲,你可以通过类似于 instance Num [a] where fromInteger x = undefined; x + y = undefined 的方式使任何东西成为任何类型类的实例(前提是种类匹配)。然而,这显然是没有用的。 - John L

2

Num [a] 约束意味着该列表是 Num 类型类的实例。

foo :: Num a => [a]

列表是值的集合,这些值是 Num 类型类的实例。

bar :: Num [a] => [a]

列表是值的集合,它本身是Num类型类的实例。这意味着您可以使用Num中的所有函数,并从数字字面量构造列表,因此以下代码是有效的,尽管需要FlexibleContexts。

bar :: Num [a] => [a]
bar = 42

附言:在这里你不需要辅助函数。如果 elemNum e l = f e l,则 elemNum = f。因此,您可以将您的函数编写为

elemNum _ [] = 0
elemNum e (x:xs)
  | x == e = 1 + f e xs
  | otherwise = f e xs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接