82 votes

Instances orphelines à Haskell

Lors de la compilation de mon Haskell application avec l' -Wall option, GHC se plaint des orphelins cas, par exemple:

Publisher.hs:45:9:
    Warning: orphan instance: instance ToSElem Result

Le type de la classe ToSElem n'est pas la mienne, elle est définie par HStringTemplate.

Maintenant, je sais comment résoudre ce problème (déplacer une instance de la déclaration dans le module où le Résultat est déclaré), et je sais pourquoi GHC préfèrent éviter orphelins cas, mais je crois encore à ma façon, c'est mieux. Je ne m'inquiète pas si le compilateur est importunée - plutôt que moi.

La raison pour laquelle je tiens à déclarer mes ToSElem cas dans le module de publication est parce que c'est le module de publication qui dépend de la HStringTemplate, pas les autres modules. Je suis d'essayer de maintenir une séparation de préoccupations et d'éviter d'avoir chaque module dépendent HStringTemplate.

J'ai pensé que l'un des avantages de Haskell des classes de type, comparé par exemple à Java les interfaces, c'est qu'elles sont ouvertes plutôt que fermées et, par conséquent, les instances n'ont pas à être déclarées dans le même lieu que le type de données. GHC l'avis semble l'ignorer.

Donc, ce que je cherche est soit de la validation de certains que ma réflexion est bonne, et que je voudrais être justifiée en ignorant/suppression de cet avertissement, ou un argument plus convaincant contre faire les choses à ma manière.

89voto

Yitz Points 3262

Je comprends pas pourquoi vous voulez le faire, mais malheureusement, il est peut-être qu'une illusion que Haskell classes semblent être "ouvert" dans le sens que vous dites. Beaucoup de personnes estiment que la possibilité de faire cela est un bug dans le Haskell spécification, pour des raisons que je vais expliquer ci-dessous. De toute façon, si ce n'est pas vraiment approprié pour l'instance, vous devez être déclarée dans le module où la classe est déclarée ou dans le module où le type est déclaré, qui est probablement un signe que vous devriez être à l'aide d'un newtype ou certains autres wrapper autour de votre type.

Les raisons de l'orphelin instances doivent être évités exécuter beaucoup plus profond que de la commodité du compilateur. Ce sujet est assez controversée, comme vous pouvez le voir à partir d'autres réponses. Pour équilibrer le débat, je vais vous expliquer le point de vue que l'on ne doit jamais, jamais, écrire orphelin cas, je pense que c'est l'opinion de la majorité entre les expérimentés Haskellers. Mon opinion personnelle est quelque part au milieu, que j'expliquerai à la fin.

Le problème vient du fait que lorsque plus d'un exemple, la déclaration existe pour la même catégorie et le type, il n'y a pas de mécanisme standard Haskell pour spécifier les utiliser. Plutôt, le programme est rejeté par le compilateur.

Le plus simple effet de cette est que vous pourriez avoir à merveille un programme de travail qui s'arrêtent subitement de la compilation en raison d'un changement de quelqu'un d'autre fait, dans certains loin de dépendance de votre module.

Encore pire, il est possible pour un programme de travail pour commencer à s'écraser lors de l'exécution à cause d'un lointain changement. Vous pourriez être en utilisant une méthode que vous êtes en supposant que vient à partir d'un certain exemple, la déclaration, et il pourrait en silence sera remplacée par une autre instance qui est juste assez différents à cause de votre programme pour démarrer inexplicablement s'écraser.

Les gens qui veulent des garanties que ces problèmes ne sera jamais survenir doivent suivre la règle que si n'importe qui, n'importe où, n'a jamais déclaré une instance d'une certaine classe d'un certain type, aucune autre instance ne doit être déclarée à nouveau dans un programme écrit par n'importe qui. Bien sûr, il y a la solution d'utiliser un newtype à déclarer une nouvelle instance, mais qui est toujours d'au moins un inconvénient mineur, et parfois majeur. Ainsi, dans ce sens, ceux qui écrivent des orphelins cas intentionnellement sont plutôt impoli.

Donc, ce qui devrait être fait à propos de ce problème? L'anti-orphelin-instance camp dit que le GHC avertissement est un bug, il doit être une erreur qui rejette toute tentative de déclarer un orphelin de l'instance. En attendant, nous devons faire preuve d'auto-discipline et de les éviter à tout prix.

Comme vous l'avez vu, il y a ceux qui ne sont pas tellement préoccupés par ces problèmes potentiels. Ils encouragent en fait l'utilisation de telles instances comme un outil pour la séparation des préoccupations, comme vous le suggérez, et dire que l'on devrait assurez-vous juste au cas par cas qu'il n'y a pas de problème. J'ai été incommodés assez de fois par d'autres personnes de l'orphelin instances pour être convaincu que cette attitude est trop cavalier.

Je pense que la bonne solution serait d'ajouter une extension à Haskell d'importation du mécanisme de contrôle de l'importation de cas. Qui ne résoudrait pas les problèmes de fond, mais cela donnerait un peu d'aide en faveur de la protection de nos programmes contre les dommages de l'orphelin instances qui existent déjà dans le monde. Et puis, avec le temps, je pourrais devenir convaincu que, dans certains cas limités, peut-être un orphelin exemple, pourrait ne pas être si mauvais. (Et que la tentation est la raison pour laquelle certains dans la lutte anti-orphelin-instance de camp sont à l'opposé de ma proposition.)

Ma conclusion de tout cela est que, au moins pour le moment, je conseille vivement d'éviter de déclarer tout orphelin cas, à être attentif aux autres, si pour aucune autre raison. Utiliser un newtype.

42voto

yairchu Points 9694

Aller de l'avant et de supprimer cette alerte!

Vous êtes en bonne compagnie. Conal t-il dans "TypeCompose". "chp-mtl" et "chp-transformateurs" de le faire, le "contrôle-monade-exception-mtl" et "contrôle-monade-exception-monadsfd" faire, etc.

btw, vous le savez probablement déjà, mais pour ceux qui n'en ont pas et de tomber de votre question de recherche:

{-# OPTIONS_GHC -fno-warn-orphans #-}

Edit:

Je reconnais les problèmes que Yitz mentionné dans sa réponse comme les vrais problèmes. Cependant je ne vois pas en utilisant des orphelins cas, d'un problème, et j'ai essayer de choisir le "moins de tous les maux", qui est à mon humble avis, à utiliser avec prudence orphelin instances.

J'ai seulement utilisé un point d'exclamation dans ma courte réponse, car votre question montre que vous êtes déjà bien au courant 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 Yitz mentionne (ne sachant pas quelle instance est choisi) pourrait être résolu dans un "holistique" de la programmation système où:

  • Vous n'êtes pas d'édition de simples fichiers texte primitivement, mais sont plutôt assistée par l'environnement (par exemple la complétion de code seulement de suggérer des choses de types pertinents, etc)
  • Le "niveau inférieur" de la langue n'a pas de soutien particulier pour le type de classes, et au lieu des tables de fonction sont passés explicitement
  • Mais, le "niveau supérieur" environnement de programmation affiche le code de façon semblable à la façon dont Haskell est présenté maintenant (généralement, vous ne verrez pas les tables de fonction passés), et sélectionne le type explicite-classes pour vous quand ils sont évidentes (par exemple tous les cas de Foncteur un seul choix) et quand il y a plusieurs exemples (compression de la liste Applicative ou de liste de monade Applicative, Premier/Dernier/ascenseur peut-être Monoïde) il vous permet de choisir l'instance à utiliser.
  • Dans tous les cas, même lorsque l'instance a été choisi pour vous automatiquement, l'environnement permet facilement de voir de quelle instance a été utilisé, avec une interface simple (un lien hypertexte ou passez la souris de l'interface ou de quelque chose)

De retour de la fantasy monde (ou je l'espère à l'avenir), maintenant: je vous recommande d'essayer d'éviter orphelin instances tout en continuant à utiliser lorsque vous avez réellement besoin" de

36voto

augustss Points 201

Orphelin cas, c'est une nuisance, mais à mon avis, ils sont parfois nécessaires. J'ai souvent combiner les bibliothèques où un type vient d'une bibliothèque et d'une classe à une autre bibliothèque. Bien sûr, les auteurs de ces bibliothèques ne peut pas fournir de cas pour toutes les combinaisons imaginables de types et de classes. J'ai donc à leur fournir, et donc ils sont orphelins.

L'idée que vous devez envelopper le type dans un nouveau type lorsque vous avez besoin de fournir une instance est une idée théorique de mérite, mais c'est juste trop fastidieux dans de nombreuses circonstances; c'est le genre d'idée que de mettre de l'avant par des gens qui n'écrivent pas de code Haskell pour une vie. :)

Donc, aller de l'avant et de fournir des orphelins cas. Ils sont inoffensifs.
Si vous pouvez planter ghc orphelin cas alors que c'est un bug et doit être signalé comme tel. (Le bug de ghc a/a propos de ne pas détecter plusieurs instances n'est pas difficile à résoudre.)

Mais il faut savoir qu'à un certain moment dans l'avenir, quelqu'un d'autre pourrait ajouter les quelque instance que vous avez déjà, et vous pouvez obtenir une (compilation) erreur.

17voto

sclv Points 25335

Dans ce cas, je pense que l'utilisation de telles instances est très bien. La règle générale de pouce pour moi, c'est -- vous pouvez définir une instance si vous "propre" de la typeclass ou si vous "propre" le type de données (ou une composante de celui-ci -- c'est à dire, un exemple, Peut-être, MyData est très bien, au moins parfois). À l'intérieur de ces contraintes, où vous décidez de mettre de l'instance est de votre propre entreprise.

Il y a une autre exception -- si vous ne détiennent la typeclass ou le type de données, mais sont la production d'un binaire et non pas une bibliothèque, alors que c'est bien trop.

5voto

Trystan Spangler Points 979

(Je sais je suis en retard à la fête, mais cela peut être encore être utile à d’autres)

Vous pouvez conserver les instances orphelines dans leur propre module, puis si quiconque importe ce module que c’est précisément parce qu’ils en ont besoin et qu’ils puissent éviter les important s’ils causent des problèmes.

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