如何为我自己的类型类中的所有类型声明类型类的实例(如Show)?

Lei*_*rts 0 haskell typeclass newtype overlapping-instances

我有一个类型类:

class Wrapper w where
    open :: w -> Map String Int
    close :: Map String Int -> w
Run Code Online (Sandbox Code Playgroud)

它看起来并不是很有用,但是我用它强烈地(不仅仅是一个type同义词)来区分语义上不同的Map String Ints:

newtype FlapMap = Flap (Map String Int)
newtype SnapMap = Snap (Map String Int)
...
Run Code Online (Sandbox Code Playgroud)

并且仍然具有可在任何类型的类上运行的函数.

  1. 有没有更好的方法来做这种区分(可能没有Wrapper实例样板)?

我想做这个:

instance (Wrapper wrapper) => Show wrapper where
    show w = show $ toList $ open w
Run Code Online (Sandbox Code Playgroud)

而不是编写许多样板Show实例.

通过FlexibleInstancesUndecidableInstances,GHC引导我到一个点,它认为我的实例声明适用于所有内容,因为它据称与Show我的代码中的其他实例冲突GHC.Show.HaskellWiki和StackOverflow的回答者和HaskellWiki说服我OverlappingInstances不太安全,可能会让人困惑.GHC甚至没有建议.

  1. 为什么GHC首先抱怨不知道Show Int要选择哪个fx实例(为什么它不查看我在编译时给出的约束?)然后,被告知实例可能重叠,突然知道该怎么做?

  2. 我可以避免让OverlappingInstances我的newtypes?

Ale*_*ing 5

如果没有OverlappingInstances,你可以做到这一点,正如你所提到的,这是不可预测的.无论如何,它对你没有帮助,所以如果没有包装类型,你几乎无法做到这一点.

当然,这是相当不满意的,为什么会这样呢?正如您已经确定的那样,GHC在选择实例时不会查看实例上下文,只会查看实例头.为什么?好吧,请考虑以下代码:

class Foo a where
  fooToString :: a -> String

class Bar a where
  barToString :: a -> String

data Something = Something

instance Foo Something where
  fooToString _ = "foo something"

instance Bar Something where
  barToString _ = "bar something"

instance Foo a => Show a where
  show = fooToString

instance Bar a => Show a where
  show = barToString
Run Code Online (Sandbox Code Playgroud)

如果您单独考虑FooBar类型类,则上述定义是有意义的.任何实现Foo类型类的东西都应该Show"免费" 获得一个实例.不幸的是,Bar实例也是如此,所以现在你有两个有效的实例show Something.

由于类型类总是打开的(事实上,Show如果你能够为它定义自己的实例,它必须是开放的),不可能知道某人不会出现并添加他们自己的类似实例,然后在你的数据类型上创建一个实例,造成歧义.这实际上是来自类型形式的OO多重继承的经典钻石问题.

您可以获得的最好的方法是创建一个提供相关实例的包装器类型:

{-# LANGUAGE ExistentialQuantification #-}

data ShowableWrapper = forall w. Wrapper w => ShowableWrapper w

instance Show ShowableWrapper where
  show (ShowableWrapper w) = show . toList $ open w
Run Code Online (Sandbox Code Playgroud)

不过,在那时,你真的没有比仅仅编写自己的showWrapper :: Wrapper w => w -> String功能更有优势了.