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...