哈斯克尔"could not deduce"错误



我有以下代码:

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(因使用上下文(Coll c e(中的"合并"而产生[...]可能的解决方法:将 (Coll c e2( 添加到 sumMap 的类型签名上下文中 [...]

所以我用sumMap :: (Num e2, Coll c e2) => (e1 -> e2) -> c e1 -> e2替换sumMap :: (Num e2) => (e1 -> e2) -> c e1 -> e2,但随后它给出了另一个错误:

无法推断(Coll c e0(从上下文(Coll c e(中使用"地图"引起的[...]可能的解决方法:添加一个类型签名来修复这些类型变量 [...]

我很困惑,所以我注释掉了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.这是怎么回事?为什么这个函数只有在我不尝试将其绑定到名称时才有效?

你的问题源于你定义Col类的方式。特别是,类定义包括ce两种类型。这意味着您可以为不同类型的元素使用不同的实例 - 可能不是您想要的。相反,您希望为每个可能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排除在类定义之外,那应该不是问题。

最新更新