Le langage Go : ce que nous avons bien fait et mal fait
(commandcenter.blogspot.com)- Depuis sa publication en open source le 10 novembre 2009, Go a évolué pendant 14 ans avec pour objectif non pas le langage lui-même, mais une manière de créer plus facilement des logiciels de production
- Une spécification formelle, plusieurs implémentations de compilateur, une cross-compilation simple, une garantie de compatibilité après Go 1.0, ainsi que la bibliothèque standard et gofmt ont soutenu son adoption
- La concurrence, point fort pour structurer les logiciels serveur, a aussi semé la confusion au départ faute d’explications suffisantes sur la différence avec le parallélisme et sur les bons cas d’usage
- Les interfaces sont devenues la base des bibliothèques, des tests, de la gestion des dépendances et d’une conception centrée sur la composition, tandis que l’absence de génériques a compliqué pendant plus de dix ans la conception polymorphe
- La gestion des paquets, la documentation et la collaboration avec la communauté ont donné lieu à beaucoup de tâtonnements, mais Go vise un environnement où le code se ressemble quel que soit son auteur ou son ancienneté, et continue à se compiler
L’objectif de Go : une manière de développer des logiciels, plus qu’un langage
- Le 10 novembre 2023 marquait le 14e anniversaire de la publication de Go en tant que projet open source
- Lors de l’ouverture en 2009, Ken Thompson, Robert Griesemer, Russ Cox, Ian Taylor, Adam Langley, Jini Kim et d’autres ont assisté au lancement du projet
- L’objectif initial de Go n’était pas de créer un nouveau langage de programmation pour lui-même, mais de mieux écrire des logiciels de haute qualité
- À l’époque, le principal goulot d’étranglement ne venait pas des fonctionnalités du langage, mais de la complexité de la création de logiciels serveur modernes chez Google
- contrôle des dépendances
- grandes équipes et évolution des effectifs
- facilité de maintenance
- tests efficaces
- exploitation des CPU multicœurs et du réseau
- Go se définit encore aujourd’hui comme un projet visant à rendre la construction de logiciels de production plus simple et plus productive
Une identité communautaire façonnée par la mascotte Gopher
- Le Go gopher a été l’un des premiers facteurs du succès de Go
- Créé par Renee French, le gopher est devenu à la fois un symbole partagé par les programmeurs Go et l’étendard de la communauté
- Comme le montre une conférence appelée GopherCon, le gopher a contribué à créer l’atmosphère d’un projet alliant excellence technique et plaisir
- Le choix de le publier sous licence Creative Commons Attribution a eu des avantages et des inconvénients
- il a encouragé les remixes et les variations, renforçant l’esprit communautaire
- mais la clause d’attribution a aussi suscité des débats, ou entraîné des crédits erronés sur des travaux que Renee French n’avait pas réalisés
- La gestion de la mascotte pour qu’elle conserve son esprit d’origine reste un défi
Les bons choix techniques de Go
- Le projet a commencé par rédiger une spécification formelle afin de fixer le comportement des implémentations du compilateur et de permettre à plusieurs implémentations de converger vers le même comportement
- il était considéré qu’un compilateur seul ne pouvait pas faire office de spécification
- le premier brouillon de la spécification a été rédigé au 18e étage d’un immeuble de Darling Harbour à Sydney
- Plusieurs implémentations de compilateur ont aidé à affiner le langage et la spécification
- le 7 juin 2008, Ian Taylor a envoyé un message indiquant qu’il avait lu le brouillon de la spécification Go et créé un frontend gcc
- d’autres implémentations compatibles sont ensuite apparues
- Le fait d’avoir rendu la cross-compilation simple a permis aux développeurs de travailler sur la plateforme de leur choix et de déployer sur celle dont ils avaient besoin
- Le choix d’introduire une garantie de compatibilité après avoir stabilisé le langage dans Go 1.0 a eu un impact majeur sur l’adoption de Go
- maintenir une forte compatibilité a un coût, mais réduit la crainte qu’une nouvelle version de Go casse un projet
- La bibliothèque standard fournit l’essentiel de ce qu’il faut pour écrire du code serveur du XXIe siècle et a permis à la communauté de travailler avec une boîte à outils commune
- à ses débuts, la bibliothèque a aussi grossi parce qu’il n’existait pas vraiment d’autre endroit pour installer du code Go
- cela a aidé à limiter la prolifération de bibliothèques concurrentes et à unifier la communauté
Les outils et gofmt au cœur de l’expérience de développement
- Go a été conçu comme un langage facile à parser, ce qui a facilité la création d’outils
- Au départ, on pensait qu’un IDE serait nécessaire, mais la facilité d’outillage a permis avec le temps aux IDE de prendre en charge Go, et
goplsest aussi apparu - En plus du compilateur, des outils de support ont été fournis pour les tests automatiques, la couverture et la vérification du code
- La commande go a unifié tout le processus de build, et beaucoup de projets ont pu l’utiliser pour couvrir l’essentiel de la construction et de la maintenance du code Go
- gofmt est devenu un outil qui a influencé non seulement Go, mais l’ensemble de la communauté du développement
- Robert Griesemer a insisté dès le départ pour créer gofmt
- avant gofmt, la qualité des formateurs automatiques était trop faible pour qu’ils soient vraiment utilisés
- cela a réduit le temps perdu dans les débats sur les espaces et les retours à la ligne
- lorsque l’intérieur de gofmt est devenu une bibliothèque, il est devenu possible d’analyser et modifier l’AST, puis de régénérer une sortie lisible par un humain
- cela a aussi permis d’autres outils, comme un simplificateur, un analyseur ou des outils de couverture de code
La concurrence : puissante, mais expliquée trop tard
- En 2002, les logiciels de Google évitaient largement les threads, dans un climat influencé par la position de John Ousterhout selon laquelle “threads were bad”
- Go est né de l’expérience qu’il existait une meilleure manière d’utiliser la concurrence qu’avec des paquets bas niveau comme pthread
- Go a joué un rôle important dans l’acceptation de la concurrence comme outil puissant dans le monde de la programmation
- notamment dans les environnements multicœurs et réseau
- aujourd’hui, la plupart des langages grand public prennent en charge la concurrence
- Go n’a pas repris des termes comme coroutine, task ou thread, et a introduit un nouveau terme : goroutine
async/awaitest une nette amélioration par rapport à pthread, mais comparé à goroutine, channel et select, il peut être plus simple et plus léger pour l’implémenteur tout en reportant une partie de la complexité sur le programmeur- le problème décrit par Bob Nystrom dans “What Color is Your Function?” en est un exemple
- le modèle CSP de Go s’adapte bien aux langages procéduraux, mais sa bonne implémentation exige une certaine complexité d’exécution
- comme plusieurs implémentations de la concurrence dans un même environnement peuvent poser problème, Go a intégré cela au langage plutôt que d’en faire une simple bibliothèque
Deux erreurs autour de la concurrence
- L’équipe Go n’a pas assez tôt indiqué clairement où utiliser la concurrence
- le principal cas d’usage consistait à simplifier les logiciels serveur dans des bibliothèques centrales comme
net/http - ce n’était pas une fonctionnalité à utiliser partout dans tous les programmes
- le principal cas d’usage consistait à simplifier les logiciels serveur dans des bibliothèques centrales comme
- Le fait d’avoir tardé à expliquer la différence entre parallélisme et concurrence a aussi posé problème
- beaucoup de développeurs ont tenté de paralléliser leur code avec des goroutines pour le rendre plus rapide, avant de découvrir avec surprise qu’il devenait plus lent
- le code concurrent ne peut gagner en vitesse via la parallélisation que lorsque le problème lui-même est parallélisable, par exemple dans le traitement de requêtes HTTP
- Pour corriger cela, la présentation “Concurrency is not Parallelism” a été donnée à Waza en 2012
- Cette explication aurait dû arriver plus tôt
- Malgré cela, Go a contribué à populariser la concurrence comme manière de structurer les logiciels serveur
Interfaces et conception centrée sur la composition
- Les interfaces sont, avec la concurrence, l’une des idées clés qui distinguent Go
- Les interfaces Go constituent en quelque sorte la réponse de Go à la conception orientée objet centrée sur le comportement
- L’approche dynamique où il n’est pas nécessaire de déclarer à l’avance quel type implémente quelle interface a été critiquée au départ, mais s’est révélée essentielle au style de programmation Go
- La bibliothèque standard, les tests et la gestion des dépendances reposent fortement sur le caractère ouvert des interfaces
- Le
sortde Go est implémenté comme une fonction opérant sur des interfaces- cela diffère du style orienté objet auquel beaucoup étaient habitués, mais c’était une idée puissante
- De petites interfaces comme
Reader,Writeret l’interface vide sont devenues la base des bibliothèques Go- ces trois interfaces bien connues comptent en moyenne respectivement 2 ou 3 méthodes
- de petits ensembles de méthodes sont devenus un idiome typiquement Go
Le long détour des génériques
- L’équipe Go avait observé que la programmation générique tend à faire passer les types avant les algorithmes
- elle préférait une conception organique plutôt qu’une abstraction prématurée
- et accordait plus d’importance aux fonctions qu’aux conteneurs
- Go a intégré au langage des conteneurs génériques comme map, slice, array et channel, sans exposer directement cette généricité aux utilisateurs
- Il a été estimé que ces types suffisaient pour la plupart des tâches de programmation simples, mais pas dans tous les cas, et cette frontière a été source de frustration pour certains utilisateurs
- Comme les interfaces étaient au cœur de la programmation Go, tout nouveau modèle de polymorphisme devait nécessairement bien s’y intégrer
- il y a eu plusieurs tentatives et des implémentations abandonnées
- après de nombreuses discussions, l’aide de théoriciens des types dirigés par Phil Wadler a aussi été sollicitée
- Au final, Go a conçu ses génériques en généralisant les interfaces de “jeu de méthodes” à “jeu de types”
- Le terme “generics” vient d’un polymorphisme centré sur les structures de données et ne désigne pas parfaitement le parametric polymorphism proposé par Go, mais en pratique c’est bien le mot generics qui s’est imposé
Compilateur : d’abord en C, puis en Go
- Le fait que les premiers compilateurs Go aient été écrits en C a dérangé une partie de la communauté du langage
- certains estimaient qu’il aurait fallu utiliser une boîte à outils comme LLVM ou pratiquer le self-hosting en Go
- Le choix d’écrire d’abord le compilateur en C était pragmatique
- pour amorcer un nouveau langage, il fallait utiliser un langage existant
- Ken Thompson avait déjà écrit un compilateur C, dont la structure interne pouvait servir de base au compilateur Go
- écrire le compilateur dans le langage en cours de conception risquait d’orienter ce langage vers ce qui convient à l’écriture d’un compilateur
- Le compilateur initial produisait un code ordinaire et n’était pas très propre en interne, mais il était petit, familier et facile à modifier rapidement
- L’ajout de segmented stack à croissance automatique a montré l’intérêt de ce choix
- avec une boîte à outils comme LLVM, l’intégration aurait été plus difficile, car elle aurait aussi impliqué des changements d’ABI et de prise en charge du garbage collector
- La structure issue à l’origine de la famille de compilateurs Plan 9 convenait également bien à la cross-compilation
- Dans Go 1.5, Russ Cox a écrit un outil traduisant semi-automatiquement le compilateur de C vers Go
- à ce stade, le langage était achevé et l’inquiétude d’une conception centrée sur le compilateur avait disparu
- une fois porté en Go, il est devenu possible d’appliquer au développement du compilateur les avantages de Go en matière de tests, d’outillage, de réécriture automatique et d’analyse de performance
- Il existe aujourd’hui aussi bien un compilateur Go fondé sur LLVM que plusieurs autres compilateurs
Open source et collaboration avec la communauté
- Dès le départ, il a été considéré que Go devait être un projet open source pour réussir
- Mais il paraissait plus productif de le développer en privé jusqu’à ce que les idées clés et le comportement de l’implémentation soient clarifiés
- les deux premières années avant l’ouverture publique ont été importantes pour clarifier les objectifs
- Après le passage en open source, les retours de la communauté ont été massifs, et Ian Taylor a notamment passé beaucoup de temps à répondre aux questions
- Le port Windows a été réalisé entièrement par la communauté sous la conduite d’Alex Brainman
- L’équipe Go a mis du temps à comprendre la meilleure manière de collaborer avec la communauté
- elle pensait bien communiquer, mais des malentendus et des écarts d’attente ont fait perdre du temps
- Le projet Go a choisi de ne pas intégrer le code rapidement pour le nettoyer ensuite, mais d’exiger une haute qualité de code avant fusion
- revue de code obligatoire
- attention minutieuse aux détails
- Cette méthode impose davantage de charge à la communauté ; si les contributeurs n’en perçoivent pas la valeur, ils peuvent avoir l’impression de ne pas être les bienvenus
- L’historique du dépôt du projet Go est passé par SVN, Perforce, Mercurial puis Git, mais Russ Cox a conservé l’historique, de sorte que les anciens changements SVN restent visibles dans le dépôt Git actuel
- Google soutient Go, mais ne fixe pas l’agenda de l’équipe Go
- l’importante base de code Go interne à Google sert à tester et valider les versions
- le flux va du dépôt public vers Google, et non l’inverse
- l’équipe centrale de Go est salariée par Google mais agit de manière indépendante
Les tâtonnements de la gestion des paquets
- Si la conception même des packages dans le langage Go était excellente, le développement de la gestion des paquets s’est mal déroulé
- Le choix de spécifier un chemin dans les instructions
importsous forme de chaîne ordinaire était important et flexible - Le problème est venu de la transition difficile entre un monde limité à la bibliothèque standard et un modèle où l’on récupérait du code sur le Web
- Au début, l’équipe Go était habituée au monorepo de Google et à une manière de builder depuis head
- elle n’avait pas assez d’expérience avec les gestionnaires de paquets manipulant plusieurs versions d’un même package
- elle n’a pas traité dès le départ toute la complexité technique de la résolution de graphes de dépendances
- Le travail autour de deps.dev est présenté en quelque sorte comme une compensation pour ce problème
- Les tentatives de résoudre la gestion des dépendances avec la communauté étaient animées de bonnes intentions, mais lorsque la conception finale est arrivée, beaucoup de membres de la communauté se sont sentis exclus
- Cette expérience a servi de leçon sur la manière dont la participation communautaire doit réellement se dérouler, et beaucoup de choses se sont améliorées depuis
- Aujourd’hui, la conception paraît stabilisée, techniquement excellente et fonctionne bien pour la plupart des utilisateurs
Documentation et exemples exécutables
- Une grande quantité de documentation a été écrite au début, mais elle ne correspondait pas à ce que voulait la communauté
- Le point le plus manquant était le besoin d’exemples d’usage, même pour les fonctions les plus simples
- montrer comment utiliser quelque chose avait plus de valeur que simplement dire ce que cela fait
- La documentation actuelle contient de nombreux exemples, fournis en grande majorité par des contributeurs open source
- Très tôt, Go a rendu ses exemples exécutables sur le Web
- lors d’une présentation à Google I/O en 2012, il était possible d’exécuter dans le navigateur des extraits de code montrant la concurrence
- Andrew Gerrand a écrit la technologie Web qui rendait cela possible
- cela a ensuite été déployé sur le blog et dans la documentation en ligne des packages
- The Go playground est proposé comme sandbox publique où chacun peut tester et développer librement du code
Où en est Go après 14 ans
- Grâce aux décisions prises dans la conception du langage et son processus de développement, Go s’est imposé comme bien plus qu’un simple langage de programmation : une manière d’écrire du logiciel
- Les éléments suivants ont contribué à la position atteinte par Go
- une bibliothèque standard solide, couvrant l’essentiel des fonctions de base nécessaires au code serveur
- la concurrence comme élément de première classe du langage
- une approche fondée sur la composition plutôt que sur l’héritage
- un modèle de packaging clarifiant la gestion des dépendances
- des outils intégrés pour des builds et des tests rapides
- un formatage strict et cohérent
- une préférence pour la lisibilité plutôt que pour l’astuce
- une garantie de compatibilité
- La communauté Go est composée de Gophers divers et très actifs, qui jouent un rôle central dans le succès du projet
- En conséquence, le code Go a tendance à se ressembler et à se comporter de façon similaire quel que soit son auteur, il existe relativement peu de clans utilisant des sous-ensembles différents du langage, et le code continue à se compiler et s’exécuter avec le temps
- Ce type de résultat reste rare parmi les grands langages de programmation
1 commentaires
Avis sur Hacker News
C’est vraiment appréciable que des personnes clés prennent le temps de laisser une rétrospective, et c’est très utile pour ceux qui veulent réellement provoquer des changements aujourd’hui.
Cela dit, je ne pense pas que Rob Pike ait exprimé assez clairement ce que Go a bien fait. Go n’a pas seulement bien géré le langage lui-même, mais aussi les forces qui s’exerçaient sur le projet : il a limité son usage cible à la programmation système, défini clairement le langage et ses principes, maintenu une priorité à la qualité en corrigeant les problèmes avant la diffusion, et, tout en contrôlant strictement le langage, les releases et le message central, il a laissé de la place à la communauté pour piloter des domaines plus périphériques.
Un point sous-estimé est que Google lui-même n’est pas intervenu de manière excessive. C’est peut-être parce que Go contribuait réellement aux objectifs de Google et était important. Je me demande si ce serait encore possible aujourd’hui avec un nouveau projet.
Dart a été très peu adopté en dehors de Flutter, ce qui est intéressant si l’on compare cela au fait qu’il y a beaucoup plus de code applicatif que de code système. Go a été une technologie clé pour faire passer les logiciels côté serveur de l’environnement lourd de Java vers des conteneurs natifs, et il est devenu la base de la majeure partie de l’infrastructure des applications web de la dernière décennie. Malgré cette croissance, l’équipe est restée réduite et les personnes clés sont restées en place, mais je ne sais pas si cela continuera.
Pour cette raison, par conception, ce n’est pas un langage adapté à la programmation système. Comme il s’est présenté ainsi au début, un mythe étrange a persisté longtemps, mais il semble que cette formulation ait été retirée il y a quelques années.
Go est plus proche d’un concurrent de la famille Java que d’un concurrent de C ou Rust, ce que montrent ses usages réels. Cela dit, en raison de son système de types limité et de son excellent support de la concurrence, il se place souvent un peu plus bas dans la stack.
C’était particulièrement vrai pour la gestion des versions. Au début, Go ne reconnaissait même pas le problème, puis, quand le consensus de la communauté a grandi, les discussions précédentes ont été oubliées et il a été décidé qu’on passerait désormais aux modules.
Je me souviens aussi qu’ils ne voulaient pas faire du temps monotone (monotonic time) une API publique avant que Cloudflare ne subisse une panne liée à l’heure d’été.
Dans l’univers Java, il y a un certain plaisir à refactorer une base de code avec de nouvelles fonctionnalités, par exemple d’excellents streams. Avec Go, le plaisir vient du fait de trouver l’abstraction la plus simple et la plus banale possible.
Tout langage a besoin de bibliothèques et de frameworks adaptés à un domaine donné. Flutter est un framework pour écrire des applications clientes en Dart ; sans Flutter, même si l’on adore le langage Dart, il faudrait consacrer énormément de temps rien qu’à comprendre comment dessiner des pixels à l’écran sur Android et iOS.
Toute personne qui écrit une application construit au-dessus d’une stack de bibliothèques et de frameworks. La différence entre Go et Dart associé à Flutter tient surtout au fait que, pour Go, les bibliothèques de domaine nécessaires côté serveur, comme le réseau, la sérialisation ou la cryptographie, font partie de la bibliothèque standard.
Dart dispose d’éléments de base comme les collections et l’asynchrone dans ses bibliothèques intégrées, mais dépend de paquets externes comme Flutter pour les fonctionnalités propres à un domaine. C’est en grande partie parce que Dart a eu, dès ses tout débuts, un système solide de gestion de paquets. De nombreuses bibliothèques centrales écrites et maintenues par l’équipe Dart sont elles aussi distribuées via le gestionnaire de paquets plutôt qu’intégrées au langage, ce qui les rend beaucoup plus faciles à faire évoluer.
Les frameworks d’UI ont tendance à avoir une durée de vie plus courte que les langages ; je pense donc qu’il vaut mieux que Flutter ne soit pas incrusté dans la bibliothèque standard de Dart. Flutter est excellent, mais je ne serais pas surpris qu’une meilleure solution apparaisse dans 20 ans, et si Flutter n’est pas intégré, les utilisateurs de Dart pourront alors migrer plus facilement.
Le faux FOSS comme Android est toléré pour des raisons stratégiques, mais en dehors de cela, l’ambiance est que si cela ne mène pas à des clics publicitaires, c’est inutile.
En 2012, en tant que développeur Python, j’ai pris Go parce que je devais faire de la manipulation de bits pour Modbus. Je n’ai jamais déployé ce code, mais j’ai été stupéfait de voir à quel point toucher aux bits et aux octets était simple et fonctionnait tout simplement.
Dix ans plus tard, après plusieurs expériences de travail Go presque à plein temps, je trouve toujours étonnant que la plupart des choses fonctionnent tout simplement.
J’aime le langage Go et la communauté Go. Je suis reconnaissant envers Rob, Ian, Russ et les autres personnes qui créent Go pour leur travail, et j’apprécie aussi que cette présentation et cet article abordent honnêtement les « passages difficiles » avec la communauté.
Cela dit, pour avoir traversé cette période, j’ai beaucoup de mal à croire qu’ils ne savaient pas exactement comment ils se comportaient, en particulier dans le chaos de la gestion des paquets. Mais l’article a aussi raison de dire qu’ils sont maintenant arrivés à une bonne solution.
J’espère que Go continuera à évoluer sainement au cours des dix prochaines années, tout comme des langages similaires inspirés par lui, comme Zig et Deno, avec des communautés en bonne santé.
Je suis plus biologiste computationnel que programmeur, donc mon usage de Go est irrégulier, mais quand je reviens à Go, mon ancien code compile et le langage se comporte comme prévu.
En même temps, j’apprécie que Rob Pike reconnaisse les erreurs commises en apprenant à impliquer la communauté, tout en n’acceptant pas toutes les fonctionnalités brillantes qui passaient.
Certains ont fonctionné immédiatement, d’autres ont nécessité quelques ajustements parce que le modèle d’IA avait des informations d’API un peu obsolètes.
Malgré tout, le fait de copier un programme sur un serveur avec
scpet de le voir fonctionner tout simplement est incroyable comparé à la danse qu’il fallait refaire à chaque fois avec Python autour du « c’est quoi le système de venv à la mode cette fois-ci ? »Excellent article, mais il donne aussi une impression assez auto-congratulatoire. Je m’attendais à une reconnaissance des problèmes plus profonds du langage ; au fond, le message semble être que, pour eux, le langage est parfait et que le problème, c’est moi
C’est pourquoi j’ai du mal à recommander Go. Il y a des problèmes avec le système de types, la gestion des erreurs, la concurrence non sûre, une syntaxe trop simpliste,
nil, les valeurs zéro par défaut, et beaucoup de packages courants laissés à l’abandonAujourd’hui, j’utilise Rust comme langage principal. Son écosystème est actif et il a une vision sur plusieurs aspects où Go n’en a pas
Pour le dire plus crûment, Go a sans doute eu son époque quand il était en concurrence avec PHP comme langage backend
Le premier paragraphe dit « je n’aime pas », le deuxième est sarcastique, le troisième aussi « je n’aime pas », le quatrième dit « Rust est meilleur », et le cinquième est encore sarcastique
deps.devQuelques années plus tard, au travail, j’ai essayé d’introduire Go dans l’équipe pour faire avancer les choses, mais un autre ingénieur a pris le temps d’examiner le langage et a listé les raisons pour lesquelles il n’était pas terrible ; il avait raison
Le point essentiel était que, même s’il est simple, de petites choses comme la gestion des erreurs et les imports inutilisés rendent son usage pénible. Personnellement, j’aimais bien la gestion des erreurs, mais je détestais le système de types
Comme j’avais déjà utilisé OCaml, je connaissais la valeur des types
OptionetResultpar rapport à try/catch ounil, mais j’utilisais Go parce qu’il était facile. Cette facilité a toutefois un coût de maintenance à long terme. Mieux vaut bien faire les choses dès le départ et ne pas affronter les difficultés plus tardJe ne veux pas le dire à la légère, mais j’ai souvent entendu Go décrit comme un langage qui a pris les 50 dernières années de progrès des langages de programmation et les a jetées à la poubelle
Ce n’est pas péjoratif. Rob Pike ne se préoccupe pas du fait que la concurrence ne soit pas sûre ou que
nilsoit l’erreur à un milliard de dollars, et la plupart des utilisateurs de Go non plusEst-ce que cela fait de Go un bon langage ? Non. Mais ce n’est pas le sujet. Go est un langage pratique et suffisamment correct, associé à des outils convaincants qui le rendent facile à utiliser. C’est un peu les Crocs des langages de programmation
Si j’ai besoin d’une forte intégrité et de sécurité, j’utiliserais Ada, ou Ada/SPARK avec vérification formelle. Pour le reste, Go offre une productivité bien supérieure à Rust
Personnellement, je vois Rust comme un cas emblématique de sur-ingénierie, à la manière de C++
Ça va paraître amer, mais il y a 10 ans, sur
go-nuts, on s’est moqué de moi, et Rob Pike m’a répondu avec mépris. J’avais osé dire que la manière dontgo getet les imports de modules via le réseau fonctionnaient, étaient promus dans toute la documentation pour débutants et utilisés par l’ensemble de la communauté, finirait par être nuisible et à courte vueL’étrange arbre d’espace de noms global a du sens dans ce contexte, et l’insistance sur les artefacts générés versionnés se comprend aussi si l’on considère que Google inclut même des produits de build dans son monorepo
Bien sûr, il y a le problème de
v2ou celui duv0éternel. Mais c’est quand même mieux que d’utiliser npm, ou que de devoir gérer un truc Python qui vous donne envie de faire un signe de croixCet article ressemble à une version plus personnelle de l’article publié dans l’ACM [1]. Les deux reconnaissent qu’ils n’ont pas créé un nouveau langage de programmation remarquable du point de vue de la spécification du langage, mais qu’ils ont très bien construit tout ce qui entoure le langage, ce qui peut finalement être plus important
Dans l’article soumis, ils disent avoir inventé l’approche des interfaces et celle de la concurrence. Mais les goroutines sont équivalentes aux threads de Haskell, et les interfaces, lorsqu’elles prennent en charge des paramètres génériques comme aujourd’hui, ressemblent beaucoup aux typeclasses de Haskell. Comme Haskell est antérieur à Go, il est intéressant de voir des programmeurs impératifs redécouvrir indépendamment la puissance d’idées issues de la programmation fonctionnelle
Une véritable innovation de Go est que l’implémentation d’une interface n’a pas besoin de déclarer l’interface qu’elle implémente. Du point de vue de la sûreté, c’est horrible, mais en pratique cela pose rarement problème, et cela élimine les terribles problèmes de dépendances circulaires que l’on rencontre avec Haskell et Rust
[1] https://cacm.acm.org/magazines/2022/5/260357-the-go-programm...
Si les types avaient dû déclarer à l’avance les interfaces qu’ils implémentent, Go aurait été un langage complètement différent. C’était l’un des principaux éléments différenciateurs de Go. Du moins jusqu’à ce que TypeScript arrive avec des interfaces structurelles pour d’autres raisons
Peux-tu citer ne serait-ce qu’un langage de programmation digne d’intérêt qui, lorsque tu définis une interface, t’oblige à fournir son implémentation ?
Il me semble que les problèmes liés à la cryptographie de Go n’ont pas encore été beaucoup évoqués. Après qu’OpenSSH a abandonné SHA1, il a fallu un an à l’équipe Go pour ajouter la prise en charge de SHA2 dans
x/crypto/ssh[1]. Gitea en a été une victime notable [2]De plus, voir un mainteneur crypto attaquer GnuPG [3] et tenter d’entamer la confiance envers Dan Bernstein [4] n’inspire pas confiance
[1]: https://github.com/golang/go/issues/49269
[2]: https://github.com/go-gitea/gitea/issues/17798
[3]: https://twitter.com/FiloSottile/status/1127643698676797441
[4]: https://twitter.com/FiloSottile/status/1555669786826244096
x/sont souvent laissés à l’abandon et suivent un modèle de développement qui rend difficile, pour de nouveaux contributeurs, de faire accepter des correctifsEn revanche, je considère que le package crypto de la bibliothèque standard est l’une des grandes réussites de Go. OpenSSL a longtemps été une catastrophe, et Go, en particulier agl, a créé et diffusé une implémentation de remplacement très largement utilisée, d’une qualité moyenne élevée. C’est particulièrement vrai du point de vue du nombre bien plus réduit de pièges dans l’API publique
L’article cite
gofmtcomme quelque chose de « réussi », et cela mérite vraiment d’être soulignéPour beaucoup de concepteurs et de défenseurs de langages, cela peut sembler anodin, mais cela apporte une valeur énorme en éliminant complètement, dès le premier jour du projet Go, l’un des débats de peinture du local à vélos les plus courants
J’ai vu plusieurs langages plus récents le copier délibérément, ou l’adopter en se disant que, puisque Go l’avait, il leur en fallait un aussi
J’ai vu des règles de formatage assez étranges, mais très peu auxquelles il soit impossible de s’habituer. À l’inverse, dans les bases de code sans règles de formatage imposées, le résultat rendait souvent le code plus difficile à comprendre
go fmtrende cela en grande partie inutileJe suis d’accord avec l’idée selon laquelle « l’existence d’une bibliothèque solide et bien conçue, couvrant l’essentiel de ce qu’il faut pour écrire du code serveur au XXIe siècle, a été un atout majeur ».
Go a été financé par Google parce que Google avait un problème. Il fallait écrire beaucoup de code applicatif côté serveur, Python était trop lent et C++ trop fragile. Go s’insérait très bien dans cet entre-deux.
Le gros bonus, c’est que des gens de Google ont écrit les bibliothèques nécessaires à ce genre de travail et les utilisaient aussi en interne. Donc, quand on utilisait ces bibliothèques, même les chemins d’erreur avaient souvent été réellement exercés par du code en production.
J’ai des désaccords techniques avec le langage. En particulier, au début, l’accent mis sur l’usage de files pour tout, jusqu’à construire des mutex avec des files. Mais pour le cas d’usage visé, les green threads étaient le bon choix. Le problème de la couleur des fonctions est un vrai problème, et les astuces alambiquées qu’il faut employer en Rust pour faire cohabiter asynchrone et threads vont beaucoup trop loin. Go sacrifie un peu de performance et gagne énormément en simplicité.
L’ancienne aversion pour les threads est aussi intéressante. J’ai commencé sur des mainframes UNIVAC, où les threads existaient depuis 1967 et s’appelaient alors des « activities ». En 1972, il y avait non seulement des threads en espace utilisateur, mais ils tournaient aussi sur des multiprocesseurs symétriques. Il y avait aussi des E/S asynchrones, des fonctions de complétion en espace utilisateur, des instructions intégrées pour les verrous, et un système d’exploitation lui-même threadé en interne.
Les threads et les multiprocesseurs étaient considérés comme normaux, et en passant à UNIX, j’ai eu l’impression de les perdre. Il a fallu des décennies à UNIX/Linux pour rattraper ce retard dans ce domaine, et plusieurs générations de programmeurs n’ont pas su utiliser les multiprocesseurs à mémoire partagée.
La théorie initiale de la concurrence de Dijkstra a souvent été réinventée avec une autre terminologie et des conceptions parfois pires. Les gens de Go comprenaient le bounded buffer de Dijkstra, et pourquoi les bounded buffers de longueur 0 et 1 sont utiles. C’était agréable de revoir cela. Avec les bonnes primitives, la concurrence n’est pas si difficile. Si l’on essaie de résoudre le problème à la manière classique de pthreads, en saupoudrant des verrous un peu partout, ça ne fonctionne pas correctement. Le fait qu’aux débuts d’UNIX/Linux, l’ordonnanceur CPU était vraiment médiocre et que le déblocage des threads se comportait de façon atroce n’a pas aidé non plus.
Go a été créé parce que Rob Pike avait un problème. Il n’aimait pas C++, Java ni Python, et comme il était Rob Pike, Google l’a autorisé à passer plusieurs années à travailler sur un nouveau langage. Ce n’est pas Larry, Sergey, Sundar ou Eric qui sont allés voir Rob pour lui demander de faire quelque chose.
En gros, Go a été financé parce que Rob Pike a demandé du soutien, que Google voulait garder Rob Pike, et qu’on pensait qu’il ferait presque certainement quelque chose d’intéressant. Dans Google Engineering, l’argent de l’entreprise revient en pratique surtout à autoriser des gens à travailler.
Comme Rob Pike était un ingénieur exceptionnel, Go s’est révélé plutôt bon à certains égards, et correspondait bien à son problème initial : devoir utiliser C++, Python ou Java pour écrire des applications opérationnelles chez Google. Certaines personnes en interne chez Google étaient d’accord et ont commencé à utiliser Go, dont quelques personnes influentes qui ont « encouragé » les autres à utiliser Go plutôt que Python.
En 2009, chez Google, à l’exception du cas de l’acquisition de YouTube, Python était très peu utilisé pour des serveurs au-delà de quelques utilitaires internes ; on peut donc écarter cette idée immédiatement. Le plus gros serveur Python développé par Google lui-même était Mondrian, l’outil de revue de code écrit par Guido van Rossum, qui a ensuite été remplacé par Critique, écrit en Java.
À l’époque, Google avait une politique des trois langages assez stricte afin d’éviter les guerres de langages de programmation. On pouvait utiliser C++, Java et Python ; Python servait aux scripts, C++ à l’infrastructure, et Java aux serveurs web.
Il y avait aussi des zones de recouvrement. Java avait été introduit plus tard que C++, et certains serveurs web étaient restés en C++ parce qu’ils étaient difficiles à réécrire. Il y eut aussi des tentatives de migration progressive vers Java, mais elles ne se sont pas vraiment imposées. Certains serveurs d’infrastructure, comme Megastore, étaient écrits en Java, tandis que les bibliothèques de base étaient généralement en C++ avec des bindings JNI et Python.
Il y avait aussi des failles dans la politique : certaines équipes ont créé leurs propres langages pour l’infrastructure interne, et la base de code réelle utilisait aussi des langages de configuration personnalisés ainsi que Sawzall, créé par Rob Pike. Sa syntaxe ressemblait un peu à celle de Go.
Dans l’ensemble, les gens avaient beaucoup de liberté de choix, et les nouveaux serveurs web étaient principalement écrits en Java, tandis que les nouveaux moteurs de base de données, par exemple, étaient principalement écrits en C++.
Écrire du code asynchrone était certainement difficile, et il est vrai que Go a correctement résolu ce problème. Mais Go n’était pas un projet lancé par la haute direction pour résoudre les problèmes de Google. Non pas que la direction aurait eu peur de ce genre de projet : il existait énormément de projets d’infrastructure internes créés et dotés en personnel par les équipes concernées pour résoudre directement les difficultés des développeurs. Les grandes mises à niveau réseau menées à l’époque en sont un exemple. Ces projets étaient annoncés à l’avance.
Je ne me souviens pas qu’il y ait eu une grande insatisfaction vis-à-vis de la situation. Les temps de build étaient un problème jusqu’à ce que Bazel/Blaze fonctionne avec des clusters de build distants ; ensuite, tout était mis en cache à distance, si bien que même d’immenses bases de code pouvaient être compilées en quelques secondes. La vitesse du compilateur local est devenue presque insignifiante, et
javacétait aussi très rapide.L’annonce de Go a été assez surprenante. S’il avait vraiment été développé pour résoudre un problème de Google, la manière habituelle de Google aurait été de le lancer d’abord en interne, de le faire évoluer pendant quelques années avec des utilisateurs internes, puis de le rendre public. Mais Go a d’abord été rendu public. Les gens autour de moi se demandaient à quoi cela pouvait bien servir, une réaction qu’on aurait difficilement attendue si une demande interne avait réellement été le moteur du projet.
Le passage sur la décision d’utiliser le compilateur C de Ken Thompson plutôt que LLVM est intéressant. Les gens s’en sont plaints, et surtout dans les premières versions, cela a aussi eu pour effet de produire du code moins optimisé
En contrepartie, cela a permis de créer rapidement des piles segmentées. S’il avait fallu les implémenter dans LLVM et se conformer à l’ABI LLVM, il est possible qu’ils ne l’aient tout simplement pas fait
Dans l’article, ce n’est pas présenté comme le seul bénéfice de cette décision, mais comme un exemple de bénéfice
X86FrameLowering avait déjà une prise en charge, qui existait aussi au moment de la sortie de Go. Quand on activait le fractionnement de pile, LLVM émettait un test dans le prologue de fonction et, si nécessaire, appelait
__morestackpour allouer davantage de pile. L’ABI Windows MSVC a elle aussi besoin d’un code très similaire pour prendre en charge_chkstk, ce qui s’articulait naturellement avec la prise en charge de__morestackEn réalité, faire en sorte que GDB comprenne les piles segmentées a été plus difficile que n’importe quelle partie de l’implémentation du compilateur. C’est indépendant du backend
Ce que l’auteur a probablement confondu, ce sont les piles relocalisables. À l’époque, c’était difficile à implémenter parce qu’il fallait un ramasse-miettes précis, mais Azul l’a désormais implémenté dans LLVM. À ce moment-là, la façon la plus simple d’implémenter un ramasse-miettes précis consistait à spiller tous les registres à travers les appels de fonction ; cela demandait plus d’efforts d’implémentation, mais rien d’excessif. Comme le compilateur Plan 9 semble de toute façon faire cela, il n’y aurait probablement pas eu de régression de performances par rapport à 6g/8g. Quoi qu’il en soit, la prise en charge du ramasse-miettes d’Azul dispose maintenant d’une vraie implémentation capable de stocker les racines dans les registres
La capacité à expérimenter avait donc bien de la valeur, mais cet exemple précis n’est pas un cas parfait
[1]: https://go.dev/doc/go1.3#:~:text=Go%201.3%20has%20changed%20...
En imaginant un langage statiquement typé de loisir, j’essaie quelque chose qui traite à la fois les interfaces et les génériques, et j’ai pu constater par moi-même que Rob a raison. Faire bien fonctionner les deux ensemble est vraiment difficile
Je pense tout de même que cela en vaut la peine. Personnellement, je trouverais peu gratifiant d’utiliser un langage statiquement typé dans lequel les utilisateurs ne peuvent pas définir eux-mêmes de types génériques
J’ai déjà programmé dans un environnement BASIC où il y avait
GOSUBpour les sous-routines, mais aucun moyen d’écrire une sous-routine prenant des arguments. Je n’ai pas envie de revivre cette expérience au niveau du système de typesJe comprends donc que l’équipe Go ait mis du temps à trouver un bon design. Concevoir un bon langage est difficile ; concevoir un bon langage avec un système de types l’est 10 fois plus ; concevoir un système de types avec des génériques l’est encore 10 fois plus
Go s’est donc attaqué à la partie la plus facile de l’implémentation des génériques. La raison pour laquelle cela n’a pas été fait plus tôt n’était pas technique. Elle était idéologique, fondée uniquement sur l’ignorance, et tenait au fait que la plupart des concepteurs de Go ne suivaient plus le domaine de la théorie des langages de programmation depuis la fin des années 1990