【问题标题】:Haskell "could not deduce" errorHaskell“无法推断”错误
【发布时间】:2014-12-12 22:41:24
【问题描述】:

我有以下代码:

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 e) 中推断出 (Coll c e2) [...] 可能的修复:将 (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 e) 中推断 (Coll c e0) [...] 可能的修复:添加修复这些类型变量的类型签名 [... ]

我很困惑,所以我将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。这里发生了什么?为什么这个功能只有在我不尝试将其与名称绑定时才起作用?

【问题讨论】:

  • "e 甚至没有在定义中使用!那为什么它在那里!?" - 这正是问题所在! e 没有在定义中使用,因此编译器永远无法确定它应该具有什么类型,但它必须在那里,因为该类是用 2 个参数声明的。顺便说一句,我预计这是一个学习练习,但你的课程只是 Foldable 是伪装的。

标签: function haskell compiler-errors


【解决方案1】:

您的问题源于您定义Col 类的方式。特别是,类定义包括both 类型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 这样的类型变量可以代表参数化类型而无需其他注释。类型系统会根据您在mapmerge 等的签名中使用参数的方式来确定c 采用参数。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-28
    • 1970-01-01
    • 2013-05-10
    相关资源
    最近更新 更多