Je suis un programmeur C ++ / Java et le paradigme principal que j'utilise dans la programmation quotidienne est OOP. Dans certains threads, j'ai lu un commentaire que les classes de type sont plus intuitives dans la nature que le POOP. Quelqu'un peut-il expliquer le concept de classes de type dans des mots simples de sorte qu'un gars oop comme moi puisse le comprendre? P>
6 Réponses :
Une classe de type peut être comparée au concept de «mise en œuvre» d'une interface. Si certains types de données in Haskell implémentent l'interface "show", il peut être utilisé avec toutes les fonctions qui s'attendent à un objet "Afficher". P>
Eh bien, la version courte est la suivante: Type Classes est ce que Haskell utilise pour le polymorphisme ad-hoc. strong> ... mais cela n'a probablement rien clarifié pour vous. P> Le polymorphisme devrait être un concept familier aux personnes provenant d'un antécédents d'OUP. Le point clé ici est toutefois la différence entre paramétric em> et polymorphisme ad-hoc em>. P> polymorphisme paramétrique strong> signifie des fonctions qui fonctionnent sur un type structurel qui lui-même est paramétré par d'autres types, tels qu'une liste de valeurs. Le polymorphisme paramétrique est à peu près la norme partout dans Haskell; C # et Java appellent-le polymorphisme ad-hoc fort>, d'autre part, signifie une collection de fonctions distinctes, faire différents em> (mais conceptuellement associés) en fonction des types. Contrairement au polymorphisme paramétrique, les fonctions polymorphes ad-hoc doivent être spécifiées individuellement pour chaque type possible qu'ils peuvent être utilisés. Le polymorphisme ad hoc est donc un terme généralisé pour une variété de fonctions trouvées dans d'autres langues, telles que la surcharge de fonction dans le polymorphisme de l'expédition C / C ++ ou à base de classe dans OOP. strong> P> Un important point de vente de classes de type Haskell sur d'autres formes de polymorphisme ad hoc est une plus grande flexibilité en raison de Les interfaces disponibles dans de nombreuses langues de l'OOP sont quelque peu similaires aux classes de type Haskell - vous spécifiez un groupe de noms de fonction / signatures que vous souhaitez traiter de manière polymorphe ad hoc, puis Décrivez explicitement comment différents types peuvent être utilisés avec ces fonctions. Les classes de type Haskell sont utilisées de la même manière, mais avec une plus grande flexibilité: vous pouvez écrire des signatures de type arbitraire pour les fonctions de classe de type, avec la variable de type utilisée pour la sélection d'instance apparaissant n'importe où em> vous aimez, non seulement comme le Type d'un objet em> que des méthodes sont appelées. P> Certains compilateurs HASKELLL - y compris les extensions de langue les plus populaires, GHC - offrent des classes de type encore plus puissantes, telles que Les classes de type multi-paramètres EM>, qui vous permettent de faire une répartition de la fonction polymorphe ad-hoc sur la base de plusieurs types (similaires à ce que l'appelé "multiple expédition" em> dans OOP). P> Pour essayer de vous donner un peu de la saveur de celui-ci, voici un pseudocode Vaguement Java / C # -Forded: P>
interface IApplicative<>
{
IApplicative<T> Pure<T>(T item);
IApplicative<U> Map<T, U>(Function<T, U> mapFunc, IApplicative<T> source);
IApplicative<U> Apply<T, U>(IApplicative<Function<T, U>> apFunc, IApplicative<T> source);
}
interface IReducible<>
{
U Reduce<T,U>(Function<T, U, U> reduceFunc, U seed, IReducible<T> source);
}
En plus de ce que XTOFL et CAMCCANN ont déjà écrit dans leurs excellentes réponses, une chose utile à constater lors de la comparaison des interfaces de Java avec les classes de type Haskell, c'est le suivant: P>
Les interfaces Java sont Les classes de type Haskell sont Cette ouverture de classes de type (et protocoles de Clojure, qui sont très similaires) est une propriété très utile; Il est tout à fait d'habitude qu'un programmeur Haskell propose une nouvelle abstraction et l'applique immédiatement à une gamme de problèmes impliquant des types préexistants grâce à une utilisation intelligente des classes de type. P>
Pour être juste, les classes de type étant ouverte ont également des descentes. Mais vous devez pousser le système assez loin (et peut-être utiliser des extensions de GHC) pour les courir vraiment.
Dit des descentes peuvent être ressenties facilement chaque fois que vous faites une instance orpheline. Dites au revoir à la modularité. J'évite des instances orphelines comme la peste.
en C ++ / etc, "Méthodes virtuelles" sont expédiées en fonction du type de fonctionnent différemment et peuvent faire tout ce qui "interface" peut et plus encore. Commençons par un exemple simple de quelque chose que les interfaces ne peuvent pas faire: Haskell's Ceci ne peut pas être effectué dans l'approche de C ++ et al, où la table de fonction est extraite de l'objet - ici, vous ne le faites pas même avoir l'objet pertinent jusqu'à après une différence de mise en œuvre clé à partir d'interfaces qui permettent que cela se produise, est que la table des fonctions n'est pas pointé vers l'intérieur de l'objet, il est passé séparément par le compilateur sur les fonctions appelées. p> En outre, en C ++ / etc. Lorsque l'on définit une classe, ils sont également responsables de la mise en œuvre de leurs interfaces. Cela signifie que vous ne pouvez pas simplement inventer une nouvelle interface et faire in haskell vous pouvez, et Ce n'est pas par "patching de singe" comme dans Ruby. Haskell a un bon schéma d'espacement de nom qui signifie que deux classes de type peuvent avoir une fonction du même nom et qu'un type peut toujours mettre en œuvre les deux. P> Ceci permet à HASKELL d'avoir de nombreuses classes simples comme avec la richesse des classes de type, les gens ont tendance à programmer à des types plus généraux, puis ont des fonctions plus réutilisables et car elles ont également moins de liberté lorsque Les types sont généraux, ils peuvent même produire moins de bugs! p> TLDR: classes de type == génial p> p> Ceci code> / auto code> argument implicite. (La méthode est indiquée dans une table de fonction que l'objet pointe implicitement à) les classes de type lire code> classe de type. P> lisez code> > Le type de 'code est (lire a) => chaîne -> A code>, ce qui signifie pour chaque type qui implémente le code> lecture code> classe de type, lisez code > Peut convertir une chaîne code> code> sur ce type. C'est une expédition basée sur le type de retour, impossible avec "interfaces". P> lire code> le renvoie alors comment pouvez-vous l'appeler? p> int code> ou std :: vecteur code> implémenter. P> EQ code> (types qui prennent en charge la vérification de l'égalité), Afficher code> (types pouvant être imprimés sur une chaîne code> code>), lire code> (Types qui peut être analysé à partir d'une chaîne code> code>), monoïde code> (types qui ont une opération de concaténation et un élément vide) et de beaucoup d'autres, et permettent même aux types primitifs comme Int code> pour implémenter les classes de type appropriées. P>
Eh bien, je ne pense pas que votre somme (lire "[3, 5]") code> est un excellent exemple car il fonctionne, somme (lecture "[3.1, 5.2]") / code> échoue avec une erreur (même si bien sûr somme [3.1, 5.2] code> réussit). Je soupçonne que votre exemple ne fonctionne que des règles de type défaut, qui sont pratiques, mais franchement un peu de hack.
Entier CODE> S peut être des monoïsmes de nombreuses façons. Comment fournir un sous-ensemble arbitraire d'entre eux sous forme monoïde code> des instances? Modules ML == génial.
Premièrement, je suis toujours très méfiant des affirmations que cette structure ou cette structure de programme est plus intuitive. La programmation est contre-intuitive et sera toujours parce que les gens pensent naturellement en termes de cas spécifiques plutôt que de règles générales. Changer cela nécessite une formation et une pratique, autrement appelé "Apprendre à programmer".
passer à la viande de la question, la différence clé entre OO Classes et Haskell Typeclasses est que dans OO une classe (même une classe d'interface) est à la fois un type et un modèle pour de nouveaux types (descendants). Dans HASKELL, une typlass est seulement em> un modèle pour de nouveaux types. Plus précisément, une clasteclass décrit un ensemble de types partageant une interface commune, mais il est pas lui-même un type em>. P> de sorte que le typlass "num" décrit des types numériques avec addition, opérateurs de soustraction et de multiplication. Le type "INTEGER" est une instance de "NUG", ce qui signifie que INTEGER est membre de l'ensemble des types qui implémentent ces opérateurs. P> afin que je puisse écrire une fonction de sommation avec ce type: p > sum :: [Num] -> Num
Les superclasses des contextes sont une excuse plutôt désolée pour "héritage", cependant, car elles exigent que vous écriviez manuellement des cas pour toutes les classes des parents. Sans parler de problèmes de gêners tels que "Pourquoi ne pas fonctionner une superclasse de monad" ...
Vous venez d'un monde OO avant d'apprendre Haskell, je suis venu à penser à l'héritage comme une excuse plutôt désolée pour des fonctions de première classe.
Le foncteur est une superclasse de monade. Vous utilisez simplement la mauvaise mise en œuvre de la monade.
Meilleure explication que j'ai trouvée! Merci beaucoup!
Vous pouvez avoir des listes hétérogènes: {- # langage existentiensquanification # -} code>. (En fait, cette extension de système de type spécifique est la principale raison pour laquelle j'utilise parfois HASKELL au lieu de la norme standard de manière autrement supérieure.)
avec OOP, vous héritez à la fois d'interface et de mise en œuvre. Les classes de type Haskell permettent d'être séparés. Deux types totalement non liés peuvent tous deux exposer la même interface. P>
Peut-être plus important encore, Haskell permet d'ajouter des implémentations de classe "après le fait". C'est-à-dire que je peux inventer une nouvelle catégorie de type de la mienne, puis aller et que tous les types prédéfinis standard soient des instances de cette classe. Dans une langue oo, vous ne pouvez pas facilement ajouter une nouvelle méthode à une classe existante, peu importe la manière dont cela serait utile. P>
JavaScript est l'une des langues qui vous permettent d'ajouter / prototyper facilement toutes les méthodes que vous souhaitez "classe" ou un objet instancié :)
Ouais, JavaScript est un peu inhabituel comme ça. Il n'est toujours pas particulièrement facile de trouver tous les objets i> d'un certain type et de les modifier. ;-)
@Neil Butterworth: J'ai ajouté les tags C ++ et Java afin que les personnes qui connaissent l'une de ces langues et que les classes de type Haskell peuvent les expliquer dans les termes que je peux comprendre. Pas besoin de les éditer.
Il n'y a aucun moyen de classes de type Haskell sont "plus intuitives" que oop. Le système de type de type Haskell comprend une implémentation de Prolog! (Datalog, vraiment). Et la plupart des programmeurs trouvent la programmation logique assez inindictionnelle et difficile à apprendre au début - c'est tout au sujet des preuves.
Le mécanisme d'expédition de OOP (polymorphisme, surcharge) est également un système Mini-PROG pour décrire également les types, c'est juste que Haskell le rend explicite. Les classes de type sont très similaires aux classes abstraites ou aux interfaces dans les langues que vous venez de répertorier. Haskell utilise les combinaisons de valeurs et de types de chaque argument, y compris la valeur de retour pour l'envoi, tout en examinant le type et les superypes de ce argument implicites, puis les types du reste des arguments d'expédition.