Quelle est la différence entre les traits dans Rust et typeclasses dans Haskell?

Les Traits dans Rust semblent au moins superficiellement similaires à typeclasses dans Haskell, mais j'ai vu des gens écrire qu'il y avait quelques différences entre eux. Je me demandais exactement quelles étaient ces différences.

114
demandé sur Martin Broadhurst 2015-01-24 10:50:44

3 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 type comme des "méthodes", tout comme les traits décrivent les méthodes POO dans les objets qu'elles contiennent. 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 de niveau de surface la plus évidente.

La seule chose que Rust ne pouvait pas faire, up jusqu'à récemment, était caractères typés d'ordre supérieur , tels que les infâmes Functor et Monad typeclasses.

Cela signifie que les traits de rouille ne peuvent décrire que ce qu'on appelle souvent un "type concret", en d'autres termes, un sans argument Générique. Haskell, cependant, peut faire des classes de type 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 décrire l'autre. Nous n'avons pas pu implémenter les classes de type mentionnées ci-dessus dans Rust, car il ne supportait pas cette caractéristique qui est considérée comme fondamentale, et même essentielle, dans Haskell. Heureusement, cela a été mis en œuvre récemment avec éléments associés .* Cependant, ce N'est pas la rouille idiomatique, et est généralement considéré comme un "hack".

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 type, Y Compris multi-paramètres (c'est-à-dire de nombreux types impliqués), et dépendances fonctionnelles , un belle option qui permet des calculs au niveau du type, et mène à familles de types . À ma connaissance, la rouille n'a ni funDeps ni familles de type, bien qu'elle puisse à l'avenir.†

Dans l'ensemble, alors que les traits et les classes de type peuvent sembler équivalents à la surface, ils ont beaucoup de différences lorsqu'ils sont comparés plus profondément, lorsqu'on considère de nombreuses qualités.


* sur une note de côté, Swift, malgré ses traits, n'a pas un tel typage plus élevé mécanisme. Une future mise à jour de la langue, peut-être?

† un bel article sur les classes typeclasses de Haskell (y compris les plus typées) peut être trouvé ici, et un tout aussi bon sur les traits de Rust est conservé ici, bien que malheureusement un peu obsolète.

45
répondu AJFarmar 2017-08-15 14:51:53

Les "traits" de Rust sont analogues aux classes de type 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 de Rust ne supportent pas les types d'ordre supérieur car ils manquent de tout le langage, c'est-à-dire que ce n'est pas une différence philosophique entre les traits et les classes de type

9
répondu Anuj Gupta 2015-04-01 10:35:19

Je pense que les réponses actuelles négligent les différences les plus fondamentales entre les traits de Rust et les classes de type Haskell. Ces différences ont à voir avec la façon dont les traits sont liés aux constructions de langage orientées objet. Pour plus d'informations à ce sujet, consultez le livre Rust.

  1. 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 traits comme paramètres sur fonction, struct champs et type paramètre instanciations.

    Une variable de référence de trait peut à l'exécution contenir des objets de différents types, tant que le type d'exécution de l'objet référencé implémente le trait.

    // The shape variable might contain a Square or a Circle, 
    // we don't know until runtime
    let shape: &Shape = get_unknown_shape();
    
    // Might contain different kinds of shapes at the same time
    let shapes: Vec<&Shape> = get_shapes();
    

    Ce n'est pas ainsi que les classes de type fonctionnent. Les classes de Type ne créent aucun type , vous ne pouvez donc pas déclarer de variables avec le nom de la classe. Les classes de Type 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 le type de la classe elle-même.

    Vous ne pouvez pas avoir une liste de choses différentes de différents types 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.)

  2. Les méthodes de caractères peuvent être distribuées dynamiquement . Ceci est fortement lié à ce qui est décrit dans la puce ci-dessus.

    La répartition dynamique signifie que le type d'exécution de l'objet a points de référence est utilisé pour déterminer la méthode appelée si la référence.

    let shape: &Shape = get_unknown_shape();
    
    // This calls a method, which might be Square.area or
    // Circle.area depending on the runtime type of shape
    print!("Area: {}", shape.area());
    

    Encore une fois, les types existentiels sont utilisés pour cela dans Haskell.

En Conclusion

Il me semble que les traits sont essentiellement le même concept que les classes de type. Il plus, ils ont la fonctionnalité des interfaces orientées objet.

(Les classes de type de Haskell sont plus avancées, car Haskell a par exemple des types et des extensions plus élevés comme des classes de type multi-paramètres. Mais je pense qu'ils sont essentiellement les mêmes.)

8
répondu Lii 2018-08-06 11:21:34