【问题标题】:How to declare instances of a typeclass (like Show) for all types in my own typeclass?如何为我自己的类型类中的所有类型声明类型类的实例(如 Show)?
【发布时间】:2016-09-07 08:55:43
【问题描述】:

我有一个类型类:

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 实例。

通过FlexibleInstancesUndecidableInstances,GHC 将我引导到它认为我的实例声明适用于所有事物的地步,因为据称它与我的代码和GHC.Show 中的其他Show 实例发生冲突。 HaskellWiki 和 StackOverflow 的回答者和 HaskellWiki 说服我 OverlappingInstances 不是很安全,可能会令人困惑。 GHC 甚至不建议这样做。

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

  2. 我可以避免允许OverlappingInstances 与我的newtypes 一起使用吗?

【问题讨论】:

  • deriving Show 与您想要实现的目标不同吗?
  • 是的。我不只是想要FlapMap (fromList [...])
  • 我不会覆盖 Show 实例,因为它对于创建输出、在 ghci 中显示并将其复制到测试用例非常有帮助——尤其是与漂亮打印库结合使用时。我宁愿创建一个 UserFriendlyShow 类型类 - 但无论如何你都需要 OverlappingInstances。

标签: haskell typeclass newtype overlapping-instances


【解决方案1】:

如果没有 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

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

由于类型类始终是开放的(事实上,Show 必须是开放的,如果你能够为它定义自己的实例),不可能知道有人不会出现并添加他们自己的类似实例,然后创建您的数据类型上的一个实例,从而产生歧义。这实际上是类型类形式的面向对象多重继承的经典 diamond problem

你能得到的最好的方法是创建一个提供相关实例的包装器类型:

{-# 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 函数相比,你真的没有太多优势。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-08-25
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-27
    • 2010-10-30
    • 1970-01-01
    相关资源
    最近更新 更多