33 votes

Le choix entre une classe et un record

D'où la question: quels principes doit-on suivre lors du choix entre l'utilisation d'une classe ou à l'aide d'un enregistrement (avec polymorphes champs) ?

Tout d'abord, nous savons que les classes et les enregistrements sont essentiellement équivalentes (puisque de Base, les classes d'obtenir délactosé les dictionnaires, qui sont juste des enregistrements). Néanmoins, il existe des différences: les cours sont transmis implicitement, les dossiers doivent être explicites.

En regardant un peu plus loin, les classes sont vraiment utiles pour:

  1. nous avons de nombreuses représentations de "la même chose", et
  2. en utilisation réelle, la représentation est utilisée peut être déduite.

Les Classes sont difficiles à faire quand nous avons (jusqu'à polymorphisme paramétrique) une seule représentation de nos données, mais nous avons plusieurs instances. Cela conduit à la syntaxiques bruit de devoir utiliser newtype pour ajouter des tags (qui n'existent que dans notre code, nous savons que ces balises sont effacées au moment de l'exécution) si nous ne voulons pas tourner sur toutes sortes de gênant extensions (c'est à dire le chevauchement et/ou indécidable cas).

Bien sûr, les choses deviennent boueux: que faire si je veux avoir des contraintes sur mes types? Prenons un exemple concret:

class (Bounded i, Enum i) => Partition a i where
    index :: a -> i

J'ai pu tout aussi bien fait

data Partition a i = Partition { index :: a -> i}

Mais maintenant, j'ai perdu mes contraintes, et je vais avoir à les ajouter à des fonctions spécifiques au lieu.

Existe-il des lignes directrices de conception qui serait m'aider?

6voto

sclv Points 25335

J'ai tendance à ne voir aucun problème à seulement besoin de contraintes sur les fonctions. La question est, je suppose, que la structure de données ne sont plus les modèles précisément ce que vous avez l'intention de faire. D'autre part, si vous pensez à elle comme une structure de données d'abord et avant tout, alors que ce devrait avoir moins d'importance.

Je me sens comme je n'ai pas forcément toujours avoir une bonne idée sur la question, et c'est aussi vague que peut-être, mais ma règle d'or a tendance à être que typeclasses sont des choses qu'obéir à des lois (ou modèle de la signification), et les types de données sont des choses qui codent pour une certaine quantité d'informations.

Lorsque nous voulons couche de comportement est un processus complexe, j'ai trouvé que typeclasses commencer séduisante, mais peut devenir pénible rapidement et de passer à un dictionnaire de passage rend les choses plus simple. Qui est-à-dire que, lorsque nous voulons implémentations pour être interopérable, alors nous devrions revenir à un uniforme de type dictionnaire.


C'est de prendre les deux, en pleine expansion un peu sur un exemple concret, mais encore juste une sorte de filer des idées...

Supposons que l'on veut modéliser des distributions de probabilité sur le corps des réels. Deux les représentations naturelles viennent à l'esprit.

A) Typeclass-driven

class PDist a where
        sample :: a -> Gen -> Double

B) Dictionnaire-driven

data PDist = PDist (Gen -> Double)

Les anciens nous permet de faire

data NormalDist = NormalDist Double Double -- mean, var
instance PDist NormalDist where...

data LognormalDist = LognormalDist Double Double
instance PDist LognormalDist where...

Ce dernier nous permet de faire

mkNormalDist :: Double -> Double -> PDist...
mkLognormalDist :: Double -> Double -> PDist...

Dans le premier cas, nous pouvons écrire

data SumDist a b = SumDist a b
instance (PDist a, PDist b) => PDist (SumDist a b)...

dans ces derniers, nous pouvons écrire tout simplement

sumDist :: PDist -> PDist -> PDist

Alors, quels sont les avantages et les inconvénients? Typeclass-driven nous permet de spécifier quelles distributions nous sommes donné. La différence est que nous avons à construire une algèbre de distributions explicitement, y compris de nouveaux types de leurs combinaisons. Piloté par les données ne nous permet pas de restreindre les distributions que nous sommes donné (ou même si ils sont bien formés), mais en retour, nous pouvons faire tout ce que le diable nous voulons.

En outre, nous pouvons écrire un parseDist :: String -> PDist relativement facilement, mais nous devons passer par une certaine angoisse de faire l'équivalent pour la typeclass approche.

C'est donc, en un sens, l'tapé/non statique/dynamique compromis à un autre niveau. Nous pouvons lui donner une touche bien, et soutiennent que la typeclass, ainsi que les lois algébriques, précise la sémantique d'une distribution de probabilité. Et le PDist type peut en effet être une instance de la PDist typeclass. En attendant, nous pouvons nous résigner à l'aide de la PDist type (plutôt que typeclass) presque partout, alors que la pensée de ce que l'iso à la tour d'instances et de types de données nécessaires pour utiliser le typeclass plus "riche."

En fait, on peut même définir de base PDist fonction en termes de typeclass fonctions. c'est à dire mkNormalPDist m v = PDist (sample $ NormalDist m v) il y a Donc beaucoup de place dans la conception d'espace pour glisser entre les deux représentations que nécessaire...

4voto

Matt Fenwick Points 17097

Note: je ne suis pas sûr que je comprends l'OP exactement. Suggestions et commentaires pour l'amélioration apprécié!


Arrière-plan:

Quand j'ai d'abord appris à propos typeclasses en Haskell, la règle générale de pouce, j'ai ramassé est que, en comparaison avec Java-comme des langues:

  • typeclasses sont semblables à des interfaces
  • data sont similaires aux classes

Voici un autre DONC, la question et la réponse qui décrivent des lignes directrices pour l'utilisation d'interfaces (aussi certains inconvénients de l'interface sur la consommation). Mon interprétation:

  • dossiers/Java-classes sont ce que quelque chose est
  • interfaces/typeclasses sont les rôles qu'une concrétion pouvez remplir
  • plusieurs, sans rapport avec concrétions peuvent remplir le même rôle

Je parie que vous savez déjà tout cela.


Les lignes directrices j'essaie de suivre mon propre code sont:

  • typeclasses sont pour des abstractions
  • les enregistrements sont de concrétions

Donc, dans la pratique, cela signifie:

  • laissez les besoins de données de déterminer les enregistrements
  • laissez-le code du client de déterminer ce que les interfaces sont -- les clients doivent dépendre des abstractions, et ainsi conduire à la création et la conception de typeclasses

Exemple:

typeclass Show, avec la fonction show :: (Show s) => s -> String: pour les données qui peuvent être représentées en tant que String.

  • les clients veulent juste pour transformer les données en chaînes
  • les clients ne se soucient pas ce que les données (concrétion) est, seulement attention, il peut être représenté comme une chaîne de caractères
  • rôle de la mise en œuvre de données: string-identifiés
  • ceci ne peut être réalisé sans un typeclass -- chaque type de données nécessiterait une fonction de conversion avec un nom différent, ce qu'est une douleur à traiter!

1voto

yairchu Points 9694

Type-classes peuvent parfois apporter un autre type de sécurité (par exemple Ord avec Data.Map.union). Si vous avez des circonstances similaires, où choisir le type de classes peuvent aider votre type de sécurité, puis le type d'utilisation des classes.

Je vais vous présenter un autre exemple où je pense que le type de classes ne serait pas fournir plus de sécurité:

class Drawing a where
    drawAsHtml :: a -> Html
    drawOpenGL :: a -> IO ()

exampleFunctionA :: Drawing a => a -> a -> Something
exampleFunctionB :: (Drawing a, Drawing b) => a -> b -> Something

Il n'y a rien exampleFunctionA il peut faire et exampleFunctionB ne pouvait pas le faire (j'ai du mal à expliquer pourquoi, les idées sont les bienvenues).

Dans ce cas je ne vois pas l'avantage de l'utilisation d'un type de classe.

(Modifié suivant les commentaires de Jacques et de question de missingo)

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