2013-04-04 78 views
8

假设我有以下类:哈斯克尔继承类型类

class P a where 
    nameOf :: a -> String 

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

instance P a => Show a where 
    show = nameOf 

我第一次走这条路昨天导致语言扩展的养兔场的尝试:我是第一次告知要灵活的情况下切换,那么不可判定的情况下,则可以重叠的情况下,最后得到一个关于重叠实例声明的错误。我放弃了,然后回到重复的代码。然而,这从根本上看似乎是一个非常简单的需求,而且应该很容易满足。

于是,两个问题:

  1. 有一个平凡简单的方法来做到这一点,我刚刚错过?
  2. 为什么我会遇到重叠实例问题?我可以明白为什么我可能需要UndecidableInstances,因为我似乎违反了Paterson条件,但是在这里没有重叠的情况:甚至没有P的实例。为什么typechecker认为有多个实例Show Double(在这个玩具的例子中似乎是这种情况)?
+3

重叠(和重载分辨率)仅由实例头部'Show a'确定,所以它确实与其他每个Show实例重叠。 – augustss 2013-04-04 10:03:23

+1

假设你声明了'P Int'的实例,并且你已经有了'Show Int'的实例,所以这将导致Show的重叠实例。 – Satvik 2013-04-04 10:06:09

+0

@Satvik当然,除了我没有'P Int'的实例。如果我试图创建一个'P Int',我会期待一个错误,但不是仅仅声明它可能存在。 – Impredicative 2013-04-04 10:14:13

回答

5

你得到重叠的情况下错误,因为你的一些的P实例可能有Show其他实例,那么编译器将无法决定使用哪一个。如果你的P的实例为Double,那么你去了,你得到Show的两个实例Double:你的一般和已经在Haskell的基础库中声明的实例。 @augustss在你的问题的评论中正确地陈述了这个错误是如何被触发的。欲了解更多信息,请登录the specs

正如你所知,没有UndecidableInstances没有办法实现你正在尝试的东西。当您启用该标志时,您必须明白您正在接管编译器的责任,以确保不会出现任何冲突的实例。这意味着,当然,在您的图书馆中不得存在任何其他Show实例。这也意味着您的库不会导出P类,这将消除库的用户声明冲突实例的可能性。

如果你的情况与上述说法有冲突,这是一个可靠的迹象,说明它一定有什么问题。而事实上有...


你想达到什么是不正确的首先。这是因为丢失了几个重要点有关Show类型类,从结构像一个toString方法流行的面向对象语言的区分是:

  1. Show's haddock

    显示的结果是一个语法正确Haskell的表达只包含常量,因为在声明类型的地方有效的固定性声明。它仅包含数据类型,括号和空格中定义的构造函数名称。使用带标签的构造函数字段时,还使用大括号,逗号,字段名称和等号。

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

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

  3. 当一个类型不允许导出它(例如,GADT),通常你还是要坚持键入特定的情况下,产生正确的结果。

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

class Repr a where 
    repr :: a -> String 

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

+0

我很谨慎接受这个答案,因为我觉得这在技术上部分是错误的。问题不在于'P'的某些实例有'Show'的其他实例:如上所述,*有*没有'P'的实例。 “augustss”已经解释了真正的问题,即如何评估重叠。如果你解决了这个问题,我很乐意接受。我对你使用'show'的观点有所了解。 – Impredicative 2013-04-04 15:44:55

+0

@Impredicative可能我没有说清楚,我已经更新了第一段。 – 2013-04-04 16:07:42