2016-09-06 2 views
0

J'ai une classe de types:Comment déclarer des instances d'une classe de type (comme Show) pour tous les types dans ma propre classe de types?

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

Il ne semble pas très utile, mais je l'utiliser pour fortement (et pas seulement synonyme type) la distinction entre les variétés sémantiquement différentes de Map String Int s:

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

et ont encore des fonctions qui fonctionnent sur n'importe quel type de la classe.

  1. Y at-il une meilleure façon de faire cette distinction (peut-être sans les instances boilerplate Wrapper)?

Je veux faire:

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

au lieu d'écrire un grand nombre d'instances de passe-partout Show aussi bien.

Via FlexibleInstances et UndecidableInstances, GHC me conduit à un point où il pense ma déclaration d'instance s'applique à tout, comme elle se heurte apparemment avec les autres instances Show dans mon code et à GHC.Show. HaskellWiki et StackOverflow répondeurs et HaskellWiki me convaincre OverlappingInstances n'est pas tout à fait sûr et peut-être déroutant. GHC ne le suggère même pas.

  1. Pourquoi GHC d'abord se plaindre de ne pas savoir quelle instance de fx Show Int à ramasser (alors pourquoi il ne regarde pas la contrainte que je donne au moment de la compilation?) Puis, étant dit que les instances peuvent se chevaucher, soudainement savoir quoi faire? Puis-je éviter d'autoriser OverlappingInstances avec mes codes newtype?

+0

Est-ce que 'Deriving Show' est différent de ce que vous voulez réaliser? – chi

+0

Oui. Je ne veux pas juste 'FlapMap (fromList [...])'. –

+0

Je ne remplacerais pas l'instance de 'Show' car elle est vraiment utile pour créer une sortie, la montrer dans ghci et la copier dans un cas de test - en particulier en combinaison avec une jolie bibliothèque d'impression. Je préférerais créer une classe de caractères UserFriendlyShow - mais de toute façon, vous avez besoin de OverlappingInstances. – epsilonhalbe

Répondre

5

Vous ne pouvez pas faire cela sans OverlappingInstances, ce qui, comme vous le mentionnez, est imprévisible. Cela ne vous aidera pas du tout, donc vous ne pouvez pas faire cela du tout sans un type de wrapper.

C'est plutôt insatisfaisant, bien sûr, alors pourquoi est-ce le cas? Comme vous l'avez déjà déterminé, GHC ne regarde pas le contexte de l'instance lors de la sélection d'une instance, seulement la tête de l'instance. Pourquoi? Eh bien, considérez le code suivant:

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 

Si vous considérez les Foo ou isolément Bar classes de types, les définitions ci-dessus sens. Tout ce qui implémente la classe Foo devrait obtenir une instance Show "gratuitement". Malheureusement, la même chose est vraie pour l'instance Bar, donc maintenant vous avez deux instances valides pour show Something. Comme les classes de type sont toujours ouvertes (et en effet, Show doit être ouvert si vous êtes capable de définir vos propres instances), il est impossible de savoir que quelqu'un ne viendra pas et ajoutera sa propre instance similaire, puis créera un instance sur votre type de données, créant une ambiguïté. C'est effectivement le classique diamond problem de l'héritage multiple OO sous forme de typeclass.

Le mieux que vous pouvez obtenir est de créer un type d'emballage qui fournit les instances pertinentes:

{-# LANGUAGE ExistentialQuantification #-} 

data ShowableWrapper = forall w. Wrapper w => ShowableWrapper w 

instance Show ShowableWrapper where 
    show (ShowableWrapper w) = show . toList $ open w 

À ce moment-là, cependant, vous êtes vraiment pas obtenir beaucoup d'un avantage par rapport à écrire juste votre propre showWrapper :: Wrapper w => w -> String fonction.