Haskell“无法推断”错误

时间:2014-12-12 22:41:24

标签: function haskell compiler-errors

我有以下代码:

class Coll c e where
    map :: (e1 -> e2) -> c e1 -> c e2
    merge :: (e -> e -> e) -> e -> c e -> e
    sum :: (Num e) => c e -> e
    sum = merge (+) 0

到目前为止一切顺利。但后来我有:

    sumMap :: (Num e2) => (e1 -> e2) -> c e1 -> e2
    sumMap f c = (merge (+) 0) (map f c)

编译会出错:

  

无法推断(Coll c e2)从上下文中使用'merge'(Coll ce)[...]可能的修复:将(Coll c e2)添加到sumMap的类型签名的上下文中[ ...]

所以我将sumMap :: (Num e2) => (e1 -> e2) -> c e1 -> e2替换为sumMap :: (Num e2, Coll c e2) => (e1 -> e2) -> c e1 -> e2,但之后又出现了另一个错误:

  

无法推断(Coll c e0)因使用上下文中的'map'而引起的[Coll ce] [...]可能的解决方法:添加修复这些类型变量的类型签名[...] ]

我很困惑,所以我会对sumMap的定义进行评论,并运行:t (merge (+) 0) . (map (* 2)),这会给我[...] :: (Num c, Coll c1 c, Coll c1 e) => c1 c -> c。忽略它如何破坏变量的名称,Coll c1 e是奇怪的; e甚至没有在定义中使用!,为什么它在那里!?无论如何,我运行((merge (+) 0) . (map (* 2))) [1,2,3,4],成功返回20。这里发生了什么?为什么只有在我不尝试将它与名称联系起来时,此功能才起作用?

1 个答案:

答案 0 :(得分:5)

您的问题源于您定义Col课程的方式。特别是,类定义包括两种类型c e。这意味着您可以为不同类型的元素提供不同的实例 - 可能不是您想要的。相反,您希望为每个可能适用于任何类型元素的c创建一个实例。

将课程编写为:

会更好
class Coll c where
  map :: (e1 -> e2) -> c e1 -> c e2
  merge :: (e -> e -> e) -> e -> c e -> e
  sum :: Num e => c e -> e
  sum = merge (+) 0

现在每个单独的实例仅依赖于c,而不是其元素的类型。

我怀疑你写了class Coll c e where,因为你想确保c是元素的集合。 (就像在Java中编写C<E>一样。)但是,在Haskell中这是不必要的:像c这样的类型变量可以代表没有其他注释的参数化类型。类型系统会发现c通过您在mapmerge的签名中使用它的方式来获取参数。

由于这是不必要的,class Coll c e意味着该类基于两个不同的类型变量,这些变量甚至不必相关。这意味着,要确定要使用的实例,类型系统需要知道集合其元素的特定类型,并且在您的特定情况下,它没有足够的信息来执行此操作。如果你只是将e从类定义中删除,那应该不是问题。