我有一个类型类:
class Wrapper w where
open :: w -> Map String Int
close :: Map String Int -> w
它看起来不是很有用,但是我用它来强烈地(不仅仅是 type
同义词)区分语义上不同的 Map String Int
:
newtype FlapMap = Flap (Map String Int)
newtype SnapMap = Snap (Map String Int)
...
并且仍然具有可在任何类型的类上运行的函数 .
- 有没有更好的方法来做这种区分(可能没有
Wrapper
实例样板)?
我想做这个:
instance (Wrapper wrapper) => Show wrapper where
show w = show $ toList $ open w
而不是编写许多样板 Show
实例 .
通过 FlexibleInstances
和 UndecidableInstances
,GHC引导我认为我的实例声明适用于所有内容,因为据称它与我的代码和 GHC.Show
中的其他 Show
实例冲突 . HaskellWiki和StackOverflow回答者和HaskellWiki说服我 OverlappingInstances
不是很安全,可能会让人困惑 . GHC甚至没有建议 .
-
为什么GHC首先抱怨不知道要选择哪个fx
Show Int
实例(为什么它不查看我在编译时给出的约束?)然后,被告知实例可能重叠,突然知道该怎么办? -
我可以避免在
newtype
中使用OverlappingInstances
吗?
1 回答
如果没有
OverlappingInstances
,你就不能这样做,正如你所提到的,这是不可预测的 . 无论如何,它对你没有帮助,所以如果没有包装类型,你几乎无法做到这一点 .当然,这是相当不满意的,为什么会这样呢?正如您已经确定的那样,GHC在选择实例时不会查看实例上下文,只会查看实例头 . 为什么?好吧,请考虑以下代码:
如果单独考虑
Foo
或Bar
类型类,则上述定义是有意义的 . 任何实现Foo
类型类的东西都应该“免费”获得一个Show
实例 . 不幸的是,Bar
实例也是如此,所以现在你有show Something
的两个有效实例 .由于类型类总是打开的(事实上,如果你能够为它定义自己的实例,
Show
必须是打开的),所以不可能知道某人不会出现并添加他们自己的类似实例,然后在你的数据类型上创建一个实例,造成歧义 . 这实际上是来自类型形式的OO多重继承的经典diamond problem .您可以获得的最好的方法是创建一个提供相关实例的包装器类型:
但是,在那时,你真的没有比仅仅编写自己的
showWrapper :: Wrapper w => w -> String
功能更有优势 .