Les traits de Rust semblent au moins superficiellement similaires aux classes de types de Haskell, mais j'ai vu des gens écrire qu'il y avait des différences entre eux. Je me demandais exactement quelles étaient ces différences.
157
class Functor f where fmap :: (a -> b) -> (f a -> f b)
; un exemple de ce dernier estclass Bounded a where maxBound :: a
.std::default
), et les traits multiparamètres fonctionnent en quelque sorte (y compris un analogue des dépendances fonctionnelles), même si AFAIK il faut contourner le premier paramètre privilégié. Pas de HKT cependant. Ils sont sur la liste de souhaits pour un futur lointain mais pas encore à l'horizon.Réponses:
Au niveau de base, il n'y a pas beaucoup de différence, mais ils sont toujours là.
Haskell décrit les fonctions ou les valeurs définies dans une classe de types comme des «méthodes», tout comme les traits décrivent les méthodes POO dans les objets qu'ils renferment. Cependant, Haskell les traite différemment, les traitant comme des valeurs individuelles plutôt que de les épingler à un objet comme la POO le conduirait à le faire. Il s'agit de la différence la plus évidente au niveau de la surface.
La seule chose que Rust ne pouvait pas faire pendant un certain temps était les traits typés d'ordre supérieur , tels que les fameuses classes
Functor
et lesMonad
classes de types.Cela signifie que les traits Rust ne pouvaient décrire que ce qu'on appelle souvent un «type concret», en d'autres termes, un sans argument générique. Haskell pouvait dès le départ créer des classes de types d'ordre supérieur qui utilisent des types similaires à la façon dont les fonctions d'ordre supérieur utilisent d'autres fonctions: en utilisant l'une pour en décrire une autre. Pendant un certain temps, cela n'a pas été possible dans Rust, mais depuis que les éléments associés ont été mis en œuvre, ces caractéristiques sont devenues courantes et idiomatiques.
Donc, si nous ignorons les extensions, elles ne sont pas exactement les mêmes, mais chacune peut se rapprocher de ce que l'autre peut faire.
Il est également mentionné, comme indiqué dans les commentaires, que GHC (le compilateur principal de Haskell) prend en charge d'autres options pour les classes de types, y compris les classes de types à paramètres multiples (c'est -à- dire de nombreux types impliqués) et les dépendances fonctionnelles , une belle option qui permet des calculs au niveau du type et mène aux familles de types . À ma connaissance, Rust n'a ni funDeps ni familles de types, bien que cela puisse se produire à l'avenir. †
Dans l'ensemble, les traits et les classes de types ont des différences fondamentales qui, en raison de la façon dont ils interagissent, les font agir et semblent assez similaires à la fin.
† Un bel article sur les classes de types de Haskell (y compris les classes les plus typées) peut être trouvé ici , et le chapitre Rust by Example sur les traits peut être trouvé ici
la source
Je pense que les réponses actuelles négligent les différences les plus fondamentales entre les traits Rust et les classes de type Haskell. Ces différences sont liées à la manière dont les traits sont liés aux constructions de langage orientées objet. Pour plus d'informations à ce sujet, consultez le livre Rust .
Une déclaration de trait crée un type de trait . Cela signifie que vous pouvez déclarer des variables d'un tel type (ou plutôt des références du type). Vous pouvez également utiliser des types de trait comme paramètres sur des fonctions, des champs de structure et des instanciations de paramètres de type.
Une variable de référence de trait peut, au moment de l'exécution, contenir des objets de types différents, tant que le type d'exécution de l'objet référencé implémente le trait.
Ce n'est pas ainsi que fonctionnent les classes de types. Les classes de type ne créent aucun type , vous ne pouvez donc pas déclarer de variables avec le nom de classe. Les classes de types agissent comme des limites sur les paramètres de type, mais les paramètres de type doivent être instanciés avec un type concret, pas la classe de type elle-même.
Vous ne pouvez pas avoir une liste de différents éléments de types différents qui implémentent la même classe de type. (Au lieu de cela, les types existentiels sont utilisés dans Haskell pour exprimer une chose similaire.) Note 1
Les méthodes de trait peuvent être distribuées dynamiquement . Ceci est fortement lié aux choses qui sont décrites dans la section ci-dessus.
La répartition dynamique signifie que le type d'exécution de l'objet pointé par une référence est utilisé pour déterminer la méthode appelée via la référence.
Encore une fois, des types existentiels sont utilisés pour cela dans Haskell.
En conclusion
Il me semble que les traits sont à bien des égards le même concept que les classes de types. De plus, ils ont la fonctionnalité d'interfaces orientées objet.
En revanche, les classes de types de Haskell sont plus avancées. Haskell a par exemple des types plus élevés et des extensions comme des classes de types multi-paramètres.
Remarque 1 : Les versions récentes de Rust ont une mise à jour pour différencier l'utilisation des noms de trait comme types et l'utilisation des noms de trait comme limites. Dans un type de trait, le nom est précédé du
dyn
mot - clé. Voir par exemple cette réponse pour plus d'informations.la source
dyn Trait
comme une forme de typage existentiel en ce qui concerne les traits / classes de types. On peut considérerdyn
un opérateur sur les bornes les projetant vers des types, iedyn : List Bound -> Type
. En prenant cette idée de Haskell, et en ce qui concerne «vous ne pouvez pas déclarer des variables avec le nom de classe », nous pouvons le faire indirectement en Haskell:data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t
. Après avoir défini cela, nous pouvons travailler avec[D True, D "abc", D 42] :: [D Show]
.Les «traits» de Rust sont analogues aux classes de types de Haskell.
La principale différence avec Haskell est que les traits n'interviennent que pour les expressions avec notation par points, c'est-à-dire de la forme a.foo (b).
Les classes de type Haskell s'étendent aux types d'ordre supérieur. Les traits Rust ne prennent en charge que les types d'ordre supérieur car ils sont absents de l'ensemble du langage, c'est-à-dire que ce n'est pas une différence philosophique entre les traits et les classes de types
la source
Default
trait qui n'a pas de méthodes, uniquement des fonctions non associées à des méthodes.