10

假设我有以下课程:

class P a where
  nameOf :: a -> String

我想声明这个类的所有实例都是自动的Show。我的第一次尝试如下:

instance P a => Show a where
  show = nameOf

我昨天第一次尝试这种方式导致了语言扩展的兔子沃伦:我首先被告知要打开灵活实例,然后是不可判定实例,然后是重叠实例,最后得到关于重叠实例声明的错误。我放弃并返回重复代码。然而,从根本上来说,这似乎是一个非常简单的需求,应该很容易满足。

所以,两个问题:

  1. 有没有一种我刚刚错过的简单的方法来做到这一点?
  2. 为什么会出现重叠实例问题?我知道为什么我可能需要UndecidableInstances,因为我似乎违反了帕特森条件,但这里没有重叠的实例:P甚至没有 的实例。为什么类型检查器认为有多个实例Show Double(在这个玩具示例中似乎就是这种情况)?
4

1 回答 1

5

您会收到重叠实例错误,因为您的某些实例P可能有其他实例,Show然后编译器将无法决定使用哪些实例。如果你有一个Pfor的实例Double,那么你就得到了两个Showfor 的实例Double:你的一个通用实例和一个已经在 Haskell 的基础库中声明的实例。@augustss 在对您问题的评论中正确说明了如何触发此错误。有关更多信息,请参阅规格

如您所知,没有UndecidableInstances. 当您启用该标志时,您必须了解您正在接管编译器的责任,以确保不会出现任何冲突的实例。Show这意味着,当然,您的库中不能有任何其他的生产实​​例。这也意味着您的库不会导出P该类,这将消除库用户声明冲突实例的可能性。

如果您的案例与上述内容有某种冲突,则这是一个可靠的迹象,表明它一定有问题。而事实上有...


你想要达到的目标首先是不正确的。您遗漏了关于类型类的几个重要点,将其与流行的 OO 语言Show的方法等构造区分开来:toString

  1. 来自Show 的黑线鳕

    show 的结果是一个仅包含常量的语法正确的 Haskell 表达式,假设在声明类型的点处有效的固定性声明。它仅包含在数据类型、括号和空格中定义的构造函数名称。当使用带标签的构造函数字段时,还使用大括号、逗号、字段名称和等号。

    换句话说,声明一个Show不产生有效 Haskell 表达式的实例本身是不正确的。

  2. Show鉴于上述情况,声明类型何时允许简单地派生它的自定义实例是没有意义的。

  3. 当一个类型不允许派生它(例如,GADT)时,通常您仍然必须坚持使用特定于类型的实例才能产生正确的结果。

因此,如果您需要自定义表示功能,则不应使用Show它。只需声明一个自定义类,例如:

class Repr a where
  repr :: a -> String

并负责任地处理实例声明。

于 2013-04-04T15:29:18.407 回答