假设我有以下课程:
class P a where
nameOf :: a -> String
我想声明此类的所有实例都是Show
的自动实例。我的第一次尝试将如下:
instance P a => Show a where
show = nameOf
我昨天第一次尝试这种方式导致了一个语言扩展的兔子:我首先被告知要打开灵活的实例,然后是不可判定的实例,然后重叠实例,最后得到关于重叠实例声明的错误。我放弃了并重新开始重复代码。然而,这从根本上说似乎是一个非常简单的需求,而且应该很容易满足。
所以,有两个问题:
UndecidableInstances
,因为我似乎违反了Paterson条件,但这里没有重叠的实例:没有P
的实例,甚至。为什么类型检查器认为Show Double
有多个实例(在这个玩具示例中似乎就是这种情况)?答案 0 :(得分:5)
您收到重叠实例错误,因为您的某些P
实例可能有Show
的其他实例,然后编译器将无法决定使用哪些实例。如果你有P
Double
的实例,那么你可以得到Show
Double
的两个实例:你的一般实例和已经在Haskell中声明的实例基础库。 @augustss在您的问题的评论中正确说明了如何触发此错误。有关详细信息,请参阅the specs。
正如您所知,如果没有UndecidableInstances
,就无法实现您的目标。当您启用该标志时,您必须了解您正在接管编译器的责任,以确保不会出现任何冲突的实例。这意味着,当然,您的库中不得出现Show
的任何其他实例。这也意味着您的库不会导出P
类,这将消除库的用户声明冲突实例的可能性。
如果您的案件与上述情况发生某种冲突,那么这是一个可靠的迹象,表明它一定有问题。事实上还有......
你想要实现的目标是不正确的。您遗漏了关于Show
类型类的几个要点,将其与流行的OO语言的toString
方法等结构区分开来:
show的结果是一个语法正确的Haskell表达式,只包含常量,给定在声明类型的位置生效的固定声明。它仅包含数据类型,括号和空格中定义的构造函数名称。使用标记的构造函数字段时,还会使用大括号,逗号,字段名称和等号。
换句话说,声明Show
的实例,它不会产生有效的Haskell表达式,本身就是不正确的。
鉴于上述内容,当类型允许简单派生时,声明Show
的自定义实例是没有意义的。
当一个类型不允许派生它(例如,GADT)时,通常你仍然必须坚持使用特定类型的实例来产生正确的结果。
因此,如果您需要自定义表示功能,则不应使用Show
。只需声明一个自定义类,例如:
class Repr a where
repr :: a -> String
负责任地处理实例声明。