{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FlexibleContexts #-} module OverlappingSpecificsError where class EqM a b where (===) :: a -> b -> Bool instance {-# OVERLAPPABLE #-} Eq a => EqM a a where a === b = a == b instance {-# OVERLAPPABLE #-} EqM a b where a === b = False aretheyreallyeq :: (Eq a, Eq b) => Either a b -> Either a b -> Bool aretheyreallyeq (Left a1) (Right b2) = a1 == b2 aretheyeq :: (Eq a, Eq b) => Either a b -> Either a b -> Bool aretheyeq (Left a1) (Right b2) = a1 === b2 Neither aretheyreallyeq or aretheyeq compile, but the error for aretheyreallyeq makes sense to me, and also tells me that aretheyeq should not give an error: One of the instances that GHCi suggests are possible for EqM in aretheyeq should be impossible due to the same error on aretheyreallyeq. What's going on?The point is, GHCi insists that both of the instances of EqM are applicable in aretheyeq. But a1 is of type a and b2 is of type b, so in order for the first instance to be applicable, it would have to have the types a and b unify.But this should not be possible, since they are declared as type variables at the function signature (that is, using the first EqM instance would give rise to the function being of type Either a a -> Either a a -> Bool, and the error in aretheyreallyeq tells me that GHCi will not allow that (which is what I expected anyway).Am I missing something, or is this a bug in how overlapping instances with multi-parameter type classes are checked?I am thinking maybe it has to do with the fact that a and b could be further instantiated later on to the point where they are equal, outside aretheyeq, and then the first instance would be valid? But the same is true for aretheyreallyeq. The only difference is that if they do not ever unify we have an option for aretheyeq, but we do not for aretheyreallyeq. In any case, Haskell does not have dynamic dispatch for plenty of good and obvious reasons, so what is the fear in committing to the instance that will always work regardless of whether later on a and b are unifiable? Maybe there is some way to present this that would make choosing the instance when calling the function possible in some way?It is worth noting that if I remove the second instance, then the function obviously still does not compile, stating that no instance EqM a b can be found. So if I do not have that instance, then none works, but when that one works, suddenly the other does too and I have an overlap? Smells like bug to me miles away.
4 Réponses :
Dans ce cas, mon L'interprétation est que cela dit que cela donne certains choix pour A et B, il existe potentiellement plusieurs instances de correspondance différentes. P> P> artheyeallyeq code> échoue car il existe deux variables de type différentes dans la portée. Dans
a1 :: a code>, et
b2 :: b code>, il n'y a pas de méthode de comparaison des valeurs de types potentiellement différents (comme ceci est la façon dont ils sont déclarés), donc cela échoue. Cela n'a rien à voir avec aucune des extensions ou pragmes activées bien sûr. P>
Aretheyeq code> échoue car il y a deux instances que pourrait em> match, pas que ils font définitivement. Je ne sais pas quelle version de GHC que vous utilisez, mais voici le message d'exception que je vois et il semble être assez clair pour moi: p>
Je sais déjà tout ça. Mais je pense que vous avez échoué à comprendre mon point. La deuxième instance est EQM A A A code>. Pour que cela correspond à cela compte tenu de la signature de
arrétheyeallyeq code>, il devrait forcer
arrétheyeallyeq code> pour avoir des variables de type
a code> et
b < / code> être égal. Si vous avez pu faire cela, alors
arrétheyeq code> serait également de type chèque. Ce qui est prouvé, au fait, si vous supprimez simplement l'autre instance de
EQM CODE>: cette fonction ne compile toujours pas, car l'instance qu'il affirme se chevaucherait n'est pas une instance valide. L'instance
EQM A A A code> ne pourrait jamais correspondre.
La correspondance d'instance sur les variables génériques fonctionne de cette façon afin d'empêcher certains scénarios potentiellement déroutants (et dangereux).
Si le compilateur a donné à votre intuition et a choisi l'instance EQM AB CODE> lors de la compilation
arrétheyeq code> (car
A code> et
b code> ne l'unifiez pas nécessairement, comme vous le dites), alors l'appel suivant: p>
dummy :: a -> b -> Bool
dummy a b = aretheyeq (Left a) (Right b)
Je pense que vous n'avez pas non plus compris mon point. Je ne veux vraiment pas que l'instance code> eqm a une instance code> soit utilisée dans arrétheyeq code>. Jamais. Donc, vous faites fondamentalement mon point pour moi: il n'y a pas de dépêche dynamique, sans deviner. Il devrait simplement choisir l'instance
EQM A B code> et être effectuée avec elle. Pourquoi est-ce même un problème que l'instance code> EQM A A code>, qui ne fonctionnerait jamais, existe?
Si j'avais la contrainte code> EQM A B code> sur la fonction, ce serait une histoire complète. Ensuite, la fonction doit compiler, aucune question posée, puis peut-être peut-être parfois utiliser l'instance EQM A A code> lorsqu'elle est appelée avec des types plus instanciés qui rendent celui-ci possible. Et dans un tel cas, le chevauchement serait vérifié de toute façon au niveau supérieur où il a été appelé. Vérifiez donc cela ici, où je n'ai pas cette contrainte sur la fonction, ne sert à rien d'autre que de rendre ma vie difficile.
"Je pense que vous n'avez pas non plus compris mon point" - je répondais en fait que dans le tout premier paragraphe: si le compilateur a toujours choisi EQM A B code> dans ces circonstances, cela conduirait à des résultats inattendus comme ça.
J'ai oublié cette première partie après avoir lu le reste. Je ne peux pas comprendre pourquoi cela serait contraire à l'intuition. L'utilisation de === code> est à l'intérieur de la fonction. Le type de fonction n'a pas de contrainte code> EQM code>. Ainsi, lorsque vous l'utilisez, vous n'avez même pas besoin de savoir qu'il utilise
EQM code>. Lors de la programmation, vous savez que
A code> et
B code> sont des variables de type différentes et que l'instance
EQM A A code> ne serait jamais utilisée. Quelle confusion est là pour avoir eu? Si vous avez essayé de deviner à quel point chaque fonction utilisée est mise en œuvre, vous seriez condamné. La fonction devrait décrire ce qu'elle fait.
Il y a une différence entre une explication formelle et une attente intuitive. L'espoir que l'instance "droite" soit choisie par magie est assez courante que les concepteurs de compilateur choisissent de rejeter cette situation au prix de la rejet des programmes corrects. C'est le choix fondamental de la conception de la langue. Chaque fonctionnalité fait ce compromis.
Ce n'est pas un bug en sens de travail exactement comme documenté . Commençant par p>
Supposons maintenant que, dans un module client, nous recherchons une instance de la contrainte cible
(c Ty1 .. TYN) code>. La recherche fonctionne comme ceci: p> blockQuote>
La première étape de la recherche instances candidates em> fonctionne comme prévu;
EQM A B CODE> est le seul candidat et donc le candidat principal. Mais la dernière étape est p>
Déterminez maintenant toutes les instances ou des contraintes données intégrées, qui unifient avec la contrainte cible, mais ne le correspondent pas. Ces instances non candidates peuvent correspondre lorsque la contrainte cible est ultérieure instanciée. Si tous sont des instances de haut niveau incohérentes, la recherche réussit, renvoyant le premier candidat. Sinon, la recherche échoue. P> blockQuote>
Le
EQM A A A CODE> L'instance tombe dans cette catégorie et n'est pas incohérente, la recherche échoue. Et vous pouvez obtenir le comportement que vous voulez en le marquant comme
{- # incoherent # -} code> au lieu de superposable. P>
J'avais lu cette page un million de fois avant de poster cela, mais je n'avais pas compris que le dernier bit a été référé précisément à cela. Merci d'avoir fait remarquer cela. Jamais tout à fait compris ce que cela signifiait par «cela unifie, mais cela ne correspond pas». Je ne pense pas que le marquant aussi incohérent fonctionnera pour ma demande réelle, comme dans ce cas, j'ai deux cas qui sont en quelque sorte symétrique, mais chacun d'entre eux entrez sur l'autre sur différentes situations, en trouvant une instanciation potentielle non assortie comme dans le cas j'ai présenté. Je devrais les marquer à la fois aussi incohérents et cela ne fonctionnerait probablement pas?
Cela finira probablement de «si tous les candidats restants sont incohérents, la recherche réussit, renvoyant un candidat survivant arbitraire» dans au moins certaines situations. Que ce soit acceptable pour votre cas d'utilisation, je ne sais pas.
Pour compléter davantage la réponse d'Alexey, qui m'a vraiment donné l'indicateur sur ce que je devrais faire pour atteindre le comportement que je voulais, j'ai compilé l'exemple minimum de travail suivant d'une situation légèrement différente de mon cas de réel usage (ce qui doit faire avec si vous supprimez le et ensuite, le code fonctionne exactement comme vous l'attendez: Donc, ma conclusion est la suivante: le Je pense toujours que c'est un Bit Absurd que Haskell n'a pas de moyen plus naturel et sans danger de dire au compilateur d'arrêter de m'inquiéter de moi étant potentiellement confus et de faire ce qui est évidemment la seule vérification de type au problème ... p> p> existentiensquantifantification code>):
{- # incoherent # -} code> annotations, vous obtenez exactement les mêmes erreurs de compilation que dans mon exemple original sur code> Createbar code> et
createbar2 code>, et le point est identique: dans
Createbar code> Il est clair que la seule instance appropriée est la seule instance appropriée. Deuxièmement, alors que dans
CreateBar2 code> Le seul approprié est le seul est le premier, mais Haskell refuse de compiler en raison de cette confusion apparente qu'il pourrait créer lors de l'utilisation,
incoherent code>. p>
getta Createbar code> retourne
gauche "ABC" < / code> alors que
g eTA CreateBar2 code> renvoie
Droite "DEFDEF" CODE>, qui est exactement la seule chose qui pourrait arriver dans un système de type sensible. P>
IncoHerent CODE> ANNOTATION est précisément pour permettre ce que je voulais faire depuis le début sans haskell se plaignant des instances potentiellement déroutantes et de prendre la seule personne qui a du sens. Un doute reste quant à savoir si
incohérent code> peut le faire afin que des instances qui restent en effet se chevauchent, même après avoir pris en compte tout ce qui compilait, en utilisant un arbitraire (qui est évidemment mauvais et dangereux). Donc, un corollaire à ma conclusion est: Utilisez uniquement
incohérent code> lorsque vous avez absolument besoin de et êtes absolument convaincu qu'il n'y a en effet qu'une seule instance valide. P>
Vous voudrez peut-être comparer les types
A code> et
B code> pour égalité à runtime i> au lieu de compiler heure. Si tel est le cas, vous pouvez utiliser
typéable A, typable B code> comme une contrainte supplémentaire. Notez que si vous le faites, vous devrez satisfaire les contraintes à chaque appel.