Go (langage)

Go est un langage de programmation compilé et concurrent inspiré de C et Pascal. Ce langage a été développé par Google[6] à partir d’un concept initial de Robert Griesemer, Rob Pike et Ken Thompson. Go possède deux implémentations : la première utilise gc, le compilateur Go ; la seconde utilise gccgo, « frontend » GCC écrit en C++. Go est écrit en C en utilisant yacc et GNU Bison pour l’analyse syntaxique[7] jusqu’à la version 1.4, et en Go lui-même pour les versions suivantes (1.5).

Mascotte de Google Go.
Pour les articles homonymes, voir GO.
Cet article concerne le langage de programmation open source créé par Google en 2009 . Pour le langage de programmation créé en 2003 , voir Go! (langage). Pour go dans d’autres contexte, voir GO.

Go

Date de première version
Paradigme Langage compilé, programmation concurrente, impérative et structurée
Auteur Robert Griesemer (en)
Rob Pike
Ken Thompson
Dernière version 1.17 ()[1]
Version en développement 1.16beta1 ()[2]
Typage Fort, statique, structurel
Influencé par C
Python
Oberon-2 (en)
Limbo
Active Oberon (en)
Communicating sequential processes
Pascal
Oberon
Smalltalk
Newsqueak (en)
Modula-2
Alef
APL
BCPL
Modula (en)
Occam
Système d'exploitation Windows, GNU/Linux, Mac OS X, FreeBSD, OpenBSD, DragonflyBSD, Solaris, Plan 9[3]
Licence Licence BSD[4], breveté[5]
Site web golang.org
Extension de fichier go

Un objectif de Go est donné par Rob Pike, l’un de ses trois créateurs, qui dit à propos des développeurs inexpérimentés[8] :

« Ils ne sont pas capables de comprendre un langage brillant, mais nous voulons les amener à réaliser de bons programmes. Ainsi, le langage que nous leur donnons doit être facile à comprendre et facile à adopter »

Go veut faciliter et accélérer la programmation à grande échelle : en raison de sa simplicité, il est donc concevable de l’utiliser aussi bien pour écrire des applications, des scripts ou de grands systèmes. Cette simplicité est nécessaire aussi pour assurer la maintenance et l’évolution des programmes sur plusieurs générations de développeurs.

S’il vise aussi la rapidité d’exécution, indispensable à la programmation système, il considère le multithreading comme le moyen le plus robuste d’assurer sur les processeurs actuels cette rapidité[9] tout en rendant la maintenance facile par séparation de tâches simples exécutées indépendamment afin d’éviter de créer des « usines à gaz ». Cette conception permet également le fonctionnement sans réécriture sur des architectures multi-cœurs en exploitant immédiatement l’augmentation de puissance correspondante.

« Hello, world »

Voici un exemple d'un programme Hello world typique écrit en Go :

package main

import "fmt"

func main() {
	fmt.Printf("Hello, world\n")
}

Caractéristiques

Le langage Go a été créé pour la programmation système et a depuis été étendu aux applications, ce qui constitue la même cible que le C et surtout le C++. Il s'agit d'un langage impératif et concurrent. Sa vitesse de compilation (due à la simplicité de sa syntaxe) le fait parfois utiliser comme langage de script.

Concurrence

Go intègre directement, comme Java, les traitements de code en concurrence. Le mot clé go permet à un appel de fonction de s'exécuter en concurrence avec la goroutine courante[10]. Une goroutine, ainsi nommée par analogie lointaine avec les coroutines, est un fil d'exécution supervisé par l'ordonnanceur inclus dans le runtime[11]. Le programme prendra alors avantage de la topologie de l'ordinateur pour exécuter au mieux les goroutines, pas forcément dans un nouveau thread, mais il est aussi possible qu'un groupe de goroutines soit multiplexé sur un groupe de threads[12].

Pour appeler une fonction f, on écrit f(). Pour l'appeler en tant que goroutine, on écrit simplement go f(), ce qui est très semblable au call f task; de PL/I, langage gérant également le multitâche depuis 1970.

Les goroutines communiquent entre elles par passage de messages, en envoyant ou en recevant des messages sur des canaux[13].

Ces messages synchronisent les goroutines entre elles conformément au modèle CSP, considéré par les auteurs comme plus intuitif que le modèle multi-threadé (avec synchronisation par sémaphores comportant des verrous, notion introduite elle aussi par Dijkstra).

Faux-amis

Dans l'état actuel du langage (2018)

Le langage contient des aspects de Pascal et de C, mais on s'expose à beaucoup d'erreurs si on oublie momentanément qu'on n'est ni en Pascal ni en C. Ainsi a:=b alloue une variable a en lui affectant la valeur et le type de b, mais si la variable a déjà été allouée il faudra se contenter d'écrire a=b. À ne pas confondre avec a==b (égalité de valeurs). L'expression derrière un if n'a pas besoin de parenthèses, mais l'expression à exécuter en cas de réussite du test devra être entre accolades. Dans son état actuel, le compilateur ne tolère pas qu'une variable déclarée ne soit pas utilisée, ce qui encourage certes de bonnes pratiques, mais rend fort pénibles les tâtonnements de mise au point des programmes.

Collections d'objets

Go connaît les types scalaires (entiers int ou int64, flottants float, chaînes string), les tableaux indexés par des entiers à partir de 0, les maps qui sont des collections d'objets indexés par des clés (nommés dictionnaires, hashes ou tableaux associatifs dans d'autres langages) et des slices qui sont une généralisation dynamique des tableaux.

Il accède aisément aux fichiers en lecture comme en écriture, que ce soit en mode ligne, ou caractère, ou bien en absorbant le fichier complet, par les packages os et io.

Système de types

Go a un système de type statique, fortement typé, structurel et sûr, fondé sur l'inférence de types avec la possibilité d'utiliser un typage explicite.

Pour donner un exemple, écrire s := "Camélia", qui déclare, alloue et initialise s, est possible et n'oblige pas à écrire var s string = "Camélia", qui reste cependant accepté.

La compatibilité des types composés est fondée sur les propriétés plutôt que sur le nom. C'est-à-dire que deux types composés seront équivalents si leurs propriétés sont équivalentes : même nom pour la propriété et équivalence de type. C'est le typage structurel.

Cela a pour conséquence que le langage n'est pas objet au sens classique (soit avec classes, soit avec prototype), cependant les concepteurs du langage ont fait un choix plus original pour un langage statique. Il est possible de définir des interfaces portant des méthodes décrivant le comportement d'un objet (il est aussi facilement possible de mélanger plusieurs interfaces en une seule)[14]. Les fonctions Go peuvent déclarer accepter un argument de cette interface. Un objet déclarant toutes les méthodes de cette interface, avec la même signature, peut être passé en argument de cette méthode. La vérification du type est effectuée statiquement par le compilateur.

Le fait que Go ne soit pas objet au sens classique fait que Go n'a pas d'héritage de type et pas de sous-classage. Ceci permet de contourner les problèmes posés par ces systèmes tels l'héritage multiple dans les langages qui le permettent (en C++ par exemple), ou l'héritage simple (en Java par exemple). Grâce à l'équivalence de types fondée sur les propriétés, Go n'a pas besoin d'héritage de type. Le sous-classage est émulé par l'« embarquement de type ». Ceci permet de mélanger facilement deux bases de code conçues indépendamment, sans qu'elles aient besoin de partager des types communs.

La visibilité des structures, attributs, variables, constantes, méthodes, types de haut niveau et des fonctions hors de leur paquetage de déclaration est définie par la casse du premier caractère de leurs identificateurs.

Runes

Go travaille en Unicode aussi bien pour son code source que pour son traitement des chaînes. Sa littérature abandonne toutefois l'expression code points pour l'abréviation de runes. Des procédures permettent de transformer des représentations de caractère en runes (toute rune occupant une taille fixe[15]) et transformer par des procédures standards une suite de caractères Unicode en runes d'un tableau (une rune par élément), ainsi que l'inverse, sans avoir à jongler avec les représentations de longueur variables ni s'occuper de savoir si la machine est little-endian ou big-endian. La portabilité est donc assurée.

Parler de runes évite les ambiguités qui seraient présentes avec caractère ou octet[16]. Pour explorer une chaîne, soit on utilise directement des fonctions de chaîne, soit on la parcourt de rune en rune.

Les mises en capitales de caractères nationaux (par exemple "è"⇒ "È") se font simplement par unicode.ToUpper(r rune) rune. Les runes permettent de spécifier en tout caractère Unicode, thaï, chinois, grec, y compris du langage APL- et également toute émoticône s'y trouvant.

Divers

Dans Go, la gestion de la mémoire est assurée par un ramasse-miettes[14].

Il n'y a pas encore de programmation générique même si les concepteurs du langage y réfléchissent. Il n'y a pas de surcharge de méthodes ou d'arithmétique des pointeurs. Enfin, il n'y a pas d'assertions ou d'exceptions. Pour remplacer ces deux derniers, Go fournit les mots clés defer, panic et recover[17] qui donnent des mécanismes similaires aux systèmes de gestion des exceptions de langages tels que C++ et Java (mots clés try, catch, finally et throw).

Go peut s'interfacer avec des bibliothèques en C/C++, des développeurs tiers ayant déjà développé des bindings pour SDL et MySQL.

Go définit un format de code standard (au niveau des indentations, et de la présentation des structures de contrôle) et fournit un outil pour l'appliquer (go fmt).

Go propose également un système de documentation à partir du code et un framework de test.

L'unité de compilation de go est le package qui est représenté dans l'implémentation standard par un répertoire et les fichiers directement contenus dans ce répertoire.

L'import d'un package se fait par son chemin d'importation et peut préciser soit une bibliothèque standard, soit également des packages tiers installés dans des dépôts de sources distants (actuellement supporté : dépôt sous svn, git, mercurial et bazaar).

Extensions expérimentales

Bien que Go soit au départ destiné à produire des applications système robustes et non des programmes destinés à des utilisateurs, des liens avec OpenGL sont développés à titre expérimental[18].

Bibliothèques

Liste des bibliothèques de Go (début).

Comme le C, Go exige d'indiquer quelles bibliothèques on utilisera. À la différence du C, il considère la compilation en erreur si cette bibliothèque n'est pas utilisée. Le compilateur Go ne contient en effet pas de messages d'avertissements par choix des concepteurs : « Il n'est pas indispensable de signaler ce qu'il ne serait pas indispensable de corriger ».

Principales bibliothèques :

  • fmt fournit 19 fonctions de mise en forme des entrées-sorties dont Println, Print et Printf ;
  • io fournit des procédures d'entrées-sorties elles-mêmes, dont ReadFull, Seek, WriteString...
  • math fournit un ensemble de fonctions mathématiques, telles que Min, Max, Sqrt, Log...
  • os interface le système, par exemple pour ouvrir et fermer des fichiers, dont OpenFile, ReadAt...
  • strings fournit une cinquantaine de fonctions traitant des chaînes de caractères ;
  • time fournit des outils de mesure des temps d'exécution, dont time.Now et time.Since().

Les bibliothèques sont tantôt indépendantes, tantôt dépendantes. Il existe par ailleurs plusieurs façons de faire des choses similaires. Par exemple pour lire un fichier, on pourra utiliser ioutil.ReadAll, file.Read() ou bufio.NewScanner().

On obtient la liste des bibliothèques depuis la ligne de commande par go list all.

Exemple de programme

Go admet l'appel récursif des programmes, ce qui peut parfois les rendre plus lisibles, sans perte de vitesse excessive :

Calcul récursif de la suite de Fibonacci par un programme go chronométré. La récursivité rend ici uniquement le programme plus lisible, car on arriverait au même résultat avec une simple boucle. L'instruction commençant par if s[n] !=0 réutilise directement tout résultat déjà calculé. Si on la supprime, la lenteur du programme devient calamiteuse.
package main

import "fmt"
import "time"

var s [61]int

func fib(n int) int {
        if n < 3 {
                return 1
        }
        if s[n] != 0 {
                return s[n]
        }
        s[n] = fib(n-1) + fib(n-2)
        return s[n]

}

func main() {
        var i int
        t0 := time.Now()
        for i = 1; i <= 60; i++ {
                fmt.Printf("fib(%d) = %-15d\t", i, fib(i))
        }
        println()
        println("Durée :", time.Since(t0).Seconds())
}

Ce programme est formaté à la manière standard du go par l'utilitaire gofmt avec les options -s (simplifier le code si possible) et -w (écrire le code modifié in-situ). Cet utilitaire aligne les niveaux de profondeur du programme, facilitant la maintenance en particulier si plusieurs développeurs doivent maintenir le même code. On remarquera qu'il n'aligne pas verticalement les accolades ouvrantes et fermantes, la plupart des éditeurs actuels (2018) se chargeant de signaler visuellement les correspondances en mode édition.

Possibilités évoluées, syntaxe désuète, lisibilité par rapport à C

Les choix syntaxiques de Go ne font pas l'unanimité. Si le langage se réclame simpliste dans son écriture, certains lui reprochent des partis pris trop impactants et dogmatiques à cet égard[19] en qualifiant sa syntaxe de confuse[20],[21] et son compilateur d'une rigidité questionnable[22], citant entre autres :

  • la portée des variables contrôlée par la présence ou non d'une majuscule ;
  • l'absence d'opérateur ternaire ;
  • le manque de sucres syntaxiques ;
  • le refus de compilation s'il existe des variables non utilisées ;
  • la gestion des erreurs et notamment les conditions répétitives inhérentes à leur vérification[23] ;
  • l'inconsistence de certaines APIs du langage comme les channels[24].

Le langage s'est fait reprocher d'avoir « un moteur de Ferrari dans une carrosserie de Ford T »[25].

En revanche, Go propose de simplifier la syntaxe du C, intentionnellement conservée en C++ pour assurer la compatibilité ascendante, en supprimant par exemple les parenthèses syntaxiquement inutiles derrière le if et le for, en proposant un fall-through par défaut dans un switch, etc. Ces modifications peuvent fournir des programmes plus lisibles[26].

Projets utilisant Go

Liste d’applications libres notables écrits en Go[27] :

  • Docker - Création, déploiement et exécution d'application dans des conteneurs.
  • Go Ethereum : Une des trois implémentations originales (avec C ++ et Python) du protocole Ethereum.
  • Gogs - Forge multiplateforme fondée sur Git.
  • Grafana - Outil de monitoring, d'analyse de métriques et de création de tableaux de bord.
  • Kubernetes - Système de gestion d'applications conteneurisées.
  • Hugo : générateur de sites web statiques.
  • Caddy (en) : Serveur de pages Web.
  • Syncthing : application de synchronisation de fichiers pair à pair open source disponible pour Windows, Mac, Linux, Android, Solaris, Darwin et BSD.
  • Terraform : Environnement logiciel d'« infrastructure as code » publié en open-source par la société HashiCorp.
  • Rclone : Utilitaire de synchronisation de fichiers dans le Cloud ou en montage de système de fichiers.

Notes et références

  1. « Go 1.17 »
  2. « https://twitter.com/golang/status/1339638551122685952 »
  3. (en) « Go on Plan9 ».
  4. (en) « Text file LICENSE » (consulté le ).
  5. (en) « Additional IP Rights Grant », sur golang (consulté le ).
  6. (en) https://techcrunch.com/2009/11/10/google-go-language/.
  7. (en) « FAQ - The Go Programming Language », golang.org, 6 octobre 2011.
  8. (en) « From Parallel to Concurrent ».
  9. (en) [vidéo] Rob Pike sur la programmation concurrente sur YouTube.
  10. Cette notion avait été introduite dans PL/I en 1970 en faisant suivre un appel de sous-programme du mot TASK.
  11. « Frequently Asked Questions (FAQ) - The Go Programming Language », sur golang.org (consulté le )
  12. « Frequently Asked Questions (FAQ) - The Go Programming Language », sur golang.org (consulté le )
  13. Voir Message Passing Interface.
  14. (en) Rob Pike, « Go at Google » (consulté le ).
  15. La version 6.3 d'Unicode comprenant 210 000 possibilités de caractères, plus de 21 bits sont nécessaires et on peut supposer qu'une rune occupera la même place qu'un int32. Le standard initial de Go était qu'une rune occupe autant de place qu'un int16. Aucun code source n'a dû pour autant être modifié.
  16. Chaînes en langage Go : les runes
  17. « Defer, Panic, and Recover », sur blog.golang.org, .
  18. (en) https://github.com/go-gl.
  19. (en) « Ten Reasons Why I Don't Like Golang », sur www.teamten.com, (consulté le )
  20. (en) « My Thoughts on Go », sur blog.goodstuff.im, (consulté le )
  21. (en) Cliff Berg, « The "go" language is a mess », sur Value-Driven IT, (consulté le )
  22. (en) « Go is a poorly designed language », sur aletheia.icu, (consulté le )
  23. (en) Jesse Hallett, « Changes I would make to Go », sur sitr.us, (consulté le )
  24. (en) « Go channels are bad and you should feel bad », sur www.jtolio.com, (consulté le )
  25. Langage Go : une Ford T avec un moteur Ferrari
  26. « A C++ developer looks at Go (the programming language), Part 1: Simple Features », sur www.murrayc.com (consulté le )
  27. (en) « Go Projects »

Annexes

Liens externes

  • Portail de la programmation informatique
  • Portail des logiciels libres
  • Portail de Google
  • Portail de l’informatique
Cet article est issu de Wikipedia. Le texte est sous licence Creative Commons - Attribution - Partage dans les Mêmes. Des conditions supplémentaires peuvent s'appliquer aux fichiers multimédias.