É geralmente aceito que os genéricos Java falharam em alguns aspectos importantes. A combinação de curingas e limites levou a algum código seriamente ilegível.
No entanto, quando olho para outras linguagens, realmente não consigo encontrar um sistema de tipos genéricos com o qual os programadores estejam satisfeitos.
Se considerarmos o seguinte como objetivos de design de um sistema desse tipo:
- Sempre produz declarações do tipo de fácil leitura
- Fácil de aprender (não há necessidade de retocar covariância, contravariância etc.)
- maximiza o número de erros em tempo de compilação
Existe algum idioma que acertou? Se eu pesquisar no Google, a única coisa que vejo são reclamações sobre como o sistema de tipos é péssimo na linguagem X. Esse tipo de complexidade é inerente à digitação genérica? Devemos desistir de tentar verificar a segurança do tipo 100% em tempo de compilação?
Minha principal pergunta é qual é a linguagem que "acertou" a melhor em relação a esses três objetivos. Percebo que isso é subjetivo, mas até agora não consigo nem encontrar uma linguagem em que nem todos os programadores concordem que o sistema de tipos genéricos é uma bagunça.
Adendo: como observado, a combinação de subtipo / herança e genéricos é o que cria a complexidade. Por isso, estou realmente procurando por uma linguagem que combine ambos e evite a explosão da complexidade.
fonte
easy-to-read type declarations
? O terceiro critério também é ambíguo: por exemplo, eu posso transformar exceções de índice fora dos limites em erros de tempo de compilação, não permitindo indexar matrizes, a menos que eu possa calcular o índice em tempo de compilação. Além disso, o segundo critério exclui a subtipagem. Isso não é necessariamente uma coisa ruim, mas você deve estar ciente do que está pedindo.Foo<T> where SiameseCat:T
) e que não há possibilidade de ter um tipo genérico que não seja convertívelObject
. IMHO, .NET se beneficiaria de tipos agregados que eram semelhantes a estruturas, mas ainda mais despojados. SeKeyValuePair<TKey,TValue>
fosse desse tipo, umIEnumerable<KeyValuePair<SiameseCat,FordFocus>>
poderia ser convertido paraIEnumerable<KeyValuePair<Animal,Vehicle>>
, mas apenas se o tipo não pudesse ser encaixotado.Respostas:
Enquanto os genéricos são dominantes na comunidade de programação funcional há décadas, a adição de genéricos a linguagens de programação orientadas a objetos oferece alguns desafios exclusivos, especificamente a interação de subtipagem e genéricos.
No entanto, mesmo se focarmos nas linguagens de programação orientada a objetos e no Java em particular, um sistema genérico muito melhor poderia ter sido projetado:
Tipos genéricos devem ser admissíveis onde quer que estejam. Em particular, se
T
for um parâmetro de tipo, as seguintes expressões deverão ser compiladas sem avisos:Sim, isso exige que os genéricos sejam reificados, assim como todos os outros tipos no idioma.
A covariância e a contravariância de um tipo genérico devem ser especificadas em (ou deduzidas de) sua declaração, em vez de toda vez que o tipo genérico é usado, para que possamos escrever
ao invés de
Como os tipos genéricos podem demorar um pouco, não precisamos especificá-los de forma redundante. Ou seja, devemos ser capazes de escrever
ao invés de
Qualquer tipo deve ser admissível como parâmetro de tipo, não apenas tipos de referência. (Se podemos ter um
int[]
, por que não podemos ter umList<int>
)?Tudo isso é possível em C #.
fonte
O uso de subtipos cria muitas complicações ao executar a programação genérica. Se você insistir em usar uma linguagem com subtipos, precisará aceitar que há uma certa complexidade inerente à programação genérica que a acompanha. Alguns idiomas fazem isso melhor que outros, mas você só pode levar isso até agora.
Compare isso com os genéricos de Haskell, por exemplo. Eles são simples o suficiente para que, se você usar a inferência de tipo, possa escrever uma função genérica correta por acidente . Na verdade, se você especificar um único tipo, o compilador muitas vezes diz para si mesmo: "Bem, eu estava indo fazer este genérico, mas você me pediu para fazê-lo apenas para ints, então que seja."
É certo que as pessoas usam o sistema de tipos de Haskell de maneiras surpreendentemente complexas, tornando-o o banimento de qualquer novato, mas o sistema de tipos subjacente em si é elegante e muito admirado.
fonte
a
deve ser algum tipo de número inteiro".Houve muita pesquisa sobre a combinação de genéricos com subtipagem ocorrida há cerca de 20 anos. A linguagem de programação Thor desenvolvida pelo grupo de pesquisa de Barbara Liskov no MIT tinha uma noção de "onde" cláusulas que permitem especificar os requisitos do tipo que você está parametrizando. (Isso é semelhante ao que o C ++ está tentando fazer com os Conceitos .)
O artigo que descreve os genéricos de Thor e como eles interagem com os subtipos de Thor é: Dia, M; Gruber, R; Liskov, B; Myers, AC: subtipos vs. cláusulas where: constrangimento do polimorfismo paramétrico , ACM Conf em Obj-Oriented Prog, Sys, Lang e Apps , (OOPSLA-10): 156-158, 1995.
Eu acredito que eles, por sua vez, se basearam no trabalho que foi feito no Emerald no final dos anos 80. (Não li esse trabalho, mas a referência é: Black, A; Hutchinson, N; Jul, E; Levy, H; Carter, L: Distribuição e tipos abstratos em Emerald , _IEEE T. Software Eng., 13 ( 1): 65-76, 1987.
Tanto Thor quanto Emerald eram "linguagens acadêmicas", então provavelmente não tiveram uso suficiente para as pessoas realmente entenderem se as cláusulas where (conceitos) realmente resolvem quaisquer problemas reais. É interessante ler o artigo de Bjarne Stroustrup sobre por que a primeira tentativa de Concepts em C ++ falhou: Stroustrup, B: A decisão "Remover conceitos" do C ++ 0x , Dr. Dobbs , 22 de julho de 2009. (Mais informações na página inicial do Stroustrup . )
Outra direção que as pessoas parecem estar tentando é algo chamado traços . Por exemplo, a linguagem de programação Rust da Mozilla usa características. Pelo que entendi (o que pode estar completamente errado), declarar que uma classe satisfaz uma característica é como dizer que uma classe implementa uma interface, mas você está dizendo "se comporta como um" em vez de "é um". Parece que as novas linguagens de programação Swift da Apple estão usando um conceito semelhante de protocolos para especificar restrições nos parâmetros dos genéricos .
fonte