Premier Training & Business Partner Red Hat

Il linguaggio Go – seconda parte

Andrea Manzini
Ti piacerebbe diventare anche tu uno di noi e
pubblicare i tuoi articoli nel blog degli RHCE italiani?

HEY! HO! LET’S GO!

11 novembre 2009, con un post dal titolo di una famosa canzone dei Ramones, Google annunciava ufficialmente la nascita di un nuovo linguaggio di programmazione open source, denominato proprio “Go”.

Il linguaggio di programmazione Go è un progetto open source di google per rendere più produttivi i programmatori. Nell’intenzione dei loro creatori, Go è conciso, espressivo, efficiente e pulito. Una volta installato, il programmatore ha subito a disposizione una ‘cassetta degli attrezzi’ ben fornita. Ad esempio solo scrivendo

$ go

ci appare l’elenco dei comandi disponibili:

build       compile packages and dependencies
clean       remove object files and cached files
doc         show documentation for package or symbol
env         print Go environment information
bug         start a bug report
fix         update packages to use new APIs
fmt         gofmt (reformat) package sources
generate    generate Go files by processing source
get         download and install packages and dependencies
install     compile and install packages and dependencies
list        list packages
run         compile and run Go program
test        test packages
tool        run specified go tool
version     print Go version
vet         report likely mistakes in packages

Vediamo dunque che il compilatore Go che abbiamo installato comprende anche documentazione, gestore dei pacchetti, build automation, test, installer, e molto altro!

Nello scorso articolo abbiamo elencato le principali caratteristiche del linguaggio ed abbiamo installato il compilatore, provando subito il classico “Hello, World” ; ora e’ il momento di mettersi all’opera. Anzichè perdersi in lunghe descrizioni sulla sintassi, per rispettare la filosofia di Go ho pensato di preparare dei programmi didattici, commentandoli insieme così da prendere subito confidenza.

Il primo programma è molto semplice; chiunque abbia rudimenti di un altro linguaggio C-like capirà subito di cosa si tratta

package main

import (
    "fmt"
    "time"
)

func scrivi(n int) {
    fmt.Println(n)
    time.Sleep(1 * time.Second)
}

func main() {
    for i := 1; i <= 10; i++ {
	scrivi(i)
    }
    time.Sleep(1 * time.Second)
}

Vediamo rapidamente come è composto il programma: subito dopo la dichiarazione del package, abbiamo 2 direttive import, raggruppate dentro una parentesi: in questo modo diciamo a Go che abbiamo intenzione di usare alcune funzioni di queste librerie. Di seguito una dichiarazione: la funzione scrivi accetta un parametro n, di tipo intero e non restituisce alcun valore; l’unica cosa che fa è stampare a video il numero che riceve e aspettare un secondo prima di ritornare. Notiamo già alcune cose, ad esempio che in Go il tipo delle variabili va messo dopo l’identificativo, a differenza di altri linguaggi come C, C# o Java… E che finalmente non serve più mettere il punto-e-virgola alla fine di ogni istruzione! Quante volte ce lo siamo dimenticati ?

Abbiamo infine la nostra main, il cuore del programma: con un ciclo for da 1 a 10, chiamiamo 10 volte la funzione “scrivi”; al termine del ciclo il programma si ferma per un secondo di attesa e poi termina.

Salviamo il programma in un file (ad esempio contanumeri.go) e prima di lanciare il nostro programma, proviamo a digitare

$ go fmt contanumeri.go

il sorgente verrà automaticamente “formattato” , ovvero indentato con la spaziatura e le parentesi giuste per rispettare le linee guida del linguaggio. Ora compiliamo con

$ go build contanumeri.go 

e lanciamo l’eseguibile risultante, per ottenere l’ovvio risultato dei numeri in fila da 1 a 10.

$ time ./contanumeri
1
2
3
4
5
6
7
8
9
10

real    0m11,006s
user    0m0,004s
sys     0m0,003s

Niente di sconvolgente, ma abbiamo fatto qualche passo avanti: imparare Go non è affatto difficile!

Con questo sorgente possiamo fare un esperimento interessante: prendiamo le righe con time.Sleep e le commentiamo…

package main

import (
    "fmt"
    "time"
)

func scrivi(n int) {
    fmt.Println(n)
    //time.Sleep(1 * time.Second)
}

func main() {
    for i := 1; i <= 10; i++ {
	scrivi(i)
    }
    //time.Sleep(1 * time.Second)
}

compilando il codice, avremo un messaggio di errore. Dove sta il problema ?

$ go build contanumeri.go
.\contanumeri.go:5: imported and not used: "time"

Il nostro sorgente contiene infatti una direttiva “import” per usare un package, ma di fatto non lo sta usando: questo causa un errore di compilazione in Go ed e’ un comportamento voluto: in Go si vuole forzare il programmatore alla pulizia, all’essenzialità e all’efficienza del codice: un programma che include un package per non utilizzarlo non viene neppure compilato.

Ora togliamo il commento alle righe di prima e proviamo una delle caratteristiche più potenti di Go: abbiamo visto che la funzione “scrivi” è (artificialmente, nel nostro caso) piuttosto lenta: impiega ben un secondo ad essere eseguita. In tutto infatti il programma occupa il nostro pc per circa 11 secondi. Grazie a Go, possiamo lanciare in parallelo tante copie di “scrivi” che gireranno in modo indipendente; il bello è che per far questo ci basterà modificare UNA sola riga …

package main

import (
    "fmt"
    "time"
)

func scrivi(n int) {
    fmt.Println(n)
    time.Sleep(1 * time.Second)
}

func main() {
    for i := 1; i <= 10; i++ {
	go scrivi(i)
    }
    time.Sleep(1 * time.Second)
}

in pratica abbiamo aggiunto l’istruzione go prima di invocare la funzione; in questo modo ogni esecuzione della funzione “scrivi” inizia in un thread parallelo, e il programma dopo aver avviato il thread può proseguire con le istruzioni successive senza aspettare che la funzione finisca. Di fatto questo si traduce in 10 “goroutine” eseguite contemporaneamente, lo dimostra anche il nostro output che non è più così ordinato, ma notiamo che il programma ora impiega 1 secondo in totale contro gli 11 di prima!

$ go build contanumeri.go
$ time ./contanumeri
5
4
2
3
8
9
6
10
1
7

real    0m1,003s
user    0m0,000s
sys     0m0,004s

E se volessimo invece i numeri tutti in fila ? Go mette a disposizione del programmatore un tipo speciale di oggetti, detti “channels”, per “incanalare” i dati e far comunicare tra di loro le goroutine; ma tratteremo questo costrutto in un prossimo articolo. Come al solito, prima di concludere lascio ai lettori un link per approfondire e far pratica con Go, in attesa della prossima puntata! Go By Example è un pratico tutorial passo-passo che si può seguire direttamente dal browser, e consiste in una serie di programmini di difficoltà crescente ma ben commentati riga per riga, in modo da capire ed apprendere la sintassi del linguaggio e le sue caratteristiche.

Info about author

Andrea Manzini