Pourquoi Go Design est mauvais pour les programmeurs intelligents

Au cours des derniers mois, j'ai utilisé Go pour les implémentations. Proof of Concept (environ.: code pour tester la fonctionnalité d'une idée) pendant son temps libre, en partie pour étudier le langage de programmation lui-même. Les programmes eux-mêmes sont très simples et ne constituent pas le but de cet article, mais l'expérience d'utilisation de Go elle-même mérite quelques mots à ce sujet. Allez promet d'être (environ.: article écrit en 2015) un langage populaire pour du code sérieux et évolutif. Le langage a été créé par Google, où il est activement utilisé. En fin de compte, je pense honnêtement que la conception du langage Go est mauvaise pour les programmeurs intelligents.

Conçu pour les programmeurs faibles ?

Go est très facile à apprendre, si simple que l'introduction m'a pris une soirée, après quoi je pouvais déjà coder de manière productive. Le livre que j'ai appris à jouer au Go s'appelle Une introduction à la programmation en Go (traduction), il est disponible en ligne. Le livre, comme le code source de Go lui-même, est facile à lire, contient de bons exemples de code et contient environ 150 pages pouvant être lues en une seule fois. Cette simplicité est rafraîchissante au début, surtout dans un monde de programmation rempli de technologies trop compliquées. Mais à la fin, tôt ou tard, la pensée surgit : « Est-ce vraiment le cas ?

Google affirme que la simplicité de Go est son argument de vente et que le langage est conçu pour une productivité maximale dans les grandes équipes, mais j'en doute. Certaines fonctionnalités sont manquantes ou trop détaillées. Et tout cela à cause d’un manque de confiance dans les développeurs, avec l’hypothèse qu’ils ne sont pas capables de faire quoi que ce soit de bien. Ce désir de simplicité était une décision consciente de la part des concepteurs du langage, et afin de bien comprendre pourquoi cela était nécessaire, nous devons comprendre la motivation des développeurs et ce qu'ils essayaient de réaliser dans Go.

Alors pourquoi a-t-il été rendu si simple ? Voici quelques citations Rob Pike (environ.: l'un des co-créateurs du langage Go) :

Le point clé ici est que nos programmeurs (environ.: Googleurs) ne sont pas des chercheurs. Ils sont généralement assez jeunes et viennent chez nous après des études, peut-être ont-ils étudié Java, ou C/C++, ou Python. Ils ne peuvent pas comprendre un bon langage, mais en même temps, nous voulons qu'ils créent de bons logiciels. C’est pourquoi leur langue doit être facile à comprendre et à apprendre.
 
Il devrait être familier, à peu près similaire à C. Les programmeurs travaillant chez Google commencent leur carrière tôt et sont pour la plupart familiers 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.

Quoi? Donc Rob Pike dit en gros que les développeurs de Google ne sont pas très bons, c'est pourquoi ils ont créé un langage pour les idiots (environ.: abrutissant) pour qu'ils soient capables de faire quelque chose. Quel genre de regard arrogant envers vos propres collègues ? J'ai toujours pensé que les développeurs de Google étaient triés sur le volet parmi les plus brillants et les meilleurs de la planète. Ils peuvent sûrement gérer quelque chose de plus difficile ?

Des artefacts d’une simplicité excessive

Être simple est un objectif louable dans toute conception, et essayer de faire quelque chose de simple est difficile. Cependant, lorsqu’on tente de résoudre (ou même d’exprimer) des problèmes complexes, un outil complexe est parfois nécessaire. La complexité et l'intrication ne sont pas les meilleures caractéristiques d'un langage de programmation, mais il existe un juste milieu dans lequel le langage peut créer des abstractions élégantes, faciles à comprendre et à utiliser.

Pas très expressif

En raison de son engagement en faveur de la simplicité, Go manque de constructions perçues comme naturelles dans d'autres langages. Cela peut sembler une bonne idée au début, mais en pratique, cela aboutit à un code détaillé. La raison en est évidente : il doit être facile pour les développeurs de lire le code des autres, mais en fait, ces simplifications ne font que nuire à la lisibilité. Il n’y a pas d’abréviations en Go : soit beaucoup, soit rien.

Par exemple, un utilitaire de console qui lit stdin ou un fichier à partir des arguments de ligne de commande ressemblerait à ceci :

package main

import (
    "bufio"
    "flag"
    "fmt"
    "log"
    "os"
)

func main() {

    flag.Parse()
    flags := flag.Args()

    var text string
    var scanner *bufio.Scanner
    var err error

    if len(flags) > 0 {

        file, err := os.Open(flags[0])

        if err != nil {
            log.Fatal(err)
        }

        scanner = bufio.NewScanner(file)

    } else {
        scanner = bufio.NewScanner(os.Stdin)
    }

    for scanner.Scan() {
        text += scanner.Text()
    }

    err = scanner.Err()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(text)
}

Bien que ce code essaie également d'être aussi général que possible, la verbosité forcée de Go gêne et, par conséquent, la résolution d'un problème simple entraîne une grande quantité de code.

Voici, par exemple, une solution au même problème dans D:

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);
    }
}

Et qui est le plus lisible maintenant ? Je donnerai mon vote à D. Son code est beaucoup plus lisible car il décrit les actions plus clairement. D utilise des concepts beaucoup plus complexes (environ.: appel de fonction alternative и Modèles) que dans l'exemple Go, mais il n'y a vraiment rien de compliqué à les comprendre.

L'enfer de la copie

Une suggestion populaire pour améliorer Go est la généralité. Cela aidera au moins à éviter la copie inutile du code pour prendre en charge tous les types de données. Par exemple, une fonction de sommation d'une liste d'entiers ne peut être implémentée autrement qu'en copiant-collant sa fonction de base pour chaque type d'entier ; il n'y a pas d'autre moyen :

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 int16Sum(list []int16) (uint64) {
    var result int16 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func int8Sum(list []int8) (uint64) {
    var result int8 = 0
    for x := 0; x < len(list); x++ {
        result += list[x]
    }
    return uint64(result)
}

func main() {

    list8  := []int8 {1, 2, 3, 4, 5}
    list16 := []int16{1, 2, 3, 4, 5}
    list32 := []int32{1, 2, 3, 4, 5}
    list64 := []int64{1, 2, 3, 4, 5}

    fmt.Println(int8Sum(list8))
    fmt.Println(int16Sum(list16))
    fmt.Println(int32Sum(list32))
    fmt.Println(int64Sum(list64))
}

Et cet exemple ne fonctionne même pas pour les types signés. Cette approche viole complètement le principe de ne pas se répéter (ASSÉCHER), l’un des principes les plus connus et les plus évidents, dont l’ignorance est à l’origine de nombreuses erreurs. Pourquoi Go fait-il ça ? C'est un aspect terrible du langage.

Même exemple sur D :

import std.stdio;
import std.algorithm;

void main(string[] args)
{
    [1, 2, 3, 4, 5].reduce!((a, b) => a + b).writeln;
}

Simple, élégant et va droit au but. La fonction utilisée ici est reduce pour le type de modèle et le prédicat. Oui, c'est encore une fois plus compliqué que la version Go, mais pas si difficile à comprendre pour les programmeurs intelligents. Quel exemple est le plus facile à maintenir et à lire ?

Contournement du système de type simple

J'imagine que les programmeurs Go qui liront ceci auront l'écume à la bouche et crieront : « Vous vous trompez ! » Eh bien, il existe une autre façon de créer une fonction et des types génériques, mais cela brise complètement le système de types !

Jetez un œil à cet exemple de correctif de langage stupide pour contourner le problème :

package main

import "fmt"
import "reflect"

func Reduce(in interface{}, memo interface{}, fn func(interface{}, interface{}) interface{}) interface{} {
    val := reflect.ValueOf(in)

    for i := 0; i < val.Len(); i++ {
        memo = fn(val.Index(i).Interface(), memo)
    }

    return memo
}

func main() {

    list := []int{1, 2, 3, 4, 5}

    result := Reduce(list, 0, func(val interface{}, memo interface{}) interface{} {
        return memo.(int) + val.(int)
    })

    fmt.Println(result)
}

Cette mise en œuvre Reduce a été emprunté à l'article Génériques idiomatiques en Go (environ.: Je n'ai pas trouvé la traduction, je serai heureux si vous m'aidez). Eh bien, si c'est idiomatique, je détesterais voir un exemple non idiomatique. Usage interface{} - une farce, et dans la langue, il suffit de contourner la frappe. Il s'agit d'une interface vide et tous les types l'implémentent, permettant une liberté totale à chacun. Ce style de programmation est terriblement moche, et ce n'est pas tout. Des prouesses acrobatiques comme celles-ci nécessitent l’utilisation de la réflexion à l’exécution. Même Rob Pike n'aime pas les individus qui en abusent, comme il l'a mentionné dans l'un de ses rapports.

Il s’agit d’un outil puissant qui doit être utilisé avec prudence. Cela doit être évité sauf si cela est strictement nécessaire.

Je prendrais des modèles D au lieu de ces bêtises. Comment peut-on dire ça interface{} plus lisible ou même plus sûr ?

Les malheurs de la gestion des dépendances

Go dispose d'un système de dépendance intégré construit sur les fournisseurs d'hébergement populaires VCS. Les outils fournis avec Go connaissent ces services et peuvent télécharger, créer et installer du code à partir d'eux d'un seul coup. Bien que ce soit génial, il y a un défaut majeur avec le versioning ! Oui, il est vrai que vous pouvez obtenir le code source à partir de services comme github ou bitbucket à l'aide des outils Go, mais vous ne pouvez pas spécifier la version. Et encore une fois la simplicité au détriment de l'utilité. Je n'arrive pas à comprendre la logique d'une telle décision.

Après avoir posé des questions sur une solution à ce problème, l'équipe de développement de Go a créé fil de discussion, qui décrivait comment ils allaient contourner ce problème. Leur recommandation était de simplement copier un jour l'intégralité du référentiel dans votre projet et de le laisser « tel quel ». À quoi pensent-ils ? Nous disposons d'étonnants systèmes de contrôle de version avec un excellent balisage et une prise en charge des versions que les créateurs de Go ignorent et copient simplement le code source.

Bagage culturel de Xi

À mon avis, Go a été développé par des gens qui avaient utilisé C toute leur vie et par ceux qui ne voulaient pas essayer quelque chose de nouveau. Le langage peut être décrit comme C avec des roues supplémentaires (orig.: roues d'entrainement). Il n'y a pas d'idées nouvelles, à l'exception du support du parallélisme (qui d'ailleurs est merveilleux) et c'est dommage. Vous avez un excellent parallélisme dans un langage boiteux à peine utilisable.

Un autre problème grinçant est que Go est un langage procédural (comme l’horreur silencieuse du C). Vous finissez par écrire du code dans un style procédural qui semble archaïque et dépassé. Je sais que la programmation orientée objet n'est pas une solution miracle, mais ce serait formidable de pouvoir résumer les détails en types et fournir une encapsulation.

La simplicité pour votre propre bénéfice

Go a été conçu pour être simple et il atteint cet objectif. Il a été écrit pour les programmeurs faibles, en utilisant un ancien langage comme modèle. Il est livré avec des outils simples pour réaliser des choses simples. Il est facile à lire et à utiliser.

C'est extrêmement verbeux, peu impressionnant et mauvais pour les programmeurs intelligents.

merci mersinvald pour les modifications

Source: habr.com

Ajouter un commentaire