Articles

Go (linguaggio di programmazione)

Posted by admin

Go è influenzato da C, ma con un’enfasi su una maggiore semplicità e sicurezza. Il linguaggio è composto da:

  • Una sintassi e un ambiente che adottano modelli più comuni nei linguaggi dinamici:
    • Dichiarazione e inizializzazione delle variabili concise opzionali tramite inferenza di tipo (x := 0invece di int x = 0;o var x = 0;).
    • Compilazione veloce.
    • Gestione remota dei pacchetti (go get) e documentazione dei pacchetti online.,
  • Approcci distintivi a problemi particolari:
    • Primitive di concorrenza integrate: processi leggeri (goroutine), canali e l’istruzioneselect.
    • Un sistema di interfaccia al posto dell’ereditarietà virtuale e digitare embedding anziché ereditarietà non virtuale.
    • Una toolchain che, per impostazione predefinita, produce binari nativi collegati staticamente senza dipendenze esterne.,
  • Un desiderio di mantenere la specifica della lingua abbastanza semplice da tenere nella testa di un programmatore, in parte omettendo caratteristiche che sono comuni in lingue simili.

SyntaxEdit

La sintassi di Go include modifiche da C volte a mantenere il codice conciso e leggibile. È stato introdotto un operatore di dichiarazione/inizializzazione combinato che consente al programmatore di scrivere i := 3 o s := "Hello, world!", senza specificare i tipi di variabili utilizzate. Questo contrasta con C int i = 3; e const char *s = "Hello, world!";., I punti e virgola terminano ancora le istruzioni, ma sono impliciti quando si verifica la fine di una riga. I metodi possono restituire più valori e restituire una coppiaresult, err è il modo convenzionale in cui un metodo indica un errore al suo chiamante in Go. Go aggiunge sintassi letterali per l’inizializzazione dei parametri della struttura per nome e per l’inizializzazione di mappe e sezioni. In alternativa al ciclo a tre istruzioni di C for, le espressioni di Go range consentono un’iterazione concisa su array, sezioni, stringhe, mappe e canali.,

TypesEdit

Per ogni tipo T e ogni costante intera non negativa n, esiste un tipo di array indicato con T; gli array di lunghezze diverse sono quindi di tipi diversi. Gli array dinamici sono disponibili come “slice”, indicati con T per alcuni tipi T. Questi hanno una lunghezza e una capacità che specificano quando è necessario allocare nuova memoria per espandere l’array. Diverse sezioni possono condividere la loro memoria sottostante.

I puntatori sono disponibili per tutti i tipi e il tipo puntatore a T è indicato *T., Address-taking e indirection usano gli operatori& e*, come in C, o avvengono implicitamente attraverso la chiamata al metodo o la sintassi di accesso agli attributi. Non esiste un’aritmetica del puntatore, tranne tramite lo speciale non sicuro.Tipo di puntatore nella libreria standard.

Per una coppia di tipi K, V, il tipo mapV è il tipo di tabelle hash che mappano le chiavi type-K ai valori type-V. Le tabelle hash sono integrate nella lingua, con sintassi speciale e funzioni integrate. chan T è un canale che consente l’invio di valori di tipo T tra processi Go simultanei.,

A parte il supporto per le interfacce, il sistema di tipi di Go è nominale: la parola chiave type può essere utilizzata per definire un nuovo tipo con nome, che è distinto da altri tipi con nome che hanno lo stesso layout (nel caso di una struttura, gli stessi membri nello stesso ordine). Alcune conversioni tra tipi (ad esempio, tra i vari tipi interi) sono predefinite e l’aggiunta di un nuovo tipo può definire conversioni aggiuntive, ma le conversioni tra tipi denominati devono sempre essere invocate esplicitamente., Ad esempio, la parola chiave type può essere utilizzata per definire un tipo per gli indirizzi IPv4, basato su interi senza segno a 32 bit:

type ipv4addr uint32

Con questa definizione di tipo, ipv4addr(x) interpreta il valore uint32 x come indirizzo IP. Semplicemente assegnando x a una variabile di tipo ipv4addr è un errore di tipo.

Le espressioni costanti possono essere digitate o “non tipizzate”; viene assegnato un tipo quando viene assegnato a una variabile tipizzata se il valore che rappresentano supera un controllo in fase di compilazione.,

I tipi di funzione sono indicati dalla parola chiave func; prendono zero o più parametri e restituiscono zero o più valori, tutti digitati. Il parametro e i valori di ritorno determinano un tipo di funzione; quindi, func (string, int32) (int, error) è il tipo di funzioni che accettano una stringa e un intero con segno a 32 bit e restituiscono un intero con segno (di larghezza predefinita) e un valore dell’errore di tipo di interfaccia incorporato.

Qualsiasi tipo con nome ha un set di metodi associato ad esso., L’esempio di indirizzo IP sopra può essere esteso con un metodo per verificare se il suo valore è uno standard noto:

// ZeroBroadcast reports whether addr is 255.255.255.255.func (addr ipv4addr) ZeroBroadcast() bool { return addr == 0xFFFFFFFF}

A causa della digitazione nominale, questa definizione di metodo aggiunge un metodo a ipv4addr, ma non su uint32. Mentre i metodi hanno una definizione speciale e una sintassi di chiamata, non esiste un tipo di metodo distinto.

Interfaccia systemEdit

Go fornisce due funzionalità che sostituiscono l’ereditarietà della classe.

Il primo è l’incorporamento, che può essere visto come una forma automatizzata di composizione o delega.,:255

Il secondo sono le sue interfacce, che fornisce il polimorfismo di runtime.: 266 Interfacce sono una classe di tipi e forniscono una forma limitata di tipizzazione strutturale nel sistema di tipo altrimenti nominale di Go. Un oggetto che è di un tipo di interfaccia è anche di un altro tipo, proprio come gli oggetti C++ che sono contemporaneamente di una classe base e derivata. Le interfacce Go sono state progettate dopo i protocolli del linguaggio di programmazione Smalltalk. Più fonti usano il termine duck typing quando descrivono le interfacce Go., Sebbene il termine duck typing non sia definito con precisione e quindi non sia sbagliato, di solito implica che la conformità del tipo non sia controllata staticamente. Poiché la conformità a un’interfaccia Go viene controllata staticamente dal compilatore Go (tranne quando si esegue un’asserzione di tipo), gli autori Go preferiscono il termine tipizzazione strutturale.

La definizione di un tipo di interfaccia elenca i metodi richiesti per nome e tipo. Qualsiasi oggetto di tipo T per il quale esistono funzioni che corrispondono a tutti i metodi richiesti di interfaccia di tipo I è anche un oggetto di tipo I. La definizione di tipo T non deve (e non può) identificare il tipo I., Ad esempio, se Shape, Square e Circle sono definiti come

, sia un Quadrato che un Cerchio sono implicitamente una Forma e possono essere assegnati a una variabile tipizzata con Forma.:263-268 Nel linguaggio formale, il sistema di interfaccia di Go fornisce una digitazione strutturale piuttosto che nominale. Le interfacce possono incorporare altre interfacce con l’effetto di creare un’interfaccia combinata soddisfatta esattamente dai tipi che implementano l’interfaccia incorporata e da tutti i metodi aggiunti dall’interfaccia appena definita.,:270

La libreria standard Go utilizza interfacce per fornire genericità in diversi punti, incluso il sistema di input / output basato sui concetti di Reader e Writer.: 282-283

Oltre a chiamare metodi tramite interfacce, Go consente di convertire i valori dell’interfaccia in altri tipi con un controllo di tipo runtime. I costrutti del linguaggio per farlo sono l’asserzione type, che controlla un singolo tipo potenziale, e l’interruttore type, che controlla più tipi.,

L’interfaccia vuotainterface{} è un caso base importante perché può riferirsi a un elemento di qualsiasi tipo concreto. È simile alla classe Object in Java o c# ed è soddisfatta da qualsiasi tipo, inclusi i tipi incorporati come int.: 284 Il codice che utilizza l’interfaccia vuota non può semplicemente chiamare metodi (o operatori incorporati) sull’oggetto riferito, ma può memorizzare il valore interface{}, provare a convertirlo in un tipo più utile tramite un’asserzione di tipo o un interruttore di tipo, o ispezionarlo con il pacchetto reflect di Go., Poichéinterface{} può fare riferimento a qualsiasi valore, è un modo limitato per sfuggire alle restrizioni della digitazione statica, comevoid* in C ma con ulteriori controlli di tipo runtime.

Il tipointerface{} può essere utilizzato per modellare dati strutturati di qualsiasi schema arbitrario in Go, come i dati JSON o YAML, rappresentandolo come unmapinterface{} (mappa della stringa all’interfaccia vuota). Questo descrive ricorsivamente i dati sotto forma di un dizionario con chiavi stringa e valori di qualsiasi tipo.,

I valori dell’interfaccia vengono implementati utilizzando il puntatore ai dati e un secondo puntatore alle informazioni sul tipo di runtime. Come alcuni altri tipi implementati usando i puntatori in Go, i valori dell’interfaccia sono nil se non inizializzati.

Pacchetto systemEdit

Go sistema di gestione dei pacchetti, ogni pacchetto ha un percorso (ad esempio, "compress/bzip2" o "golang.org/x/net/html") e un nome (ad esempio, bzip2 o html)., I riferimenti alle definizioni di altri pacchetti devono sempre essere preceduti dal nome dell’altro pacchetto e solo i nomi in maiuscolo di altri pacchetti sono accessibili: io.Reader è pubblico ma bzip2.reader non lo è. Il comandogo get può recuperare i pacchetti memorizzati in un repository remoto e gli sviluppatori sono incoraggiati a sviluppare pacchetti all’interno di un percorso di base corrispondente a un repository di origine (come ad esempio.,com/user_name/package_name) per ridurre la probabilità di collisione del nome con future aggiunte alla libreria standard o ad altre librerie esterne.

Esistono proposte per introdurre una corretta soluzione di gestione dei pacchetti per Go simile a CPAN per il sistema di carico di Perl o Rust o il sistema npm di Node.

Concorrenza: goroutine e channelsEdit

Il linguaggio Go ha strutture integrate, oltre al supporto della libreria, per la scrittura di programmi simultanei., La concorrenza si riferisce non solo al parallelismo della CPU, ma anche all’asincronia: lasciare che le operazioni lente come un database o una lettura di rete vengano eseguite mentre il programma fa altro lavoro, come è comune nei server basati su eventi.

Il costrutto di concorrenza principale è la goroutine, un tipo di processo leggero. Una chiamata di funzione preceduta dalla parola chiave go avvia una funzione in una nuova goroutine., La specifica del linguaggio non specifica come devono essere implementate le goroutine, ma le implementazioni attuali multiplexano le goroutine di un processo Go su un set più piccolo di thread del sistema operativo, simile alla pianificazione eseguita in Erlang.: 10

Mentre un pacchetto di librerie standard con la maggior parte delle classiche strutture di controllo della concorrenza (blocchi mutex, ecc.) è disponibile,: 151-152 i programmi simultanei idiomatici preferiscono invece i canali, che forniscono messaggi di invio tra le goroutine., I buffer opzionali memorizzano i messaggi in ordine FIFO: 43 e consentono di inviare goroutine prima che i loro messaggi vengano ricevuti.

Canali vengono digitate, in modo che un canale di tipo chan T può essere utilizzato solo per il trasferimento di messaggi di tipo T. sintassi Speciale è usato per operare su di essi; <-ch è un’espressione che causa l’esecuzione di goroutine di bloccare fino a un valore di arriva sul canale ch, mentre ch <- x invia il valore di x (possibilmente in blocco fino a quando un altro goroutine riceve il valore)., L’istruzione select integrata può essere utilizzata per implementare comunicazioni non bloccanti su più canali; vedi sotto per un esempio. Go ha un modello di memoria che descrive come goroutine devono utilizzare i canali o altre operazioni per condividere in modo sicuro i dati.

L’esistenza di set di canali Si distingue dai linguaggi concorrenti in stile modello attore come Erlang, dove i messaggi sono indirizzati direttamente agli attori (corrispondenti alle goroutine)., Lo stile attore può essere simulato in Go mantenendo una corrispondenza uno-a-uno tra goroutine e canali, ma il linguaggio consente a più goroutine di condividere un canale o una singola goroutine da inviare e ricevere su più canali.:147

Da questi strumenti si possono costruire costrutti simultanei come worker pool, pipeline (in cui, ad esempio, un file viene decompresso e analizzato durante il download), chiamate in background con timeout, chiamate parallele “fan-out” a un set di servizi e altri., I canali hanno anche trovato usi più lontani dalla solita nozione di comunicazione tra processi, come servire come elenco sicuro per la concorrenza di buffer riciclati, implementare coroutine (che ha contribuito a ispirare il nome goroutine) e implementare iteratori.

Le convenzioni strutturali correlate alla concorrenza di Go (channels and alternative channel inputs) sono derivate dal modello di processi sequenziali comunicanti di Tony Hoare., A differenza dei precedenti linguaggi di programmazione concorrenti come Occam o Limbo (un linguaggio su cui ha lavorato il co-designer di Go Rob Pike), Go non fornisce alcuna nozione integrata di concorrenza sicura o verificabile. Mentre il modello communicating-processes è favorito in Go, non è l’unico: tutte le goroutine in un programma condividono un singolo spazio di indirizzi. Ciò significa che oggetti e puntatori mutabili possono essere condivisi tra goroutine; vedere § Mancanza di sicurezza delle condizioni di gara, sotto.,

Idoneità per la programmazione paralleledit

Sebbene le funzionalità di concorrenza di Go non siano rivolte principalmente all’elaborazione parallela, possono essere utilizzate per programmare macchine multi-processore a memoria condivisa. Sono stati condotti vari studi sull’efficacia di questo approccio. Uno di questi studi ha confrontato le dimensioni (in righe di codice) e la velocità dei programmi scritti da un programmatore esperto che non ha familiarità con il linguaggio e le correzioni a questi programmi da un esperto Go (dal team di sviluppo di Google), facendo lo stesso per Chapel, Cilk e Intel TBB., Lo studio ha rilevato che il non esperto tendeva a scrivere algoritmi divide-and-conquer con una sola istruzione per ricorsione, mentre l’esperto ha scritto distribuire-lavoro-sincronizzare i programmi utilizzando una goroutine per processore. I programmi dell’esperto erano solitamente più veloci, ma anche più lunghi.

Mancanza di condizioni di gara safetyEdit

Non ci sono restrizioni su come le goroutine accedono ai dati condivisi, rendendo possibili le condizioni di gara., In particolare, a meno che un programma non si sincronizzi esplicitamente tramite canali o altri mezzi, le scritture da una goroutine potrebbero essere parzialmente, interamente o per nulla visibili a un altro, spesso senza garanzie sull’ordine delle scritture. Inoltre, le strutture dati interne di Go come valori di interfaccia, intestazioni slice, tabelle hash e intestazioni string non sono immuni alle condizioni di gara, quindi la sicurezza del tipo e della memoria può essere violata nei programmi multithread che modificano istanze condivise di tali tipi senza sincronizzazione., Invece del supporto linguistico, la programmazione simultanea sicura si basa quindi su convenzioni; ad esempio, Chisnall raccomanda un idioma chiamato “alias xor mutable”, il che significa che il passaggio di un valore mutabile (o puntatore) su un canale segnala un trasferimento di proprietà sul valore al suo ricevitore.: 155

BinariesEdit

Il linker nella toolchain gc crea binari collegati staticamente per impostazione predefinita, quindi tutti i binari Go includono il runtime Go.,

OmissionsEdit

Go omette deliberatamente alcune caratteristiche comuni in altre lingue, tra cui l’ereditarietà (implementazione), la programmazione generica, le asserzioni, l’aritmetica dei puntatori, le conversioni di tipo implicito, i sindacati senza tag e i sindacati con tag. I progettisti hanno aggiunto solo quelle strutture su cui tutti e tre erano d’accordo.,

Delle funzionalità del linguaggio omesse, i progettisti discutono esplicitamente contro le asserzioni e l’aritmetica del puntatore, mentre difendono la scelta di omettere l’ereditarietà del tipo come dare un linguaggio più utile, incoraggiando invece l’uso di interfacce per ottenere la spedizione dinamica e la composizione per riutilizzare il codice. Composizione e delega sono infatti in gran parte automatizzati da struct embedding; secondo i ricercatori Schmager et al., questa caratteristica ” ha molti degli svantaggi dell’ereditarietà: influenza l’interfaccia pubblica degli oggetti, non è a grana fine (i.,e, nessun controllo a livello di metodo sull’incorporamento), i metodi degli oggetti incorporati non possono essere nascosti ed è statico”, rendendo “non ovvio” se i programmatori lo abuseranno nella misura in cui i programmatori in altre lingue sono reputati per abusare dell’ereditarietà.

I progettisti esprimono un’apertura alla programmazione generica e notano che le funzioni incorporate sono in realtà generiche di tipo, ma queste sono trattate come casi speciali; Pike chiama questa una debolezza che potrebbe ad un certo punto essere cambiata. Il team di Google ha creato almeno un compilatore per un dialetto Go sperimentale con generici, ma non lo ha rilasciato., Sono anche aperti a standardizzare i modi per applicare la generazione di codice. Nel giugno 2020 è stata pubblicata una nuova bozza di documento di progettazione, che aggiungerebbe la sintassi necessaria per dichiarare funzioni e tipi generici. È stato fornito uno strumento di traduzione del codice go2go per consentire agli utenti di provare la nuova sintassi, insieme a una versione abilitata per i generici del parco giochi online Go.,

Inizialmente omesso, alla fine è stato aggiunto il meccanismo di panic / recover simile a un’eccezione, che gli autori di Go consigliano di utilizzare per errori irrecuperabili come quelli che dovrebbero arrestare un intero programma o una richiesta del server, o come scorciatoia per propagare gli errori nello stack all’interno di un pacchetto (ma non oltre i limiti del pacchetto,

Leave A Comment