56 votes

Comment puis-je écrire, "si la classe de type a, alors a est aussi une instance de b par cette définition."

J'ai un typeclass MyClass, et il y a une fonction qui produit un String. Je veux utiliser cela implique une instance d' Show, de sorte que je peux passer des types de la mise en œuvre de MyClass de show. Jusqu'à présent j'ai,

class MyClass a where
    someFunc :: a -> a
    myShow :: a -> String 

instance MyClass a => Show a where
    show a = myShow a

qui donne l'erreur Constraint is no smaller than the instance head. j'ai aussi essayé,

class MyClass a where
    someFunc :: a -> a
    myShow :: a -> String

instance Show (MyClass a) where
    show a = myShow a

qui donne l'erreur, ClassMyClass' utilisé comme un type".

Comment puis-je exprimer correctement cette relation en Haskell? Merci.

Je dois ajouter que je souhaite en faire le suivi des instances spécifiques d' MyClass qui émettent des chaînes spécifiques en fonction de leur type. Par exemple,

data Foo = Foo
data Bar = Bar

instance MyClass Foo where
    myShow a = "foo"

instance MyClass Bar where
    myShow a = "bar"

main = do
    print Foo
    print Bar

64voto

Edward Kmett Points 18369

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.

29voto

John L Points 20989

(Edit: en laissant le corps à la postérité, mais passer à la fin de la solution réelle)

Dans la déclaration, instance MyClass a => Show a, nous allons examiner le message d'erreur "Contrainte n'est pas plus petite que l'instance de la tête." La contrainte est la classe du type de contrainte à gauche de '=>", et dans ce cas - MyClass a. "L'instance de la tête" est tout ce qui est après la classe, vous êtes en train de rédiger une instance pour, dans ce cas - a (à droite de l' Show). L'un des type de règles d'inférence dans GHC exige que la contrainte ont de moins en moins les constructeurs et les variables de la tête. Cela fait partie de ce que l'on appelle le"Paterson Conditions'. Ils existent comme une garantie que le type de la vérification se termine.

Dans ce cas, la contrainte est exactement la même que la tête, c'est à dire a, de sorte qu'il ne réussit pas ce test. Vous pouvez supprimer le Paterson condition vérifie en permettant UndecidableInstances, le plus probable avec l' {-# LANGUAGE UndecidableInstances #-} pragma.

Dans ce cas, vous êtes essentiellement à l'aide de votre classe MyClass comme un typeclass synonyme de l' Show classe. La création de la classe des synonymes comme c'est l'un des canoniques utilise pour la UndecidableInstances extension, de sorte que vous pouvez l'utiliser en toute sécurité ici.

'Indécidable" signifie que GHC ne peut pas prouver le typage va se terminer. Bien qu'il semble dangereux, le pire qui peut arriver à partir de l'activation UndecidableInstances est que le compilateur de la boucle, en finissant par après épuisement de la pile. Si ça compile, alors il est évident que le typage résilié, donc pas de problèmes. Le dangereux extension est IncoherentInstances, qui est aussi mauvais que cela puisse paraître.

Edit: un autre problème rendue possible par cette approche découle de cette situation:

instance MyClass a => Show a where

data MyFoo = MyFoo ... deriving (Show)

instance MyClass MyFoo where

Maintenant, il y a deux instances de Spectacle pour MyFoo, celui de l'découlant de la clause et l'un de MyClass instances. Le compilateur ne peut pas décider de les utiliser, de sorte qu'il va renflouer avec un message d'erreur. Si vous êtes à essayer de faire de l' MyClass des instances de types vous n'avez pas de contrôle qui ont déjà Show des cas, vous devrez utiliser les newtypes pour masquer le Montrent les instances. Même sans MyClass instances aura toujours des conflits, car la définition de l' instance MyClass => Show a , parce que la définition fournit en fait une œuvre pour tous les possible a (le contexte vérifier intervient au plus tard; sa non-sélection de l'instance)

Donc, c'est le message d'erreur et comment UndecidableInstances fait aller loin. Malheureusement, il a beaucoup de mal à utiliser dans le code actuel, pour des raisons Edward Kmett explique. L'impulsion initiale a été d'éviter de spécifier un Show contrainte lorsqu'il y a déjà un MyClass contrainte. Étant donné que, ce que je voudrais faire est de simplement utiliser myShow de MyClass au lieu de show. Vous n'aurez pas besoin d' Show de contrainte.

5voto

dave4420 Points 31298

Je pense qu'il serait préférable de le faire dans l'autre sens:

 class Show a => MyClass a where
    someFunc :: a -> a

myShow :: MyClass a => a -> String
myShow = show
 

2voto

Raoul Supercopter Points 2943

Vous pouvez le compiler, mais pas avec Haskell 98, vous devez activer certaines extensions de langage:

 {-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
-- at the top of your file
 

Les instances flexibles permettent la déclaration de contexte dans une instance. Je ne connais pas vraiment la signification de UndecidableInstances, mais je voudrais éviter autant que possible.

1voto

Wei Hu Points 1337

Vous trouverez peut-être des réponses intéressantes dans une question SO: http://stackoverflow.com/questions/2877304/linking-combining-type-classes-in-haskell/

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X