Pendant que j'apprenais Haskell, j'ai remarqué sa classe de type , qui est censée être une grande invention originaire de Haskell.
Cependant, dans la page Wikipedia sur la classe de type :
Le programmeur définit une classe de type en spécifiant un ensemble de noms de fonctions ou de constantes, ainsi que leurs types respectifs, qui doivent exister pour chaque type appartenant à la classe.
Ce qui me semble assez proche de l'interface de Java (citant la page Interface de Wikipédia (Java) ):
Une interface dans le langage de programmation Java est un type abstrait utilisé pour spécifier une interface (au sens générique du terme) que les classes doivent implémenter.
Ces deux aspects semblent assez similaires: la classe de type limite le comportement d'un type, tandis que l'interface limite le comportement d'une classe.
Je me demande quelles sont les différences et les similitudes entre la classe de type dans Haskell et l'interface en Java, ou peut-être sont-elles fondamentalement différentes?
EDIT: J'ai remarqué que même haskell.org admet qu'ils sont similaires . S'ils sont si similaires (ou le sont-ils?), Alors pourquoi la classe de type est-elle traitée avec un tel battage médiatique?
PLUS EDIT: Wow, tant de bonnes réponses! Je suppose que je vais devoir laisser la communauté décider laquelle est la meilleure. Cependant, en lisant les réponses, tous semblent simplement dire qu ' "il y a beaucoup de choses que typeclass peut faire alors que l'interface ne peut pas ou doit faire face aux génériques" . Je ne peux pas m'empêcher de me demander, y a-t-il quelque chose que les interfaces peuvent faire alors que les classes de types ne le peuvent pas? De plus, j'ai remarqué que Wikipédia affirme que typeclass a été inventé à l'origine dans l'article de 1989 * «Comment rendre le polymorphisme ad hoc moins ad hoc», alors que Haskell est toujours dans son berceau, tandis que le projet Java a été lancé en 1991 et publié pour la première fois en 1995 Alors peut-être qu'au lieu que typeclass soit similaire aux interfaces, c'est l'inverse, que les interfaces ont été influencées par typeclass?Y a-t-il des documents / papiers qui soutiennent ou réfutent cela? Merci pour toutes les réponses, elles sont toutes très instructives!
Merci pour toutes les contributions!
Réponses:
Je dirais qu'une interface est un peu comme une classe de type
SomeInterface t
où toutes les valeurs ont le typet -> whatever
(oùwhatever
ne contient past
). En effet, avec le type de relation d'héritage en Java et dans les langages similaires, la méthode appelée dépend du type d'objet sur lequel elle est appelée, et rien d'autre.Cela signifie qu'il est vraiment difficile de faire des choses comme
add :: t -> t -> t
avec une interface, où elle est polymorphe sur plus d'un paramètre, car il n'y a aucun moyen pour l'interface de spécifier que le type d'argument et le type de retour de la méthode sont du même type que le type de l'objet sur lequel il est appelé (c'est-à-dire le type "self"). Avec Generics, il existe en quelque sorte des moyens de simuler cela en créant une interface avec un paramètre générique qui devrait être du même type que l'objet lui-même, comme comment leComparable<T>
faire, où vous êtes censé utiliserFoo implements Comparable<Foo>
pour que lecompareTo(T otherobject)
type de soit le typet -> t -> Ordering
. Mais cela oblige toujours le programmeur à suivre cette règle, et provoque également des maux de tête lorsque les gens veulent créer une fonction qui utilise cette interface, ils doivent avoir des paramètres de type générique récursifs.De plus, vous n'aurez pas des choses comme
empty :: t
parce que vous n'appelez pas une fonction ici, donc ce n'est pas une méthode.la source
Ce qui est similaire entre les interfaces et les classes de types, c'est qu'elles nomment et décrivent un ensemble d'opérations associées. Les opérations elles-mêmes sont décrites par leurs noms, entrées et sorties. De même, il peut y avoir de nombreuses implémentations de ces opérations qui différeront probablement dans leur mise en œuvre.
Avec cela à l'écart, voici quelques différences notables:
En général, je pense qu'il est juste de dire que les classes de types sont plus puissantes et flexibles que les interfaces. Comment définiriez-vous une interface pour convertir une chaîne en une valeur ou une instance du type d'implémentation? Ce n'est certes pas impossible, mais le résultat ne serait ni intuitif ni élégant. Avez-vous déjà souhaité qu'il soit possible d'implémenter une interface pour un type dans une bibliothèque compilée? Ces deux méthodes sont faciles à réaliser avec les classes de types.
la source
Les classes de types ont été créées comme une manière structurée d'exprimer le «polymorphisme ad hoc», qui est essentiellement le terme technique pour les fonctions surchargées . Une définition de classe de type ressemble à ceci:
Cela signifie que, lorsque vous utilisez appliquer la fonction
foo
à certains arguments d'un type qui appartiennent à la classeFoobar
, il recherche une implémentationfoo
spécifique à ce type et l'utilise. Ceci est très similaire à la situation avec la surcharge d'opérateurs dans des langages comme C ++ / C #, sauf plus flexible et généralisé.Les interfaces servent un objectif similaire dans les langages OO, mais le concept sous-jacent est quelque peu différent; Les langages OO sont livrés avec une notion intégrée de hiérarchies de types que Haskell n'a tout simplement pas, ce qui complique les choses à certains égards car les interfaces peuvent impliquer à la fois une surcharge par sous-typage (c'est-à-dire, appeler des méthodes sur des instances appropriées, des sous-types implémentant des interfaces que leurs supertypes font) et par répartition basée sur le type plat (puisque deux classes implémentant une interface peuvent ne pas avoir une superclasse commune qui l'implémente également). Compte tenu de l'énorme complexité supplémentaire introduite par le sous-typage, je suggère qu'il soit plus utile de penser aux classes de types comme une version améliorée de fonctions surchargées dans un langage non-OO.
Il convient également de noter que les classes de types ont des moyens d'envoi beaucoup plus flexibles - les interfaces ne s'appliquent généralement qu'à la classe unique qui les implémente, tandis que les classes de types sont définies pour un type , qui peut apparaître n'importe où dans la signature des fonctions de la classe. L'équivalent de ceci dans les interfaces OO serait de permettre à l'interface de définir des moyens de passer un objet de cette classe à d'autres classes, de définir des méthodes statiques et des constructeurs qui sélectionneraient une implémentation en fonction du type de retour requis dans le contexte d'appel, de définir des méthodes qui prenez des arguments du même type que la classe implémentant l'interface, et diverses autres choses qui ne se traduisent pas vraiment du tout.
En bref: ils servent des objectifs similaires, mais leur mode de fonctionnement est quelque peu différent, et les classes de types sont à la fois beaucoup plus expressives et, dans certains cas, plus simples à utiliser car elles travaillent sur des types fixes plutôt que sur des éléments d'une hiérarchie d'héritage.
la source
J'ai lu les réponses ci-dessus. Je sens que je peux répondre un peu plus clairement:
Une "classe de type" Haskell et une "interface" Java / C # ou un "trait" Scala sont fondamentalement analogues. Il n'y a pas de distinction conceptuelle entre eux mais il existe des différences de mise en œuvre:
la source
Dans Master minds of Programming , il y a une interview sur Haskell avec Phil Wadler, l'inventeur des classes de types, qui explique les similitudes entre les interfaces en Java et les classes de types en Haskell:
Ainsi, les classes de types sont liées aux interfaces, mais la vraie correspondance serait une méthode statique paramétrée avec un type comme ci-dessus.
la source
Regardez le discours de Phillip Wadler sur la foi, l'évolution et les langages de programmation . Wadler a travaillé sur Haskell et a été un contributeur majeur à Java Generics.
la source
Lisez Extension logicielle et intégration avec les classes de types où des exemples sont donnés sur la façon dont les classes de types peuvent résoudre un certain nombre de problèmes que les interfaces ne peuvent pas.
Les exemples énumérés dans l'article sont:
la source
Je ne peux pas parler du niveau "hype", si cela semble bien. Mais oui, les classes de types sont similaires à bien des égards. Une différence que je peux penser est que ce Haskell vous pouvez fournir un comportement pour certains de la classe de type opérations :
qui montre qu'il y a deux opérations, égales
(==)
et non égales(/=)
, pour les choses qui sont des instances de laEq
classe de type. Mais l'opération non-égale est définie en termes d'égaux (de sorte que vous n'avez qu'à en fournir un), et vice versa.Donc en Java probablement pas légal, ce serait quelque chose comme:
et la façon dont cela fonctionnerait est que vous n'avez besoin que de fournir une de ces méthodes pour implémenter l'interface. Je dirais donc que la possibilité de fournir une sorte d'implémentation partielle du comportement souhaité au niveau de l' interface est une différence.
la source
Elles sont similaires (lire: ont un usage similaire), et probablement implémentées de la même manière: les fonctions polymorphes dans Haskell prennent sous le capot une 'vtable' listant les fonctions associées à la classe de types.
Cette table peut souvent être déduite au moment de la compilation. C'est probablement moins vrai en Java.
Mais c'est un tableau de fonctions , pas de méthodes . Les méthodes sont liées à un objet, les classes de types Haskell ne le sont pas.
Voyez-les plutôt comme les génériques de Java.
la source
Comme le dit Daniel, les implémentations d'interface sont définies séparément des déclarations de données. Et comme d'autres l'ont souligné, il existe un moyen simple de définir des opérations qui utilisent le même type libre à plusieurs endroits. Il est donc facile à définir
Num
comme une classe de types. Ainsi, dans Haskell, nous obtenons les avantages syntaxiques de la surcharge d'opérateurs sans avoir réellement d'opérateurs surchargés magiques - juste des classes de types standard.Une autre différence est que vous pouvez utiliser des méthodes basées sur un type, même si vous n'avez pas encore de valeur concrète de ce type!
Par exemple
read :: Read a => String -> a
,. Donc, si vous avez suffisamment d'autres informations de type sur la façon dont vous utiliserez le résultat d'une "lecture", vous pouvez laisser le compilateur déterminer quel dictionnaire utiliser pour vous.Vous pouvez également faire des choses comme
instance (Read a) => Read [a] where...
ce qui vous permet de définir une instance de lecture pour toute liste d'éléments lisibles. Je ne pense pas que ce soit tout à fait possible en Java.Et tout cela n'est que des classes de types standard à paramètre unique sans aucune supercherie. Une fois que nous introduisons les classes de types à paramètres multiples, un tout nouveau monde de possibilités s'ouvre, et plus encore avec des dépendances fonctionnelles et des familles de types, qui vous permettent d'intégrer beaucoup plus d'informations et de calculs dans le système de types.
la source