将Haskell加法函数与Num和Char值一起使用

时间:2018-11-26 19:11:16

标签: haskell

我对以下功能有疑问

sum f l1 l2 = (f l1) + (f l2)

它不适用于sum length [1,2] ['a','b']。当我尝试这个时,我会得到

No instance for (Num Char) arising from the literal ‘1’ 

错误,所以问题出在类型上。当我尝试:t函数时,我得到sum :: Num a => (t -> a) -> t -> t -> a。因此,如果我正确地理解了这一点,就不能只同时使用数字和字符值的+函数,而对于这种情况的确切原因以及解决方法,我缺乏更深入的了解。 / p>

我尝试了几件事,例如将let用于一个文字或id函数,但这似乎不起作用。有帮助吗?

2 个答案:

答案 0 :(得分:10)

从代码中推断类型时,GHC会假设您希望f具有相对简单的类型,并希望l1l2具有相同的类型,以便两者适合作为f的输入。

您显然想传递一个多态的f,它可以同时在[Int][Char]上使用。根据您希望获得的一般程度,以下是一些选项:

在列表上工作,f必须在任何列表上工作,无论元素类型如何:

sum0 :: (forall x. [x] -> Int) -> [a] -> [b] -> Int
sum0 f l1 l2 = f l1 + f l2

处理列表和其他Foldable类型(向量,集合,矩阵),只要两个输入都相同Foldable。第一个参数可以是length,也可以是Foldable的特定选择,例如Set.size

sum1 :: (Num n, Foldable f) => (forall x. f x -> n) -> f a -> f b -> n
sum1 f l1 l2 = f l1 + f l2

允许l1l2成为不同 Foldable类型。 f必须可用于 any 可折叠。 length仍然符合条件,但是Set.size不够笼统。

sum2 :: (Num n, Foldable s, Foldable t) => (forall f x. Foldable f => f x -> n) -> s a -> t b -> n
sum2 f l1 l2 = f l1 + f l2

实际上,对于这么小的函数,我认为在每个使用站点编写length l1 + length l2比定义具有上述任何复杂类型的函数要容易得多。但是很高兴知道我们可以在需要时编写这些类型。

答案 1 :(得分:3)

一种方法(有点临时)是更改列表值:

> sum length (map Right [1,2]) (map Left ['a', 'b'])
4

我们具有通用类型[Char]的参数,而不是Num a => [a]Num b => [Either b Char]类型的参数。这样就满足了第二个参数和第三个参数具有相同类型的推断约束(由f应用于l1l2这两个事实所引起的约束)。

> :t sum
sum :: Num a => (t -> a) -> t -> t -> a

实际上,因为我们知道length并不关心其参数的内容,所以我们可以丢弃有关列表中内容的任何信息,并将相同的函数映射到两者上:

> let foo = const () in sum length (map foo [1,2]) (map foo ['a', 'b'])
4

由于const ()会忽略其参数并返回(),因此您只需用等长的()列表替换每个列表:

> map (const ()) [1,2]
[(),()]
> map (const ()) ['a','b']
[(),()]