Pourquoi pas de génériques dans Go?


126

Avertissement: je ne joue avec Go que depuis un jour maintenant, il y a donc de fortes chances que j'aie raté beaucoup de choses.

Est-ce que quelqu'un sait pourquoi il n'y a pas de support réel pour les génériques / templates / whatsInAName dans Go? Il existe donc un générique map, mais qui est fourni par le compilateur, tandis qu'un programmeur Go ne peut pas écrire sa propre implémentation. Avec tous les discours sur le fait de rendre Go aussi orthogonal que possible, pourquoi puis-je UTILISER un type générique sans en CRÉER un nouveau?

Surtout en ce qui concerne la programmation fonctionnelle, il y a des lambdas, même des fermetures, mais avec un système de type statique manquant de génériques, comment écrire des fonctions génériques d'ordre supérieur comme filter(predicate, list)? OK, les listes liées et autres peuvent être faites en interface{}sacrifiant la sécurité du type.

Comme une recherche rapide sur SO / Google n'a révélé aucune idée, il semble que des génériques, voire pas du tout, seront ajoutés à Go après coup. Je fais confiance à Thompson pour faire bien mieux que les gars de Java, mais pourquoi garder les génériques à l'écart? Ou sont-ils planifiés et ne sont tout simplement pas encore mis en œuvre?


Je pense que cela vaut la peine de le souligner: l'utilisation de l'interface {} ne sacrifie pas la sécurité des types. Il s'agit d'un type et peut être asserté (non converti) en d'autres types, mais ces assertions invoquent toujours des vérifications d'exécution pour maintenir la sécurité du type.
cthom06

12
interface{}sacrifie la sécurité de type statique . Cependant, c'est une plainte quelque peu étrange à faire lorsque vous mentionnez Scheme dans le paragraphe suivant, car Scheme n'a normalement pas de vérification de type statique.
poolie

@poolie: Ce qui me préoccupe, c'est de m'en tenir à UN paradigme dans une langue. Soit j'utilise XOR de sécurité de type statique.

2
btw il est orthographié «Go», pas «GO», comme vous pouvez le voir sur golang.org. Et c'est sensible à la casse. :-)
poolie

Réponses:


78

cette réponse vous trouverez ici: http://golang.org/doc/faq#generics

Pourquoi Go n'a-t-il pas de types génériques?

Des génériques peuvent bien être ajoutés à un moment donné. Nous ne ressentons pas une urgence pour eux, même si nous comprenons que certains programmeurs le font.

Les génériques sont pratiques mais ils ont un coût en complexité dans le système de type et le temps d'exécution. Nous n'avons pas encore trouvé de design qui donne une valeur proportionnée à la complexité, même si nous continuons à y réfléchir. Pendant ce temps, les cartes et les tranches intégrées de Go, ainsi que la possibilité d'utiliser l'interface vide pour construire des conteneurs (avec unboxing explicite) signifient dans de nombreux cas qu'il est possible d'écrire du code qui fait ce que les génériques permettraient, bien que moins facilement.

Cela reste une question ouverte.


14
@amoebe, "l'interface vide", orthographié interface{}, est le type d'interface le plus basique, et chaque objet le fournit. Si vous créez un conteneur les contenant, il peut accepter n'importe quel objet (non primitif). C'est donc très similaire à un conteneur Objectsen Java.
poolie

4
Les génériques @YinWang ne sont pas si simples dans un environnement de type inféré. Plus important; interface {} n'est pas équivalent aux pointeurs void * en C. De meilleures analogies seraient les types d'identifiant System.Object de C # ou d'Objective-C. Les informations de type sont conservées et peuvent être «moulées» (affirmées, en fait) à leur type concret. Obtenez les détails ici: golang.org/ref/spec#Type_assertions
tbone

2
Le System.Object de @tbone C # (ou l'objet de Java en soi) est essentiellement ce que j'entendais par "pointeurs vides de C" (en ignorant la partie où vous ne pouvez pas faire d'arithmétique de pointeur dans ces langages). C'est là que les informations de type statique sont perdues. Un casting n'aidera pas beaucoup car vous obtiendrez une erreur d'exécution.
Ian le

1
Les modèles de @ChristopherPfohl D semblent avoir un peu moins de temps de compilation, et normalement vous ne générez pas plus de code avec des modèles que vous ne le feriez normalement autrement (vous pourriez, en fait, vous retrouver avec moins de code selon les circonstances).
Cubic

3
@ChristopherPfohl Je pense que seuls les génériques Java ont un problème de boxe / unboxing pour les types primitifs? Le générique réifié de C # n'a pas le problème.
ca9163d9

32

Aller 2

Il existe un projet de conception pour les génériques sur https://blog.golang.org/go2draft .

Aller 1

Russ Cox, l'un des vétérans du Go, a écrit un article de blog intitulé The Generic Dilemma , dans lequel il demande

… Voulez-vous des programmeurs lents, des compilateurs lents et des binaires gonflés, ou des temps d'exécution lents?

Les programmeurs lents étant le résultat de l'absence de génériques, les compilateurs lents sont causés par des génériques comme C ++ et les temps d'exécution lents découlent de l'approche boxing-unboxing utilisée par Java.

La quatrième possibilité non mentionnée dans le blog est la voie C #. Générer le code spécialisé comme en C ++, mais au moment de l'exécution quand cela est nécessaire. Je l'aime vraiment, mais Go est très différent de C # donc ce n'est probablement pas du tout applicable ...

Je devrais mentionner que l'utilisation de la technique de programmation générique populaire Java 1.4 en go qui interface{}souffre des mêmes problèmes que le boxing-unboxing (car c'est ce que nous faisons), en plus de la perte de sécurité du type de compilation. Pour les petits types (comme les ints), Go optimise le interface{}type de sorte qu'une liste d'entiers qui ont été transtypés en interface {} occupe une zone contiguë de la mémoire et ne prend que deux fois plus d'espace que les entiers normaux. Cependant, il y a toujours la surcharge des vérifications d'exécution lors de la diffusion depuis interface{}. Référence .

Tous les projets qui ajoutent un support générique pour aller (il y en a plusieurs et tous sont intéressants) suivent uniformément la voie C ++ de la génération de code temporel de compilation.


Ma solution à ce dilemme serait de passer par défaut à des «temps d'exécution lents» avec l'option de profiler le programme et de recompiler les parties les plus sensibles aux performances en mode «compilateurs lents et binaires gonflés». Dommage que les gens qui implémentent des choses comme ça aient tendance à emprunter la voie C ++.
user7610

1
Il a été mentionné que les petits types (c'est-à-dire int) qui sont stockés en cours d' []interface{}utilisation 2x la RAM comme []int. Bien que cela soit vrai, même les types plus petits (c'est-à-dire l'octet) utilisent jusqu'à 16 fois la RAM comme []byte.
BMiner

Il n'y a en fait aucun dilemme avec l'approche C ++. Si un programmeur choisit d'écrire du code modèle, l'avantage de le faire doit dépasser le coût d'une compilation lente. Sinon, il pourrait simplement le faire à l'ancienne.
John Z. Li

Le dilemme est de savoir quelle approche choisir. Si vous résolvez le dilemme en adoptant l'approche C ++, le dilemme est résolu.
user7610

9

Même si les génériques ne sont pas actuellement intégrés, il existe plusieurs implémentations externes de génériques pour go, qui utilisent des commentaires en combinaison avec de petits utilitaires qui génèrent du code.

Voici une telle implémentation: http://clipperhouse.github.io/gen/


1

En fait, selon ce post:

De nombreuses personnes ont conclu (à tort) que la position de l'équipe de Go est «Go n'aura jamais de génériques». Au contraire, nous comprenons le potentiel des génériques, à la fois pour rendre Go beaucoup plus flexible et puissant et pour rendre Go beaucoup plus compliqué. Si nous devons ajouter des génériques, nous voulons le faire d'une manière qui offre autant de flexibilité et de puissance avec le moins de complexité supplémentaire possible.


-1

Le polymorphisme paramétrique (génériques) est à l'étude pour Go 2 .

Cette approche introduirait le concept de contrat , qui peut être utilisé pour exprimer des contraintes sur les paramètres de type:

contract Addable(a T) {
  a + a // Could be += also
}

Un tel contrat pourrait alors être utilisé ainsi:

func Sum(type T Addable)(l []T) (total T) {
  for _, e := range l {
    total += e
  }
  return total
}

C'est une proposition à ce stade.


Votre filter(predicate, list)fonction pourrait être implémentée avec un paramètre de type comme celui-ci:

func Filter(type T)(f func(T) bool, l []T) (result []T) {
  for _, e := range l {
    if f(e) {
      result = append(result, e)
    }
  }
  return result
}

Dans ce cas, il n'est pas nécessaire de contraindre T.


1
Si vous lisez cette réponse aujourd'hui, sachez que les contrats ont été supprimés du projet de proposition: go.googlesource.com/proposal/+/refs/heads/master/design/…
jub0bs
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.