Haskell"无法推断"错误

Joh*_*ner 2 haskell compiler-errors function

我有以下代码:

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
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.但后来我有:

    sumMap :: (Num e2) => (e1 -> e2) -> c e1 -> e2
    sumMap f c = (merge (+) 0) (map f c)
Run Code Online (Sandbox Code Playgroud)

编译会出错:

无法推断(Coll c e2)因使用来自上下文的"合并"(Coll ce)[...]可能的修复:将(Coll c e2)添加到sumMap的类型签名的上下文中[...] ]

所以我替换sumMap :: (Num e2) => (e1 -> e2) -> c e1 -> e2sumMap :: (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.这里发生了什么?为什么只有当我不试图将它与名称联系起来时,此功能才有效?

Tik*_*vis 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
Run Code Online (Sandbox Code Playgroud)

现在每个实例仅依赖c于其元素的类型.

我怀疑你写的class Coll c e where是因为你想确保这c是一个元素的集合.(就像C<E>在Java中编写一样.)但是,在Haskell中这是不必要的:类型变量就像c可以代表没有其他注释的参数化类型一样.类型系统将c根据您在签名中使用它的方式来确定参数map,merge依此类推.

由于这是不必要的,class Coll c e意味着该类基于两个不同的类型变量,甚至不必相关.这意味着,要弄清楚要使用的情况下,该类型系统需要知道集合双方的具体类型它的元素,并且在特定情况下,它没有足够的信息来做到这一点.如果你e不在课堂定义之外,那应该不是问题.