Lors de la compilation de mon application Haskell avec l' -Wall
option, GHC se plaint des instances orphelines, par exemple:
Publisher.hs:45:9:
Warning: orphan instance: instance ToSElem Result
La classe de type ToSElem
n'est pas la mienne, elle est définie par HStringTemplate .
Maintenant, je sais comment résoudre ce problème (déplacez la déclaration d'instance dans le module où Result est déclaré), et je sais pourquoi GHC préférerait éviter les instances orphelines , mais je pense toujours que mon chemin est meilleur. Peu m'importe si le compilateur est incommodé - plutôt lui que moi.
La raison pour laquelle je souhaite déclarer mes ToSElem
instances dans le module Publisher est que c'est le module Publisher qui dépend de HStringTemplate, et non des autres modules. J'essaie de maintenir une séparation des préoccupations et d'éviter que chaque module dépende de HStringTemplate.
Je pensais que l'un des avantages des classes de types de Haskell, par rapport par exemple aux interfaces de Java, est qu'elles sont ouvertes plutôt que fermées et que les instances n'ont donc pas à être déclarées au même endroit que le type de données. Le conseil de GHC semble être d'ignorer cela.
Donc, ce que je recherche, c'est soit une confirmation que ma pensée est saine et que je serais justifié d'ignorer / supprimer cet avertissement, ou un argument plus convaincant contre faire les choses à ma façon.
Réponses:
Je comprends pourquoi vous voulez faire cela, mais malheureusement, ce n'est peut-être qu'une illusion que les classes Haskell semblent être "ouvertes" comme vous le dites. Beaucoup de gens pensent que la possibilité de faire cela est un bogue dans la spécification Haskell, pour des raisons que je vais expliquer ci-dessous. Quoi qu'il en soit, si ce n'est vraiment pas approprié pour l'instance, vous devez être déclaré soit dans le module où la classe est déclarée, soit dans le module où le type est déclaré, c'est probablement un signe que vous devriez utiliser un
newtype
ou un autre wrapper autour de votre type.Les raisons pour lesquelles les instances orphelines doivent être évitées sont bien plus profondes que la commodité du compilateur. Ce sujet est plutôt controversé, comme vous pouvez le voir à partir d'autres réponses. Pour équilibrer la discussion, je vais expliquer le point de vue qu'il ne faut jamais, jamais, écrire des instances orphelines, ce qui, je pense, est l'opinion majoritaire parmi les Haskeller expérimentés. Ma propre opinion se situe quelque part au milieu, ce que j'expliquerai à la fin.
Le problème vient du fait que lorsqu'il existe plus d'une déclaration d'instance pour la même classe et le même type, il n'y a pas de mécanisme dans Haskell standard pour spécifier lequel utiliser. Au contraire, le programme est rejeté par le compilateur.
L'effet le plus simple de cela est que vous pourriez avoir un programme parfaitement fonctionnel qui arrêterait soudainement la compilation à cause d'un changement que quelqu'un d'autre fait dans une dépendance éloignée de votre module.
Pire encore, il est possible qu'un programme fonctionnel commence à planter au moment de l'exécution en raison d'un changement distant. Vous pourriez utiliser une méthode dont vous supposez qu'elle provient d'une certaine déclaration d'instance, et elle pourrait être silencieusement remplacée par une instance différente qui est juste assez différente pour que votre programme se mette inexplicablement en panne.
Les personnes qui veulent des garanties que ces problèmes ne leur arriveront jamais doivent suivre la règle selon laquelle si quelqu'un, n'importe où, a déjà déclaré une instance d'une certaine classe pour un certain type, aucune autre instance ne doit plus jamais être déclarée dans un programme écrit par n'importe qui. Bien sûr, il existe une solution de contournement consistant à utiliser a
newtype
pour déclarer une nouvelle instance, mais c'est toujours au moins un inconvénient mineur, et parfois majeur. Donc, dans ce sens, ceux qui écrivent intentionnellement des instances orphelines sont plutôt impolis.Alors, que faut-il faire pour résoudre ce problème? Le camp anti-instance orpheline dit que l'avertissement GHC est un bogue, il doit s'agir d'une erreur qui rejette toute tentative de déclaration d'une instance orpheline. En attendant, nous devons faire preuve d'autodiscipline et les éviter à tout prix.
Comme vous l'avez vu, il y a ceux qui ne s'inquiètent pas autant de ces problèmes potentiels. Ils encouragent en fait l'utilisation d'instances orphelines comme outil de séparation des préoccupations, comme vous le suggérez, et disent qu'il faut simplement s'assurer au cas par cas qu'il n'y a pas de problème. J'ai été assez souvent incommodé par les instances orphelines d'autres personnes pour être convaincu que cette attitude est trop cavalière.
Je pense que la bonne solution serait d'ajouter une extension au mécanisme d'importation de Haskell qui contrôlerait l'importation des instances. Cela ne résoudrait pas complètement les problèmes, mais cela aiderait à protéger nos programmes contre les dommages causés par les instances orphelines qui existent déjà dans le monde. Et puis, avec le temps, je pourrais devenir convaincu que dans certains cas limités, peut-être qu'une instance orpheline pourrait ne pas être si grave. (Et cette tentation même est la raison pour laquelle certains membres du camp anti-orphelin sont opposés à ma proposition.)
Ma conclusion à partir de tout cela est qu'au moins pour le moment, je vous conseillerais fortement d'éviter de déclarer des instances orphelines, d'être prévenant envers les autres si ce n'est pour une autre raison. Utilisez un
newtype
.la source
Allez-y et supprimez cet avertissement!
Vous êtes en bonne compagnie. Conal le fait dans "TypeCompose". "chp-mtl" et "chp-transformers" le font, "control-monad-exception-mtl" et "control-monad-exception-monadsfd" le font, etc.
btw vous le savez probablement déjà, mais pour ceux qui ne le font pas et trébuchent sur votre question sur une recherche:
{-# OPTIONS_GHC -fno-warn-orphans #-}
Éditer:
Je reconnais que les problèmes mentionnés par Yitz dans sa réponse sont de vrais problèmes. Cependant, je ne vois pas non plus l'utilisation d'instances orphelines comme un problème, et j'essaie de choisir le "moindre de tous les maux", qui est à mon humble avis d'utiliser prudemment les instances orphelines.
Je n'ai utilisé qu'un point d'exclamation dans ma réponse courte parce que votre question montre que vous êtes déjà bien conscient des problèmes. Sinon, j'aurais été moins enthousiaste :)
Un peu de diversion, mais ce que je crois est la solution parfaite dans un monde parfait sans compromis:
Je crois que les problèmes mentionnés par Yitz (ne sachant pas quelle instance est choisie) pourraient être résolus dans un système de programmation "holistique" où:
De retour du monde fantastique (ou, espérons-le, du futur), maintenant: je recommande d'essayer d'éviter les instances orphelines tout en continuant de les utiliser lorsque vous "avez vraiment besoin"
la source
Les instances orphelines sont une nuisance, mais à mon avis elles sont parfois nécessaires. Je combine souvent des bibliothèques où un type provient d'une bibliothèque et une classe d'une autre bibliothèque. Bien entendu, on ne peut pas s'attendre à ce que les auteurs de ces bibliothèques fournissent des instances pour chaque combinaison imaginable de types et de classes. Je dois donc les fournir, et ils sont donc orphelins.
L'idée que vous devez envelopper le type dans un nouveau type lorsque vous devez fournir une instance est une idée qui a un mérite théorique, mais c'est trop fastidieux dans de nombreuses circonstances; c'est le genre d'idée proposée par les gens qui n'écrivent pas de code Haskell pour gagner leur vie. :)
Alors allez-y et fournissez des instances orphelines. Ils sont inoffensifs.
Si vous pouvez planter ghc avec des instances orphelines, c'est un bogue qui doit être signalé comme tel. (Le bogue que ghc avait / a à propos de ne pas détecter plusieurs instances n'est pas si difficile à corriger.)
Mais sachez que dans le futur, quelqu'un d'autre pourrait ajouter une instance comme vous l'avez déjà, et vous pourriez obtenir une erreur (lors de la compilation).
la source
(Ord k, Arbitrary k, Arbitrary v) ⇒ Arbitrary (Map k v)
utilisation de QuickCheck.Dans ce cas, je pense que l'utilisation d'instances orphelines est bien. La règle générale pour moi est la suivante: vous pouvez définir une instance si vous "possédez" la classe de types ou si vous "possédez" le type de données (ou un composant de celui-ci - c'est-à-dire qu'une instance pour Maybe MyData convient également, au moins parfois). Dans ces limites, l'endroit où vous décidez de placer l'instance est votre propre entreprise.
Il y a une autre exception: si vous ne possédez ni la classe de types ni le type de données, mais que vous produisez un binaire et non une bibliothèque, alors c'est bien aussi.
la source
(Je sais que je suis en retard à la fête mais cela peut être utile aux autres)
Vous pouvez conserver les instances orphelines dans leur propre module, alors si quelqu'un importe ce module, c'est spécifiquement parce qu'il en a besoin et il peut éviter de les importer s'ils posent des problèmes.
la source
Dans ce sens, je comprends les bibliothèques WRT de position du camp d'instances anti-orphelines, mais pour les cibles exécutables, les instances orphelines ne devraient-elles pas convenir?
la source