L'article est écrit en réponse à un article antipode publié précédemment.

Au cours des deux dernières années et plus, j'ai utilisé Go pour implémenter un serveur RADIUS spécialisé avec un système de facturation développé. Ce faisant, j'apprends les subtilités de la langue elle-même. Les programmes eux-mêmes sont très simples et n'ont pas pour but d'écrire un article, mais l'expérience d'utilisation de Go lui-même mérite de dire quelques mots pour sa défense. Go devient un langage de plus en plus populaire pour le code sérieux et évolutif. La langue est créée dans Google, dans laquelle elle est activement utilisée. Pour résumer, je crois sincèrement que la conception du langage Go est mauvaise pour les programmeurs Dumb.
Conçu pour les programmeurs faibles?
Faible discours sur les problèmes. Parler fort d'idées et de rêves ...
Go est très simple à apprendre, si simple que vous pouvez lire le code avec peu ou pas de préparation du tout. Cette fonctionnalité du langage est utilisée dans de nombreuses entreprises mondiales lorsque le code est lu avec des spécialistes non essentiels (gestionnaires, clients, etc.). Ceci est très pratique pour les méthodologies telles que le développement piloté par la conception.
Même les programmeurs débutants commencent à produire du code assez décent après une semaine ou deux. Le livre que j'ai étudié Go s'appelle «Go Programming» (écrit par Mark Summerfield). Le livre est très bon, il touche à beaucoup de nuances de la langue. Après des langages inutilement compliqués comme Java, PHP, le manque de magie est rafraîchissant. Mais tôt ou tard, de nombreux programmeurs limités souhaitent utiliser d'anciennes méthodes dans un nouveau domaine. Est-ce vraiment nécessaire?
Rob Pike (l'idéologue principal de la langue) a créé le Go comme un langage industriel facile à lire et efficace à utiliser. Le langage est conçu pour une productivité maximale dans les grandes équipes et cela ne fait aucun doute. De nombreux programmeurs débutants se plaignent du manque de nombreuses fonctionnalités. Ce désir de simplicité était une décision consciente des développeurs de langage, et pour bien comprendre à quoi cela servait, nous devons comprendre la motivation des développeurs et ce qu'ils ont accompli dans Go.
Alors pourquoi est-ce si simple? Voici quelques citations de Rob Pike:
Le point clé ici est que nos programmeurs ne sont pas des chercheurs. Ils sont généralement très jeunes, viennent chez nous après l'école, peut-être ont-ils étudié Java, ou C / C ++, ou Python. Ils ne sont pas capables de comprendre une langue exceptionnelle, mais en même temps, nous voulons qu'ils créent de bons logiciels. C'est pourquoi la langue devrait être facile à comprendre et à apprendre.
Il devrait être familier, grosso modo comme C. Les programmeurs Google commencent leur carrière tôt et sont généralement familiarisés avec les langages procéduraux, en particulier la famille C. L'exigence d'une productivité rapide dans un nouveau langage de programmation signifie que le langage ne doit pas être trop radical.
Des mots sages, non?
Artefacts de simplicité
La simplicité est une condition nécessaire au beau. Leo Tolstoy.
Être simple est l'une des aspirations les plus importantes de toute conception. Comme vous le savez, un projet parfait n'est pas un projet où il n'y a rien à ajouter, mais un - dont il n'y a rien à supprimer. Beaucoup pensent que pour résoudre (ou même exprimer) des tâches complexes, un outil complexe est nécessaire. Mais ce n'est pas le cas. Prenons par exemple le langage PERL. Les idéologues de la langue pensaient qu'un programmeur devait avoir au moins trois façons différentes de résoudre un problème. Les idéologues de la langue Go ont pris un chemin différent, ils ont décidé que pour atteindre l'objectif, un chemin est suffisant, mais vraiment bon. Cette approche a un fondement sérieux: le seul moyen est plus facile à apprendre et plus difficile à oublier.
De nombreux migrants se plaignent que la langue ne contient pas d'élégantes abstractions. Oui, c'est vrai, mais c'est l'un des principaux avantages de la langue. La langue contient un minimum de magie - par conséquent, aucune connaissance approfondie n'est requise pour lire le programme. Quant à la verbosité du code, ce n'est pas du tout un problème. Un programme Golang bien écrit est lu verticalement avec peu ou pas de structuration. De plus, la vitesse de lecture d'un programme est au moins d'un ordre de grandeur plus rapide que son écriture. Si nous considérons que tout le code a un formatage uniforme (effectué à l'aide de la commande intégrée gofmt), la lecture de quelques lignes supplémentaires n'est pas du tout un problème.
Pas très expressif
L'art ne tolère pas quand il restreint sa liberté. La précision n'est pas de sa responsabilité.
En raison du désir de simplicité, Go manque de constructions qui, dans d'autres langues, sont perçues comme quelque chose de naturel pour les gens qui y sont habitués. Au début, cela peut être quelque peu gênant, mais vous remarquerez ensuite que le programme est lu beaucoup plus facilement et plus clairement.
Par exemple, un utilitaire de console qui lit stdin ou un fichier à partir d'arguments de ligne de commande ressemblera à ceci:
package main import ( "bufio" "flag" "fmt" "log" "os" ) func main() { flag.Parse() scanner := newScanner(flag.Args()) var text string for scanner.Scan() { text += scanner.Text() } if err := scanner.Err(); err != nil { log.Fatal(err) } fmt.Println(text) } func newScanner(flags []string) *bufio.Scanner { if len(flags) == 0 { return bufio.NewScanner(os.Stdin) } file, err := os.Open(flags[0]) if err != nil { log.Fatal(err) } return bufio.NewScanner(file) }
Bien que la solution au même problème dans le langage D semble un peu plus courte, cependant, elle n'est pas plus facile à lire
import std.stdio, std.array, std.conv; void main(string[] args) { try { auto source = args.length > 1 ? File(args[1], "r") : stdin; auto text = source.byLine.join.to!(string); writeln(text); } catch (Exception ex) { writeln(ex.msg); } }
Copiez l'enfer
L'homme porte l'enfer en lui-même. Martin Luther.
Les débutants se plaignent constamment de Go en termes de manque de génériques. Pour résoudre ce problème, la plupart utilisent la copie directe de code. Par exemple, ces malheureux professionnels pensent que la fonction de synthèse de la liste des entiers ne peut être implémentée autrement que par simple copier-coller pour chaque type de données.
package main import "fmt" func int64Sum(list []int64) (uint64) { var result int64 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func int32Sum(list []int32) (uint64) { var result int32 = 0 for x := 0; x < len(list); x++ { result += list[x] } return uint64(result) } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(int32Sum(list32)) fmt.Println(int64Sum(list64)) }
Le langage dispose de moyens suffisants pour mettre en œuvre de telles constructions. Par exemple, la programmation généralisée est très bien.
package main import "fmt" func Eval32(list []int32, fn func(a, b int32)int32) int32 { var res int32 for _, val := range list { res = fn(res, val) } return res } func int32Add(a, b int32) int32 { return a + b } func int32Sub(a, b int32) int32 { return a - b } func Eval64(list []int64, fn func(a, b int64)int64) int64 { var res int64 for _, val := range list { res = fn(res, val) } return res } func int64Add(a, b int64) int64 { return a + b } func int64Sub(a, b int64) int64 { return a - b } func main() { list32 := []int32{1, 2, 3, 4, 5} list64 := []int64{1, 2, 3, 4, 5} fmt.Println(Eval32(list32, int32Add)) fmt.Println(Eval64(list64, int64Add)) fmt.Println(Eval64(list64, int64Sub)) }
Et, bien que notre code se soit avéré être un peu plus long que le cas précédent, il s'est généralisé. Par conséquent, il ne nous sera pas difficile de mettre en œuvre toutes les opérations arithmétiques.
Beaucoup diront que le programme D semble beaucoup plus court et aura raison.
import std.stdio; import std.algorithm; void main(string[] args) { [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln; }
Cependant, il est seulement plus court, mais pas plus correct, car le problème de gestion des erreurs est complètement ignoré dans l'implémentation D.
Dans la vraie vie, lorsque la complexité de la logique augmente, l'écart se rétrécit rapidement. Encore plus rapidement, l'écart se réduit lorsqu'une action ne peut pas être effectuée à l'aide d'opérateurs de langage standard.
En termes de prise en charge, d'extensibilité, de lisibilité, à mon avis, la langue Go gagne, même si elle perd en verbosité.
La programmation généralisée dans certains cas nous donne des avantages indéniables. Cela illustre clairement le package de tri. Donc, pour trier n'importe quelle liste, il nous suffit d'implémenter l'interface sort.Interface.
import "sort" type Names []string func (ns Names) Len() int { return len(ns) } func (ns Names) Less(i, j int) bool { return ns[i] < ns[j] } func (ns Names) Swap(i, j int) { ns[i], ns[j] = ns[j], ns[i] } func main() { names := Names{"London", "Berlin", "Rim"} sort.Sort(names) }
Si vous prenez un projet open source et exécutez la commande grep "interface {}" -R, vous verrez à quelle fréquence des interfaces confuses sont utilisées. Les camarades bornés diront immédiatement que tout cela est dû au manque de génériques. Cependant, c'est loin d'être toujours le cas. Prenons par exemple la langue DELPHI. Bien qu'il possède ces mêmes génériques, il contient un type spécial VARIANT pour les opérations avec des types de données arbitraires. Go fait la même chose.
D'un pistolet sur des moineaux
Et une camisole de force doit correspondre à la taille de la folie. Stanislav laisse.
De nombreux fans de sports extrêmes peuvent dire que Go a un autre mécanisme pour créer des génériques - la réflexion. Et ils auront raison ... mais seulement dans de rares cas.
Rob Pike nous avertit:
Il s'agit d'un outil puissant qui doit être utilisé avec précaution. Il doit être évité jusqu'à ce qu'il soit strictement nécessaire.
Wikipedia nous dit ce qui suit:
La réflexion désigne un processus au cours duquel un programme peut suivre et modifier sa propre structure et son propre comportement lors de l'exécution. Le paradigme de programmation sous-jacent à la réflexion est appelé programmation réflexive. Il s'agit d'un type de métaprogrammation.
Cependant, comme vous le savez, vous devez tout payer. Dans ce cas, c'est:
- difficulté à écrire des programmes
- vitesse d'exécution du programme
Par conséquent, la réflexion doit être utilisée avec prudence, comme une arme de gros calibre. L'utilisation irréfléchie de la réflexion entraîne des programmes illisibles, des erreurs constantes et une faible vitesse. Le fait est que le programmeur snob pourrait afficher son code devant d'autres collègues plus pragmatiques et modestes.
Bagage culturel de C? Non, à partir de plusieurs langues!
Avec l'État, les héritiers se retrouvent avec des dettes.
Malgré le fait que beaucoup croient que le langage est entièrement basé sur l'héritage de C, ce n'est pas le cas. Le langage a incorporé de nombreux aspects des meilleurs langages de programmation.
Syntaxe
Tout d'abord, la syntaxe des constructions grammaticales est basée sur la syntaxe du langage C. Cependant, le langage DELPHI a également eu un impact significatif. Donc, nous voyons que les parenthèses en excès sont complètement supprimées, ce qui réduit considérablement la lisibilité du programme. De plus, le langage contient l'opérateur :: =, qui est inhérent au langage DELPHI. Le concept de packages est emprunté à des langues comme ADA. La déclaration des entités non utilisées est empruntée au langage PROLOG.
Sémantique
La sémantique du langage DELPHI a été prise comme base des packages. Chaque package encapsule des données et du code et contient des entités privées et publiques. Cela vous permet de réduire au minimum l'interface du package.
L'opération de mise en œuvre par délégation a été empruntée à DELPHI.
Compilation
Pas étonnant qu'il y ait une blague: Go a été développé pendant la compilation du programme C. L'une des forces du langage est la compilation ultra-rapide. L'idée a été empruntée à DELPHI. De plus, chaque package Go correspond au module DELPHI. Ces packages sont recompilés uniquement lorsque cela est nécessaire. Par conséquent, après la prochaine édition, il n'est pas nécessaire de compiler le programme entier, mais il suffit de recompiler uniquement les packages modifiés et les packages en fonction de ces packages modifiés (et cela uniquement si les interfaces des packages ont changé).
Conceptions de haut niveau
Le langage contient de nombreuses constructions de haut niveau différentes qui ne sont en aucun cas associées à des langages de bas niveau tels que C.
- Lignes
- Hachage de table
- Tranches
- Le typage de canard est emprunté à des langues comme RUBY (que beaucoup ne comprennent malheureusement pas et n'utilisent pas à leur plein potentiel).
Gestion de la mémoire
La gestion de la mémoire mérite généralement un article séparé. Si dans des langages comme C ++, le contrôle est entièrement laissé au développeur, puis dans des langages ultérieurs comme DELPHI, un modèle de comptage de référence a été utilisé. Avec cette approche, les liens cycliques n'étaient pas autorisés, car des clusters perdus se sont formés, puis la détection de ces clusters (comme en C #) est intégrée à Go. De plus, le garbage collector est plus efficace que la plupart des implémentations actuellement connues et peut déjà être utilisé pour de nombreuses tâches en temps réel. Le langage lui-même reconnaît les situations où une valeur de stockage d'une variable peut être allouée sur la pile. Cela réduit la charge sur le gestionnaire de mémoire et augmente la vitesse du programme.
Concurrence et concurrence
Le parallélisme et la compétitivité de la langue sont au-delà des louanges. Aucune langue de bas niveau ne peut même rivaliser à distance avec la langue Go. En toute honnêteté, il convient de noter que le modèle n'a pas été inventé par les auteurs de la langue, mais simplement emprunté à la bonne vieille langue de l'ADA. Le langage est capable de traiter des millions de connexions parallèles en utilisant tous les CPU, tout en ayant de l'ordre de grandeur moins courant pour les problèmes complexes de code multithread avec des blocages et des conditions de concurrence.
Des avantages supplémentaires
Si cela est bénéfique, tout le monde deviendra altruiste.
La langue nous offre également un certain nombre d'avantages incontestables:
- Le seul fichier exécutable après la construction du projet simplifie considérablement le déploiement des applications.
- Le typage statique et l'inférence de type peuvent réduire considérablement le nombre d'erreurs dans le code même sans écrire de tests. Je connais certains programmeurs qui se passent du tout d'écriture de tests et en même temps la qualité de leur code ne souffre pas de manière significative.
- Compilation croisée très simple et excellente portabilité de la bibliothèque standard, ce qui simplifie considérablement le développement d'applications multiplateformes.
- Les expressions régulières RE2 sont thread-safe et ont des temps d'exécution prévisibles.
- Une bibliothèque standard puissante, qui permet à la plupart des projets de se passer de frameworks tiers.
- Le langage est assez puissant pour se concentrer sur le problème, et non sur les méthodes pour le résoudre, et en même temps suffisamment faible pour que le problème puisse être résolu efficacement.
- Le système éco Go contient déjà des outils prêts à l'emploi pour toutes les occasions: tests, documentation, gestion des packages, linters puissants, génération de code, détecteur de conditions de course, etc.
- La version 1.11 de Go possède désormais une gestion des dépendances sémantiques intégrée au-dessus des hôtes VCS populaires. Tous les outils qui composent l'écosystème Go utilisent ces services pour télécharger, compiler et installer leur code d'un seul coup. Et c'est super. Avec l'avènement de la version 1.11, le problème de versionnage des packages a également été complètement résolu.
- Étant donné que l'idée principale du langage est de réduire la magie, le langage encourage les développeurs à gérer explicitement la gestion des erreurs. Et c'est correct, car sinon, il oubliera tout simplement la gestion des erreurs. Une autre chose est que la plupart des développeurs ignorent délibérément la gestion des erreurs, préférant plutôt simplement transmettre l'erreur au lieu de les traiter.
- Le langage n'implémente pas la méthodologie OOP classique, car il n'y a pas de virtualité dans sa forme pure dans Go. Cependant, ce n'est pas un problème lors de l'utilisation des interfaces. L'absence de POO réduit considérablement la barrière d'entrée pour les débutants.
Facilité pour les avantages communautaires
Compliquer est simple, simplifier est difficile.
Go a été conçu pour être simple et exceller dans ce domaine. Il a été écrit pour des programmeurs intelligents qui comprennent toutes les vertus du travail d'équipe et sont fatigués de la variabilité infinie des langages de niveau entreprise. Ayant un ensemble relativement petit de constructions syntaxiques dans son arsenal, il n'est pratiquement pas soumis à des changements au fil du temps, par conséquent, les développeurs ont libéré beaucoup de temps spécifiquement pour le développement, et non pour une étude sans fin des innovations linguistiques.
Les entreprises bénéficient également d'un certain nombre d'avantages: un seuil d'entrée bas vous permet de trouver rapidement un spécialiste, et l'immuabilité de la langue vous permet d'utiliser le même code après 10 ans.
Conclusion
La grande taille du cerveau n'a pas encore fait un seul éléphant lauréat du prix Nobel.
Pour les programmeurs dont l'ego personnel prévaut sur l'esprit d'équipe, ainsi que pour les théoriciens qui aiment les tâches académiques et l '"auto-amélioration" sans fin, le langage est vraiment mauvais, car c'est un langage artisanal polyvalent qui ne permet pas de recevoir un plaisir esthétique du résultat de leur travail et de se montrer un professionnel devant des collègues (à condition de mesurer précisément l'esprit avec ces critères, et non avec le coefficient de QI). Comme tout dans la vie, c'est une question de priorités personnelles. Comme toutes les innovations valables, la langue a déjà parcouru un long chemin du déni universel à la reconnaissance de masse. La langue est ingénieuse dans sa simplicité, mais, comme vous le savez, tout ingénieux est simple!
Résumé
Parmi toutes les critiques sévères adressées à Go, les déclarations suivantes ressortent particulièrement:
- Pas de génériques. Si nous regardons les statistiques des langues les plus populaires, nous remarquons que la moitié des dix premières langues n'ont pas de génériques. Généralement, les génériques ne sont nécessaires que dans des conteneurs. Par conséquent, leur gain n'est pas trop important.
- D'autres langues comme Rust sont bien meilleures (au moins dans les catégories de sites XXX). Encore une fois, si nous regardons les statistiques des langues les plus populaires, nous ne trouverons pas du tout Rust dans la liste, ou ce sera quelque part en dessous de la note. Personnellement, j'aime Rust, mais j'ai choisi Go.
- XXX a un tel chignon. C'est le revers de la médaille de la simplicité. Que ce soit un inconvénient ou non, c'est à chacun d'en décider. Cependant, les développeurs du projet ont donné leur préférence en faveur de la simplicité.
- Ils sortiront Go 2.0, puis nous verrons. Cette position est prise par des observateurs, pas des pratiquants.
- Pas assez expressif. Je suis d'accord, dans certains domaines, l'expressivité est boiteuse, mais en général c'est un langage simple et cohérent. De plus, en raison de la pauvreté du langage, nous sommes obligés de prêter plus d'attention à l'architecture de l'application développée, ce qui affecte positivement sa flexibilité.
En fait, l'article ne pensait pas aux avantages syntaxiques du langage Go, mais plutôt à un bref aperçu de ses avantages pour le travail d'équipe et de l'évolution effective du projet en cours de développement. Il était entendu que l'article se serait poursuivi en relation avec des problèmes plus spécifiques. Cependant, en raison du manque d'intérêt pour le sujet, il n'y a probablement pas de suite.
Une expérience
Ne croyez pas les mots - ni les vôtres ni les étrangers, mais croyez les actes - les vôtres et les étrangers.
La dernière partie est destinée exclusivement à cette catégorie de personnes qui se considèrent comme des optimistes à l'esprit constructif et qui peuvent le confirmer avec leurs propres affaires. Le reste du public, veuillez sauter cette partie.
Cette expérience a été inspirée par des amis qui ont affirmé que tous les optimistes à l'esprit constructif avaient quitté depuis longtemps (au moins virtuellement) les étendues de notre pays et s'étaient installés, par exemple, sur Stack Overflow, et qu'ici, la plupart des snobs étaient restés. Pendant longtemps, je ne les ai pas crus, j'ai donc décidé de mener cette expérience.
Plusieurs articles ont été postés sur le hub, résultat de l'analyse des commentaires sur lesquels je cite.
- En effet, l'hypothèse de mes amis a été confirmée, cependant, des personnes adéquates se trouvent encore parmi les colporteurs, bien que leur pourcentage diminue rapidement. Yuri Bykov qualifie ces gens de " fous " sur lesquels repose tout le pays. Selon lui, leur pourcentage est faible (environ 2%). Je ne suis pas si pessimiste et je pense qu'il y en a beaucoup plus.
- La loi des médias. Les informations destructives présentent un intérêt beaucoup plus grand que les informations constructives.
- La psychologie de la foule. C'est une chose terrible, cela fait même un mouton violent d'une personne adéquate. Un homme dans une foule n'est plus un homme. On ne peut parler d’objectivité. Aucun argument logique, aucune source faisant autorité ou précédent ne l'affecte plus.
- Responsabilité et impunité. Les gens sont prêts à humilier un autre pour s’exalter (au moins à leurs propres yeux). Surtout si vous n'avez pas à répondre (ce qui pourrait être plus facile - cliquez sur le signe moins et vous n'avez même pas besoin d'écrire un commentaire). Il y a autant de points communs entre les paroles et les actes qu'entre un canal et un égout.
- Vanité. La plupart des snobs sont prêts à se démarquer de quelque façon que ce soit. Ils n'ont peur d'aucune barrière morale.
- Pessimisme Contrairement aux pays occidentaux (et en particulier l'Amérique), les sentiments pessimistes prévalent dans le pays. Comme vous le savez, un optimiste recherche des opportunités au milieu des difficultés, et un pessimiste cherche des difficultés au milieu des opportunités. Dans notre pays, presque personne ne fait attention aux qualités positives de quoi que ce soit.
- Professionnalisme et vision du monde. La plupart des gens choisissent les outils comme une fin en soi et non comme un moyen d'arriver à une fin. Les gens ont oublié comment travailler avec des informations. Les gens ne voient pas les forêts derrière les arbres. À partir d'un éventail d'informations, ils ne sont pas en mesure d'extraire les principales pensées. Personne ne veut regarder d'un point de vue différent et non standard pour eux-mêmes. La dissidence est supprimée. Ce n'est pas accepté ici.
- Convivialité et respect. Les groupes amicaux loués n'existent que par des mots. Les valeurs de développement agile sont uniquement sur papier.
- Hypocrisie. Vous pouvez écrire un article séparé à ce sujet en général.
- Principe. Il y a des gens qui posent la bonne question: « Qu'est-ce que je fais? »Cependant, tout le monde ne comprend pas que, par manque de principe, pour nous, l'intérêt égoïste momentané est plus important que tous nos principes réunis. Il est plus facile de tout blâmer sur les circonstances et de dire que rien ne dépend de nous.
Avec un profond respect et de la sympathie pour tous les optimistes à l'esprit constructif.
Adverax