Show instance for instances of another class

I have the following class:

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 

And I always show data this instance of this class with the functions defined in it. Therefore, 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" 

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' 

Should I use the FlexibleInstances pragma? I really donโ€™t understand what it is doing, and if itโ€™s right, or I should abandon the idea of โ€‹โ€‹generalizing the show instance.


change

I activated the pragma and got 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' 

I activated UndecidableInstances too, and now the deriving Show break will break:

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

produces the following error:

 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 ka) -- 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) 

second edit

I searched for something else in the topic and found OverlappingInstances , added a pragma, and it compiles, and I think that it works correctly. But I feel that I'm moving too far along these language extensions. How can I stop using some of them and still get the same features?

+6
source share
1 answer

Unfortunately, there is no really clean solution to this problem.

The biggest drawback of your current approach is the presence of any similar "automatic" instance of Show anywhere in the program, both of them will stop working completely, and you will get a "duplicate instances" error message.

The main problem is that instance resolution works first, first matching with the right side of the instance declaration. If there is a match, the machine agrees to accept this expression, and then goes and tries to resolve everything that is required by the left side.

So, in your case, finding an instance for Show Foo for any Foo will unconditionally match your instance, and then permission will not be executed if it does not find an instance of SappState Foo .

OverlappingInstances mitigates this a bit since it will first look for more specific instances. This way, Show Int will be resolved using a regular instance, since it mentions Int . But if there was something like instance SappState2 a => Show a , then any Foo without a more specific instance will result in a duplicate instance error.

I would recommend that SappState artists record the Show instance manually. You can reduce costs by providing the showSappState :: SappState a => a -> String utility function so that their instances can be simply

 instance Show Foo where show = showSappState 

[There are several more methods in the correct implementation of Show , but the same general approach applies)

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

 class Show a => SappState a where ... 

In fact, there are several suggestions that allow you to automatically implement superclasses that will perfectly solve your problem, but so far nothing has been implemented in GHC. One example is IntrinsicSuperclasses .

For completeness, it is worth mentioning that UndecidableInstances somewhat dangerous, because this can lead to the fact that the resolution of the instance does not stop. The rules that guarantee that it will end are necessarily somewhat conservative, since the problem in Turing is over, and in some cases, like this, they need to be disabled.

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

+4
source

Source: https://habr.com/ru/post/975597/


All Articles