Scritto da
creato: 2009 11 12
aggiornato: 2009 11 14

Go language, il linguaggio di scripting di Google

Alcune considerazioni e proposte sulla programmazione partendo dal nuovo linguaggio di Mountain view.

Cosa sostiene Google
[...] Go tenta di combinare la velocità di sviluppo offerta da un linguaggio dinamico come Python con le prestazioni e la sicurezza di un linguaggio compilato come il C o il C++. Nei nostri test la compilazione dei progetti è istantanea, anche quando il codice binario generato ha una dimesione considerevole il tutto è compilato in pochi secondi. Non solo: in fase di runtime la velocità di esecuzione si avvicina considerevolmente a quella del C. Go è progettato per farvi andare veloci. [...]
Visita Golang.org

Cosa penso personalmente
- Go è un linguaggio di scripting, non un linguaggio di programmazione, lo si può considare un linguaggio di scripting compilato.
L'idea di fondo è eccellente: un linguaggio semplice come un linguaggio di scripting, veloce come un linguaggio compilato; su molte soluzioni proposte ho però diversi dubbi.

- Proprio perchè Go è progettato e pensato per lo scripting è facile che la struttura del listato tenda a ciò che in gergo è definito spaghetti code, una delle motivazioni principali di ciò è costituita dalla mancanza di namespaces e gerarchie di classi, e, in generale, dal totale rifiuto per la programmazione ad oggetti.

- Go non implementa in alcun modo il paradigma object oriented.
Non sono un fanatico della programmazione ad oggetti, penso che un linguaggio di programmazione moderno debba consentire e prevedere differenti paradigmi di programmazione, è proprio per questo motivo che Go dovrebbe implementare la programmazione ad oggetti integralmente.
Da un progetto firmato Google ci si aspetterebbe addirittura un miglioramento, un'estensione di questo paradigma, non una totale rimozione.
Ad esempio: quante volte, nello sviluppo di un oggetto, dichiariamo delle proprietà private alle quali accediamo con un metodo pubblico?
Questo pattern è diffusissimo e, su larga scala, porta alla scrittura di codice inutilmente prolisso.
Perchè non pensare di estendere quindi il controllo di visibilità delle proprietà per offrire una soluzione a questo, e ad altri, problemi?
esempio: public property=> public readonly property
/* la proprietà "property" è pubblica solo in lettura, un tentativo di scrittura al di fuori dell'oggetto genera un errore di compilazione*/
Qualcuno potrebbe pensare che, in linguaggi come il c++, questa soluzione sia già presente, il modificatore const (introdotto da Bjarne Stroustrup ed inizialmente chiamato proprio readonly) consente infatti un accesso in sola lettura, ma la scrittura della variabile non può essere dinamica. (Il programmatore deve definirla nel listato).
Su questo tipo di problema c'è tanto da dire.
Oltre a un modificatore readonly per le proprietà degli oggetti, potrebbe essere interessante un modificatore writeonce per le variabili in generale, appunto una variabile const (scrivibile una sola volta) sia compile time che runtime.
La presenza o l'assenza di un dato è solo il primo aspetto di ciò che personalmente chiamo shaping.
Quante volte, nella programmazione, dobbiamo sviluppare dei controlli sui valori di una determinata variabile?
Forse questa proposta è eccessivamente data oriented ma non sarebbe a mio avviso da escludere la possibilità di un strongest typing; ovvero, definire nella dichiarazione delle variabili non soltanto il tipo generale ma anche la della loro forma, ovvero effettuare un controllo immediato della consistenza delle variabili stesse.
esempi:
int x diventerebbe int[range:<5] x. /* definisco la variabile x come un intero, il cui valore deve essere minore di 5*/
string x => string[length:>3,<20] x /* definisco una variabile x, di tipo stringa la cui lunghezza deve essere compresa tra 3 e 20*/
Nota: non si consideri ovviamente la consistenza della sintassi degli esempi offerti, che andrebbe, ovviamente, debitamente studiata; si consideri inoltre che la definizione della shape delle variabili sarebbe ovviamente opzionale e perfettamente compatibile con una definizione "classica".

- Go introduce i Multiple return values e i Named result parameters. Penso che il tutto debba essere sviluppato in maniera differente.
L'utilizzo di named return values o multiple return values è essenzialmente equivalente alla definizione di N variabili esterne al corpo della funzione e passarle alla funzione stessa per reference.
La soluzione di Go si limita quindi a dividere questi parametri in due gruppi, il primo è costituito dai parametri "vecchio stile" (passati per referenza o per valore), il secondo è costituito esclusivamente da variabili passate per reference. Non solo non vedo l'utilità di questo ma, poichè un valore di ritorno, in un linguaggio strong typed definisce il tipo della variabile resa dal metodo/funzione, posizionare i valori di ritorno dopo i parametri di input mi sembra una mancanza di coerenza nella progettazione del linguaggio.
Nella mia esperienza ho sentito molte volte il bisogno non tanto di valori di ritorno multipli o di risultati dotati di nome, bensì di multiple type return value, ovvero: un valore di ritorno di un metodo/funzione non vincolato ad un solo tipo ma ad uno o più tipi definiti. Generalizzando: la possibilità di dichiarare una variabile di N tipi potenziali. (Overload delle variabili).
Questo tipo di soluzione, a mio avviso, preserva i ben noti vantaggi dello strong typing estendendoli con una maggiore flessibilità ed evitando nel contempo i pericoli e le noie (come gli eccessivi controlli sul tipo) di un linguaggio week typed.
L'overload delle variabili potrebbe essere una possibilità potente accomunata, ad esempio, con l'overload degli operatori.(Che Go non implementa)
Facciamo un semplice esempio:
[string,bool] test() { FUNCTION BODY} /*test può essere una stringa o un booleano*/
Definiamo ora un semplice "manager" per "test".
 class manager { public: void execute(bool input) { std::cout << "bool";} void execute(string input) {std::cout << "string";} }; 
...ora usiamolo
 [string,bool] x = test(); manager y; y.execute(x); 
...nessun if è stato utilizzato!
L'overload delle variabili potrebbe essere utile al fine di risolvere il tipo di problemi che i multiple return values e i named results parameters di Go tentano di risolvere.
Consieriamo un esempio concreto in Go
func (file *File) Write(b []byte) (n int, err Error)
Riscriviamolo con il variable overloading
[int,error] File::Write(byte b) {}
Qualcuno potrebbe dire: perchè non utilizzare le eccezioni? (...Go non le implementa)
Anche se nell'esempio riportato preferirei sicuramente l'utilizzo di una eccezione le exceptions sono utilizzate più di quanto dovrebbero. (questo non è un buon motivo per rimuoverle del tutto)
Per esempio: utilizzerei l'overload delle variabili invece delle eccezioni ogni volta che si pone la necessità di stoppare l'esecuzione di un metodo in una condizione definita e questa condizione non è un errore o una situazione eccezionale ma una condizione perfettamente lecita, attesa o altamente probabile.

Diamo uno sguardo alla pagina Go per i programmatori C++ (Le mie considerazioni, dovrebbe esserchiaro, hanno in mente proprio la natura del C++)
[...] Go non ha classi con costruttori e distruttori, invece di classi e metodi, della gerarchia delle classi, delle funzioni virtuali Go offre le interfacce. Le interfacce sono utilizzate dove il C++ utilizza i templates.[...] [...]
Come dovrebbe essere chiaro questo mi sembra il limite principale.
[...] Go libera la memoria automaticamente. Non è necessario, ne' è possibile, liberare la memoria esplicitamente. La garbage collection è incrementale ed altamente efficente su tutti i moderni processori.[...]
In c++ puoi creare un oggetto nello stack o nell'heap...nella mia esperienza il 99% degli oggetti sono creati nell'heap e sono perciò distrutti non appena la loro visibilità finisce.
[...] Le stringhe sono offerte dal linguaggio. Non possono essere alterate una volta create.[...]
Ho discusso ampiamente sopra questo tipo di problematica.
[...] Le Hash tables sono offerte dal linguaggio. Sono chiamate mappe [...]
...che dire degli STL containers del C++?
[...] Go non supporta ne' l'overloading delle funzioni ne quello degli operatori.[...]
L'overloading dei metodi consente una programmazione più chiara (limita ad esempio l'utilizzo dell' if-else) e un maggior grado di astrazione, l'overload degli operatori è una funzionalità che il programmatore può praticamente ignorare...(Può non deve).
[...] Go non supporta i qualificatori const e volatile[...]
Il c++ si!
[...] Go utilizza nil per i puntatori invalidi, c++ utilizza NULL o semplicemente 0 [...]
Non è completamente vero: il nuovo standard c++ (C++0x) implementa il nullptr
Un puntatore nullo sarà appunto un "puntatore nullo", non un intero o un booleano o una stringa vuota o peggio ancora (come nel php) qualcosa che equivale a tutte e tre queste cose) sarà proprio un puntatore nullo!
Sappiamo tutti quanto questa lacuna sia vistosa, fastidiosa e generatrice di errori.