为什么我会得到一个“无法推断(Ord a)”的错误?

8

我正在尝试找到元素之和最小的列表。

shortest :: (Num a) => [[a]] -> [a]
shortest [] = []
shortest (x:xs) = if sum x < sum (shortest xs) then x else shortest xs

这导致了以下错误:
Could not deduce (Ord a) arising from a use of `<'
from the context (Eq a)
  bound by the type signature for shortest :: Eq a => [[a]] -> [a]
  at code.hs:(8,1)-(9,71)
Possible fix:
  add (Ord a) to the context of
    the type signature for shortest :: Eq a => [[a]] -> [a]
In the expression: sum x < sum (shortest xs)
In the expression:
  if sum x < sum (shortest xs) then x else shortest xs
In an equation for `shortest':
    shortest (x : xs)
      = if sum x < sum (shortest xs) then x else shortest xs
为什么函数无法通过类型检查?

“shortest” 不是一个合适的名称,考虑使用 minimumBy (compare \on` sum),并结合 Data.ListData.Function` 中的高阶函数。 - leftaroundabout
1
为了理解这个问题,重要的是要知道并非所有数字都可以排序。例如,像1+2i这样的复数就没有固定的排序方式。 - dflemstr
1
@leftaroundabout已经提出了使用库函数的解决方案;但如果您仍然想从头开始编写它作为练习,除了修复类型签名问题外,您还需要考虑shortest []的值应该是什么,或者换句话说,递归的基础是什么(提示:通常对于空列表,minimummaximum未定义)。 - Rafael Caetano
2个回答

18

这段代码涉及到两个类型类: NumOrd。需要注意的是,一个类型可以是 Num 成员但不是 Ord 成员,反之亦然。

sum 函数的类型是 Num a => [a] -> a ,因此 shortest 的输入元素需要是 Num 类型的成员。你在代码中还做了以下操作:

sum x < sum (shortest xs)

这意味着您在a上使用了运算符<,但在类型签名中,您没有要求as是Ord的实例,而Ord定义了<

class Eq a => Ord a where
  compare :: a -> a -> Ordering
  (<) :: a -> a -> Bool
  ...
因此,您需要将该要求添加到您的类型签名中:
shortest :: (Ord a, Num a) => [[a]] -> [a]

或者你可以省略类型签名。


5
一种诊断这种问题并同时了解类型和类型类的方法是暂时删除类型签名,将模块加载到GHCi中,然后输入“:t shortest”查看编译器分配给它的类型。同样,如果您遗漏了类型签名,请尝试添加它以查看函数是否具有您期望的类型签名。我使用这种技术学到了很多东西。 - mhwombat

6

Num 不包含 Ord,因此在类型签名中的 a 上缺少 Ord 约束。应该改为:

shortest :: (Num a, Ord a) => [[a]] -> [a]

您可以删除类型签名,GHC将自动为您推断。

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