0

我有一个类型类:

class Wrapper w where
    open :: w -> Map String Int
    close :: Map String Int -> w

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

newtype FlapMap = Flap (Map String Int)
newtype SnapMap = Snap (Map String Int)
...

并且仍然具有可在任何类型的类上运行的功能。

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

我想做这个:

instance (Wrapper wrapper) => Show wrapper where
    show w = show $ toList $ open w

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

通过FlexibleInstancesand UndecidableInstances,GHC 让我认为我的实例声明适用于所有内容,因为据称它与Show我的代码和GHC.Show. HaskellWiki 和 StackOverflow 的回答者和 HaskellWiki 说服我OverlappingInstances不是很安全,而且可能令人困惑。GHC 甚至不建议这样做。

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

  2. 我可以避免OverlappingInstances使用我newtype的 s 吗?

4

1 回答 1

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

如果您孤立地考虑FooorBar类型类,则上述定义是有意义的。任何实现类型类的东西都应该“免费”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

但是,在这一点上,您实际上并没有比仅仅编写自己的showWrapper :: Wrapper w => w -> String函数获得太多优势。

于 2016-09-06T08:09:21.907 回答