Show的实例,用于另一个类的实例 [英] Instance of Show for instances of another class

查看:48
本文介绍了Show的实例,用于另一个类的实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下课程:

class SappState s where
    getTable   :: s -> SymbolTable
    getStack   :: s -> Stack Scope
    getScopeId :: s -> ScopeNum
    getAst     :: s -> Program
    putTable   :: SymbolTable -> s -> s
    putStack   :: Stack Scope -> s -> s
    putScopeId :: ScopeNum    -> s -> s
    putAst     :: Program     -> s -> s

而且我总是显示表示该 class 实例的 data 数据,并定义其中的功能.因此,我用以下代码对其进行了通用化:

And I always show the datas that instance this class with the functions defined in it. So I generalized it with the following code:

instance (SappState s) => Show s where
    show st = showT ++ showS ++ showI ++ showA
        where
            showT = getTable st ++ "\n"
            showS = "Scope Stack:\n"  ++ getStack st ++ "\n"
            showI = "Scope Number:\t" ++ getScopeId st ++ "\n"
            showA = getAst st ++ "\n"

但是GHC给我以下错误:

but GHC gives me the following error:

SappMonad.hs:87:27:
    Illegal instance declaration for `Show s'
      (All instance types must be of the form (T a1 ... an)
       where a1 ... an are *distinct type variables*,
       and each type variable appears at most once in the instance head.
       Use -XFlexibleInstances if you want to disable this.)
    In the instance declaration for `Show s'

我应该只使用 FlexibleInstances 编译指示吗?我真的不明白它的作用,如果它是正确的方法,还是我应该放弃对 Show 实例进行泛化的想法.

Should I just use the FlexibleInstances pragma? I don't really understand what it does and if it's the right way or if I should drop the idea of generalizing the Show instance.

修改

我激活了编译指示,它出现了这个新错误:

I activated the pragma and it has this new error:

SappMonad.hs:88:10:
    Constraint is no smaller than the instance head
      in the constraint: SappState s
    (Use -XUndecidableInstances to permit this)
    In the instance declaration for `Show s'

我也激活了 UndecidableInstances ,现在一个简单的 Deriving Show 将会中断:

I activated UndecidableInstances too and now a simple deriving Show will break:

data Architecture = Arch
    { archName :: String
    , types    :: Map.Map DataType Bytes
    } deriving (Show)

引发以下错误:

SappMonad.hs:39:17:
    Overlapping instances for Show (Map.Map DataType Bytes)
      arising from the 'deriving' clause of a data type declaration
    Matching instances:
      instance (Show k, Show a) => Show (Map.Map k a)
        -- Defined in `containers-0.5.0.0:Data.Map.Base'
      instance SappState s => Show s -- Defined at SappMonad.hs:89:10
    When deriving the instance for (Show Architecture)


第二次修改

我在该主题中搜索了更多内容,并发现了 OverlappingInstances ,添加了编译指示,它可以编译,我认为它可以正常工作.但是我觉得这些语言扩展我走得太远了.我如何才能停止使用其中的一些功能,但仍然获得相同的功能?

I googled some more in the subject and found OverlappingInstances, added the pragma and it compiles and I think it works correctly. But I feel I'm going too far with these language extensions. How could I stop using some of them and still get the same functionality?

推荐答案

不幸的是,没有真正干净的解决方案.

Unfortunately there's no really clean solution for this problem.

您当前方法的最大缺点是,如果程序中的任何地方都有任何类似的 Show 的自动"实例,它们将完全停止工作,并且您会收到有关重复实例".

The biggest downside of your current approach is if there is any similar "automatic" instance of Show anywhere in the program, they will both stop working completely and you'll get an error about "duplicate instances".

基本问题是,实例解析首先要与实例声明的右侧匹配,然后才能起作用.如果存在匹配项,则该机制将提交该声明,然后继续尝试解决左侧所需的任何内容.

The fundamental issue is that instance resolution works by first matching against the right-hand side of instance declarations first. If there's a match, the machinery commits to that declaration and then goes and tries to resolve anything required by the left-hand side.

因此,在您的情况下,为任何 Foo 搜索 Show Foo 的实例将无条件地与您的实例匹配,如果找不到,解析将失败 SappState Foo 实例.

So in your case, searching for an instance for Show Foo for any Foo will match against your instance unconditionally, and then resolution will fail if it doesn't find a SappState Foo instance.

OverlappingInstances 对此稍有缓解,因为它将首先查找更具体的实例.因此, Show Int 将使用普通实例进行解析,因为它专门提到了 Int .但是,如果有类似 instance SappState2 a =>的内容,在范围内也显示一个,那么任何没有更具体实例的 Foo 都将导致重复的实例错误.

OverlappingInstances mitigates this slightly, as it will look for more specific instances first. So Show Int will resolve using the normal instance because that specifically mentions Int. But if there was something like instance SappState2 a => Show a also in scope, then any Foo without a more specific instance will lead to a duplicate instance error.

我建议让 SappState 的实现者手工编写一个 Show 实例.您可以通过提供实用程序功能 showSappState :: SappState a =>来降低成本.a->字符串,这样他们的实例就可以了

I would recommend getting implementers of SappState to write a Show instance by hand. You can mitigate the cost by providing a utility function showSappState :: SappState a => a -> String so that their instances can be just

instance Show Foo where
    show = showSappState

[ Show 的适当实现还有其他几种方法,但适用相同的通用方法]

[A proper implementation of Show has a few more methods, but the same general approach applies]

如果要确保 SappState 的所有实例也是 Show 的实例,则可以使用超类来强制执行此操作:

If you want to be sure that all instances of SappState are also instances of Show, you can use a superclass to enforce this:

class Show a => SappState a where
    ...

实际上有一些建议可以自动实现超类,这将完美地解决您的问题,但是GHC尚未实现任何东西.一个示例是 IntrinsicSuperclasses .

There are actually a few proposals around to make it possible to automatically implement superclasses, which would solve your problem perfectly, but there's nothing implemented in GHC yet. One example is IntrinsicSuperclasses.

出于完整性考虑,值得一提的是 UndecidableInstances 有点危险,因为它可能导致实例解析无法终止.由于问题已经解决了图灵的问题,所以保证终止的规则一定有点保守,在某些情况下需要将其关闭.

For completeness, it's worth mentioning that UndecidableInstances is somewhat dangerous because it can lead to instance resolution not terminating. The rules that guarantee that it will terminate are necessarily somewhat conservative since the problem is Turing complete, and in some cases like this they need to be turned off.

实例SappState a =&>的问题;显示是将显示a 约束简化为 SappState a 约束,并且在搜索新实例时没有明显的进度"和旧的一样大.想象一下,如果有人还写了 instance Show a =>会发生什么情况?SappState a .

The problem with instance SappState a => Show a is that it reduces a Show a constraint to a SappState a constraint, and there's no obvious "progress" being made as the new instance being searched for is the same size as the old one. Imagine what could happen if someone also wrote instance Show a => SappState a.

这篇关于Show的实例,用于另一个类的实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆