Por que não existem genéricos no Go?

126

Isenção de responsabilidade: só joguei no Go por um dia agora, então há uma boa chance de eu ter perdido muito.

Alguém sabe por que não há suporte real para genéricos / modelos / whatsInAName no Go? Portanto, existe um genérico map, mas é fornecido pelo compilador, enquanto um programador Go não pode escrever sua própria implementação. Com toda a conversa sobre tornar o Go o mais ortogonal possível, por que POSSO USAR um tipo genérico, mas não CRIAR um novo?

Especialmente quando se trata de programação funcional, existem lambdas e até fechamentos, mas com um sistema de tipo estático sem genéricos, como eu escrevo, bem, funções genéricas de ordem superior como filter(predicate, list)? OK, listas vinculadas e similares podem ser feitas com o interface{}sacrifício da segurança do tipo.

Como uma pesquisa rápida no SO / Google não revelou nenhum insight, parece que os genéricos, se houver, serão adicionados ao Go como uma reflexão tardia. Confio em Thompson para fazer muito melhor do que os caras do Java, mas por que manter os genéricos de fora? Ou eles estão planejados e ainda não foram implementados?

s4y
fonte
Eu acho que vale a pena destacar: o uso da interface {} não sacrifica a segurança do tipo. É um tipo e pode ser afirmado (não convertido) para outros tipos, mas essas asserções ainda invocam verificações de tempo de execução para manter a segurança do tipo.
Cthom06 12/10
12
interface{}sacrifica a segurança do tipo estático . No entanto, essa é uma reclamação um tanto estranha a ser feita ao mencionar o esquema no próximo parágrafo, pois o esquema normalmente não possui verificação de tipo estática.
Pool #
@poolie: O que me preocupa é aderir a UM paradigma dentro de um idioma. Ou eu estou usando segurança de tipo estático XOR não.
2
btw está escrito 'Go', não 'GO', como você pode ver no golang.org. E diferencia maiúsculas de minúsculas. :-)
poolie 30/07/2012
1
que tal github.com/facebookgo/generics ?
Thellimist

Respostas:

78

esta resposta você encontrará aqui: http://golang.org/doc/faq#generics

Por que o Go não possui tipos genéricos?

Os genéricos podem muito bem ser adicionados em algum momento. Não sentimos uma urgência por eles, apesar de entendermos que alguns programadores sentem.

Os genéricos são convenientes, mas têm um custo em complexidade no sistema de tipos e no tempo de execução. Ainda não encontramos um design que dê valor proporcional à complexidade, embora continuemos pensando nisso. Enquanto isso, os mapas e fatias internos do Go, além da capacidade de usar a interface vazia para construir contêineres (com unboxing explícito), significa que em muitos casos é possível escrever um código que faça o que os genéricos permitiriam, se menos suavemente.

Este continua sendo um problema em aberto.

Vinzenz
fonte
14
@amoebe, "a interface vazia", ​​grafada interface{}, é o tipo de interface mais básico e todo objeto fornece. Se você criar um contêiner para contê-los, ele poderá aceitar qualquer objeto (não primitivo). Portanto, é muito semelhante a um contêiner armazenado Objectsem Java.
poolie 30/07/12
4
Os genéricos do @YinWang não são tão simples em um ambiente inferido por tipo. Mais importante; interface {} não é equivalente a ponteiros void * em C. Melhores analogias seriam os tipos de id System.Object ou Objective-C do C #. As informações de tipo são preservadas e podem ser "convertidas" (afirmadas, na verdade) de volta ao seu tipo de concreto. Obtenha os detalhes aqui: golang.org/ref/spec#Type_assertions
tbone
2
@tbone O System.Object do C # (ou o objeto de Java em si) é essencialmente o que eu quis dizer com "ponteiros nulos do C" (ignorando a parte que você não pode fazer aritmética de ponteiro nessas linguagens). É aí que as informações do tipo estático são perdidas. Uma conversão não ajudará muito porque você receberá um erro de tempo de execução.
Ian
1
Os modelos do @ChristopherPfohl D parecem ter um pouco menos de tempo de compilação, e normalmente você não gera mais código com os modelos do que normalmente faria (caso contrário, você pode acabar com menos código, dependendo das circunstâncias).
cúbico
3
@ChristopherPfohl Acho que apenas os genéricos Java têm problema de boxe / unboxing para tipos primitivos? O genérico reificado do C # não tem o problema.
precisa saber é o seguinte
32

Go 2

Há um rascunho de design para genéricos em https://blog.golang.org/go2draft .

Go 1

Russ Cox, um dos veteranos do Go, escreveu um post intitulado The Generic Dilemma , no qual ele pergunta

… Você quer programadores lentos, compiladores lentos e binários inchados ou tempos de execução lentos?

Programadores lentos sendo o resultado de nenhum genérico, compiladores lentos são causados ​​por C ++ como genéricos e tempos de execução lentos decorrem da abordagem de boxe-unboxing usada pelo Java.

A quarta possibilidade não mencionada no blog está seguindo a rota C #. Gerando o código especializado, como em C ++, mas em tempo de execução, quando necessário. Eu realmente gosto, mas o Go é muito diferente do C #, então isso provavelmente não é aplicável a todos os…

Devo mencionar que o uso da popular técnica Java 1.4, como a programação genérica em andamento, interface{}sofre exatamente os mesmos problemas do boxing-unboxing (porque é isso que estamos fazendo), além da perda de segurança do tipo tempo de compilação. Para tipos pequenos (como ints), o Go otimiza o interface{}tipo para que uma lista de ints que foram convertidas na interface {} ocupe uma área contígua da memória e ocupe apenas o dobro do espaço que as ints normais. Ainda há a sobrecarga das verificações de tempo de execução durante a transmissão a partir de interface{}. Referência .

Todos os projetos que adicionam suporte genérico (existem vários e todos são interessantes) seguem uniformemente a rota C ++ de geração de código de tempo de compilação.

user7610
fonte
Minha solução desse dilema seria: Ir para o padrão, com "tempos de execução lentos", com a opção de criar um perfil do programa e recompilar as partes mais sensíveis ao desempenho no modo "compiladores lentos e binários inchados". Pena que as pessoas que realmente implementam coisas assim tendem a seguir a rota C ++.
usar o seguinte comando
1
Foi mencionado que tipos pequenos (ou seja, int) armazenados em []interface{}uso 2x da RAM como []int. Embora verdade, tipos ainda menores (ou seja, byte) usam até 16x a RAM como []byte.
BMiner 18/09/18
Na verdade, não há dilema com a abordagem C ++. Se um programador optar por escrever o código do modelo, o benefício de fazê-lo deverá sobrecarregar o custo da compilação lenta. Caso contrário, ele poderia fazê-lo da maneira antiga.
John Z. Li
O dilema é sobre qual abordagem escolher. Se você resolver o dilema seguindo a abordagem C ++, o dilema será resolvido.
user7610
9

Embora os genéricos não estejam embutidos no momento, existem várias implementações externas de genéricos para uso, que usam comentários em combinações com pequenos utilitários que geram código.

Aqui está uma dessas implementações: http://clipperhouse.github.io/gen/

Alexander
fonte
1

Na verdade, de acordo com este post:

Muitas pessoas concluíram (incorretamente) que a posição da equipe Go é "Go nunca terá genéricos". Pelo contrário, entendemos o potencial dos genéricos, tanto para tornar o Go muito mais flexível e poderoso quanto para tornar o Go muito mais complicado. Se quisermos adicionar genéricos, queremos fazê-lo de maneira a obter o máximo de flexibilidade e potência com o mínimo de complexidade possível.

Ayush Gupta
fonte
-1

O polimorfismo paramétrico (genéricos) está sendo considerado no Go 2 .

Essa abordagem introduz o conceito de contrato , que pode ser usado para expressar restrições nos parâmetros de tipo:

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

Esse contrato poderia então ser usado assim:

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

Esta é uma proposta nesta fase.


Sua filter(predicate, list)função pode ser implementada com um parâmetro de tipo como este:

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
}

Nesse caso, não há necessidade de restringir T.

ᆼ ᆺ ᆼ
fonte
1
Se você está lendo esta resposta hoje, saiba que os contratos foram retirados da proposta preliminar: go.googlesource.com/proposal/+/refs/heads/master/design/…
jub0bs