2016-09-06 79 views
0

我有一个类型类:如何在我自己的类型类中为所有类型声明类型类型的实例(如显示)?

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

它看起来并不非常有用,我却用它强烈(不只是一个type代名词)的Map String Int S语义不同品种区别:

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甚至没有建议。

  • 为什么GHC第一抱怨不知道该选择哪个FX Show Int的实例(所以为什么不看我给在编译时?约束),然后,被告知实例可能重叠,突然知道该怎么办?

  • 我可以避免允许OverlappingInstances与我的newtype s吗?

  • +0

    “导出Show”与你想实现的不同吗? – chi

    +0

    是的。我不只是想'FlapMap(fromList [...])'。 –

    +0

    我不会重写'Show'实例,因为它对于创建输出非常有帮助,在ghci中显示它并将其复制到测试用例 - 特别是与漂亮的打印库结合使用时。我宁愿创建一个UserFriendlyShow类型类 - 但是无论如何你需要OverlappingInstances。 – epsilonhalbe

    回答

    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 
    

    如果孤立地考虑了FooBar类型类,上面的定义是有意义的。任何实施Foo类型类别的应用程序都应该“免费”获得Show实例。不幸的是,Bar实例也是如此,所以现在您有两个有效实例为show Something

    由于typeclass始终打开(如果能够为其定义自己的实例,实际上Show必须打开),因此不可能知道有人不会出现并添加自己的类似实例,然后创建一个您的数据类型的实例,造成歧义。这实际上是来自类型类OO多重继承的经典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功能。