我对以下功能有疑问
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
函数,但这似乎不起作用。有帮助吗?
答案 0 :(得分:10)
从代码中推断类型时,GHC会假设您希望f
具有相对简单的类型,并希望l1
和l2
具有相同的类型,以便两者适合作为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
允许l1
和l2
成为不同 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
应用于l1
和l2
这两个事实所引起的约束)。
> :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']
[(),()]