Perché nessun generico in Go?


126

Disclaimer: ho giocato con Go solo per un giorno, quindi c'è una buona possibilità che mi sia perso molto.

Qualcuno sa perché non esiste un vero supporto per generics / template / whatsInAName in Go? Quindi esiste un generico map, ma viene fornito dal compilatore, mentre un programmatore Go non può scrivere la propria implementazione. Con tutto il discorso su come rendere Go il più ortogonale possibile, perché posso USARE un tipo generico ma non CREARE uno nuovo?

Soprattutto quando si tratta di programmazione funzionale, ci sono lambda, anche chiusure, ma con un sistema di tipo statico privo di generici, come faccio a scrivere, bene, funzioni generiche di ordine superiore come filter(predicate, list)? OK, le liste collegate e simili possono essere fatte interface{}sacrificando la sicurezza del tipo.

Poiché una rapida ricerca su SO / Google non ha rivelato alcun approfondimento, sembra che i generici, se non del tutto, verranno aggiunti a Go come ripensamento. Mi fido di Thompson di fare molto meglio dei ragazzi di Java, ma perché tenere fuori i generici? O sono pianificati e non ancora implementati?


Penso che valga la pena sottolineare: l'uso dell'interfaccia {} non sacrifica la sicurezza dei tipi. È un tipo e può essere asserito (non trasmesso) ad altri tipi, ma queste asserzioni invocano ancora controlli di runtime per mantenere la sicurezza del tipo.
cthom06,

12
interface{}sacrifica la sicurezza di tipo statico . Tuttavia, questa è una lamentela alquanto strana da presentare quando si parla di Scheme nel paragrafo successivo, dal momento che Scheme normalmente non ha un controllo statico del tipo.
Poolie,

@poolie: Quello di cui mi preoccupo è attenersi a UN paradigma all'interno di una lingua. O sto usando XOR di sicurezza di tipo statico no.

2
tra l'altro è scritto 'Vai', non 'GO', come puoi vedere su golang.org. Ed è sensibile al maiuscolo / minuscolo. :-)
poolie

Risposte:


78

questa risposta è disponibile qui: http://golang.org/doc/faq#generics

Perché Go non ha tipi generici?

I generici potrebbero anche essere aggiunti ad un certo punto. Non sentiamo l'urgenza per loro, anche se comprendiamo che alcuni programmatori lo fanno.

I generici sono convenienti ma hanno un costo in termini di complessità nel sistema di tipi e nel tempo di esecuzione. Non abbiamo ancora trovato un design che offra un valore proporzionato alla complessità, sebbene continuiamo a pensarci. Nel frattempo, le mappe e le sezioni incorporate di Go, oltre alla possibilità di utilizzare l'interfaccia vuota per costruire contenitori (con unboxing esplicito) significano in molti casi che è possibile scrivere codice che fa ciò che i generici consentirebbero, se meno agevolmente.

Questo rimane un problema aperto.


14
@amoebe, "l'interfaccia vuota", scritto interface{}, è il tipo di interfaccia più semplice, e ogni oggetto lo fornisce. Se si crea un contenitore che li tiene, può accettare qualsiasi oggetto (non primitivo). Quindi è molto simile a un contenitore che contiene ObjectsJava.
Poolie,

4
@YinWang I generici non sono così semplici in un ambiente inferito dal tipo. Ma ancora più importante; l'interfaccia {} non equivale ai puntatori void * in C. Le analogie migliori sarebbero i tipi di ID System.Object o Objective-C di C #. Le informazioni sul tipo vengono conservate e possono essere "espresse" (asserite, in realtà) al loro tipo concreto. Prendi i dettagli grintosi qui: golang.org/ref/spec#Type_assertions
tbone

2
@tbone System.Object di C # (o Java's Object di per sé) è essenzialmente ciò che intendevo per "puntatori del vuoto di C" (ignorando la parte che non si può fare l'aritmetica del puntatore in quelle lingue). Quelli sono dove si perdono le informazioni sul tipo statico. Un cast non sarà di grande aiuto perché otterrai un errore di runtime.
Ian,

1
I template di @ChristopherPfohl D sembrano avere un po 'meno tempo di compilazione in termini di tempo di compilazione, e normalmente non si genera più codice con i template di quanto si farebbe altrimenti (si potrebbe, in effetti, finire con meno codice a seconda delle circostanze).
Cubico

3
@ChristopherPfohl Penso che solo i generici Java abbiano problemi di boxe / unboxing per i tipi primitivi? Il generico reificato di C # non presenta il problema.
ca9163d9,

32

Vai 2

C'è un progetto di bozza per i generici su https://blog.golang.org/go2draft .

Vai 1

Russ Cox, uno dei veterani di Go, ha scritto un post sul blog intitolato The Generic Dilemma , in cui chiede

... vuoi programmatori lenti, compilatori lenti e binari gonfiati o tempi di esecuzione lenti?

I programmatori lenti sono il risultato di nessuna generica, i compilatori lenti sono causati da generici simili a C ++ e i tempi di esecuzione lenti derivano dall'approccio di box-unboxing che Java utilizza.

La quarta possibilità non menzionata nel blog sta andando sulla rotta C #. Generare il codice specializzato come in C ++, ma in fase di esecuzione quando è necessario. Mi piace davvero molto, ma Go è molto diverso dal C # quindi questo probabilmente non è applicabile affatto ...

Dovrei menzionare che l'utilizzo della popolare tecnica di programmazione generica di Java 1.4 in go che si presenta interface{}soffre esattamente degli stessi problemi del boxing-unboxing (perché è quello che stiamo facendo), oltre alla perdita della sicurezza del tipo di tempo di compilazione. Per tipi piccoli (come gli ints) Go ottimizza il interface{}tipo in modo che un elenco di ints che sono stati espressi per l'interfaccia {} occupi un'area contigua di memoria e occupi solo il doppio dello spazio rispetto agli ints normali. Tuttavia, c'è ancora il sovraccarico dei controlli di runtime durante il cast interface{}. Riferimento .

Tutti i progetti che aggiungono supporto generico per andare (ce ne sono molti e tutti sono interessanti) passano uniformemente alla route C ++ di generazione del time code di compilazione.


La mia soluzione a questo dilemma sarebbe di andare su "tempi di esecuzione lenti" per impostazione predefinita con l'opzione di profilare il programma e ricompilare le parti più sensibili alle prestazioni in una modalità "compilatori lenti e binari gonfiati". Peccato che le persone che implementano cose del genere tendano a prendere la strada del C ++.
user7610

1
È stato menzionato che piccoli tipi (cioè int) che sono memorizzati in []interface{}uso 2x la RAM come []int. Sebbene sia vero, anche i tipi più piccoli (ovvero byte) utilizzano fino a 16 volte la RAM come []byte.
BMiner,

In realtà non esiste alcun dilemma con l'approccio C ++. Se un programmatore sceglie di scrivere il codice modello, il vantaggio di farlo deve superare il costo della compilazione lenta. Altrimenti, potrebbe farlo alla vecchia maniera.
John Z. Li

Il dilemma riguarda quale approccio scegliere. Se risolvi il dilemma seguendo l'approccio C ++, il dilemma viene risolto.
user7610

9

Anche se i generici non sono attualmente integrati, ci sono diverse implementazioni esterne di generici per go, che utilizza i commenti in combinazione con piccole utility che generano codice.

Ecco una di queste implementazioni: http://clipperhouse.github.io/gen/


1

In realtà, secondo questo post:

Molte persone hanno concluso (erroneamente) che la posizione del team Go è "Go non avrà mai generici". Al contrario, comprendiamo i potenziali generici, sia per rendere Go molto più flessibile e potente, sia per rendere Go molto più complicato. Se vogliamo aggiungere farmaci generici, vogliamo farlo in un modo che ottenga la massima flessibilità e potenza con la minore complessità possibile.


-1

Il polimorfismo parametrico (generici) è in esame per Go 2 .

Questo approccio introdurrebbe il concetto di contratto , che può essere utilizzato per esprimere vincoli sui parametri di tipo:

contract Addable(a T) {
  a + a // Could be += also
}

Tale contratto potrebbe quindi essere utilizzato in tal modo:

func Sum(type T Addable)(l []T) (total T) {
  for _, e := range l {
    total += e
  }
  return total
}

Questa è una proposta in questa fase.


La tua filter(predicate, list)funzione potrebbe essere implementata con un parametro di tipo come questo:

func Filter(type T)(f func(T) bool, l []T) (result []T) {
  for _, e := range l {
    if f(e) {
      result = append(result, e)
    }
  }
  return result
}

In questo caso, non è necessario vincolare T.


1
Se stai leggendo questa risposta oggi, tieni presente che i contratti sono stati eliminati dal progetto di proposta: go.googlesource.com/proposal/+/refs/heads/master/design/…
jub0bs
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.