Je souhaite à fortement en désaccord avec l'cassé solutions posées à ce jour.
instance MyClass a => Show a where
show a = myShow a
En raison de la façon dont instance de résolution fonctionne, c'est très dangereux instance en cours d'exécution autour de!
Exemple de résolution d'un produit par un patron sur le côté droit de chaque instance de l' =>
, sans tenir aucun compte de ce qui est sur la gauche de l' =>
.
Si aucun de ces cas de chevauchement, c'est une belle chose. Cependant, ce que vous dites ici est "Ici est une règle que vous devez utiliser pour CHAQUE Spectacle instance. Lorsque vous êtes invité pour un spectacle instance pour n'importe quel type, vous aurez besoin d'une instance de Maclasse, alors allez l'obtenir, et ici est la mise en œuvre." -- une fois que le compilateur s'est engagé à le choix de l'utilisation de votre instance, (juste en raison du fait que 'a' unifie avec tout) il n'a pas de chance de tomber en arrière et utiliser toutes les autres instances!
Si vous allumez {-# LANGUAGE OverlappingInstances, IncoherentInstances #-}
, etc. pour faire de la compilation, vous obtenez not-so-subtil échecs quand vous allez à écrire des modules qui importer le module qui fournit cette définition et de la nécessité d'utiliser n'importe quel autre Spectacle instance. En fin de compte, vous serez en mesure d'obtenir ce code à compiler avec assez d'extensions, mais malheureusement il ne fera pas ce que vous pensez qu'il devrait faire!
Si vous pensez à ce sujet, étant donné:
instance MyClass a => Show a where
show = myShow
instance HisClass a => Show a where
show = hisShow
qui devrait le compilateur choisir?
Votre module ne peut définir l'un de ces, mais à la fin le code de l'utilisateur à importer un tas de modules, pas seulement le vôtre. Aussi, si un autre module définit
instance Show HisDataTypeThatHasNeverHeardOfMyClass
le compilateur pourrait être bien à l'intérieur de ses droits à ignorer son exemple et essayez d'utiliser le vôtre.
La bonne réponse, malheureusement, est de faire deux choses.
Pour chaque individu instance de Maclasse vous pouvez définir une instance de Spectacle avec la très mécanique définition
instance MyClass Foo where ...
instance Show Foo where
show = myShow
C'est assez malheureux, mais fonctionne bien quand il ya seulement quelques cas de MyClass à l'examen.
Lorsque vous avez un grand nombre de cas, la manière d'éviter la duplication (pour quand la classe est beaucoup plus compliqué que de show) est à définir.
newtype WrappedMyClass a = WrapMyClass { unwrapMyClass :: a }
instance MyClass a => Show (WrappedMyClass a) where
show (WrapMyClass a) = myShow a
Cela fournit le newtype comme un véhicule par exemple l'expédition. et puis
instance Foo a => Show (WrappedFoo a) where ...
instance Bar a => Show (WrappedBar a) where ...
est sans équivoque, car le type "patrons" pour WrappedFoo a
et WrappedBar a
sont disjoints.
Il y a un certain nombre d'exemples de cet idiome à courir dans les de la base
package.
Dans Le Contrôle.Applicatifs il y a des définitions WrappedMonad
et WrappedArrow
pour cette même raison.
Idéalement, vous seriez capable de dire:
instance Monad t => Applicative t where
pure = return
(<*>) = ap
mais effectivement ce que cette instance est en train de dire que tous les Applicatifs doivent être établies par trouver une instance de Monad, puis envoi à elle. Ainsi, alors qu'il aurait l'intention de dire que chaque Monade est Applicative (par le moyen de l'implication-comme =>
lit) de ce qu'il dit, c'est que chaque Applicative est une Monade, car le fait d'avoir une instance de la tête " t " correspond à n'importe quel type. Dans beaucoup de façons, la syntaxe de "instance" et de "classe" définitions est à l'envers.