Eu tenho tentado entender por que o JDK 8 Lambda Expert Group (EG) decidiu não incluir um novo tipo de função na linguagem de programação Java.
Revendo a lista de discussão, encontrei um tópico com a discussão sobre remoção de tipos de função .
Muitas das declarações são ambíguas para mim, talvez devido à falta de contexto e, em alguns casos, devido ao meu conhecimento limitado sobre a implementação de sistemas de tipos.
No entanto, existem algumas perguntas que acredito que posso formular com segurança neste site para me ajudar a entender melhor o que elas significam.
Eu sei que poderia fazer as perguntas na lista de discussão, mas o tópico é antigo e todas as decisões já estão tomadas, então é provável que eu seja ignorado, acima de tudo, vendo que esses caras já estão se atrasando em seus planos.
Em sua resposta para apoiar a remoção de tipos de função e endossar o uso de tipos SAM, Brian Goetz diz:
Sem reificação. Havia um longo tópico sobre o quão útil seria para os tipos de funções serem reificados. Sem reificação, os tipos de função são prejudicados.
Não consegui encontrar o fio que ele menciona. Agora, eu posso entender que a introdução de um tipo de função estrutural pode implicar certas complicações no sistema de tipos principalmente Java, o que não consigo entender é como os tipos SAM parametrizados são diferentes em termos de reificação.
Os dois não estão sujeitos aos mesmos problemas de reificação? Alguém entende como os tipos de função são diferentes dos tipos parametrizados de SAM em termos de reificação?
Em outro comentário, Goetz diz:
Existem duas abordagens básicas para a digitação: nominal e estrutural. A identidade de um nominal é baseada em seu nome; a identidade de um tipo estrutural é baseada no que é composto (como "tupla de int, int" ou "função de int para flutuar".) A maioria das línguas escolhe principalmente nominal ou principalmente estrutural; não há muitos idiomas que combinam com sucesso a tipagem nominal e estrutural, exceto "nas bordas". Java é quase inteiramente nominal (com algumas exceções: matrizes são do tipo estrutural, mas na parte inferior sempre existe um tipo de elemento nominal; os genéricos também têm uma mistura de nominal e estrutural, e isso é de fato parte da fonte de muitos reclamações das pessoas sobre genéricos.) Enxertando um sistema de tipo estrutural (tipos de função) no Java ' s sistema do tipo nominal significa nova complexidade e casos extremos. O benefício dos tipos de função vale isso?
Aqueles de vocês com experiência na implementação de sistemas de tipos. Você conhece algum exemplo dessas complexidades ou casos extremos que ele menciona aqui?
Honestamente, fico confuso com essas alegações quando considero que uma linguagem de programação como Scala, inteiramente baseada na JVM, oferece suporte a tipos estruturais, como funções e tuplas, mesmo com os problemas de reificação da plataforma subjacente.
Não me interpretem mal, não estou dizendo que um tipo de função deve ser melhor que os tipos SAM. Eu só quero entender por que eles tomaram essa decisão.
fonte
Respostas:
A abordagem SAM é realmente um pouco semelhante ao que Scala (e C ++ 11) fazem com funções anônimas (criadas com o
=>
operador do Scala ou com a[]()
sintaxe do C ++ 11 (lambda)).A primeira pergunta a ser respondida no lado Java é se o tipo de retorno de uma instrução lambda é um novo tipo primitivo, como
int
oubyte
, ou um tipo de objeto de algum tipo. Em Scala, não existem tipos primitivos - mesmo um número inteiro é um objeto de classeInt
- e as funções não são diferentes, sendo objetos de classeFunction1
,Function2
etc., dependendo do número de argumentos que a função recebe.C ++ 11, ruby e python também têm expressões lambda que retornam um objeto que pode ser chamado de maneira explícita ou implícita. O objeto retornado possui algum método padrão (como o
#call
que pode ser usado para chamá-lo como uma função. C ++ 11, por exemplo, usa umstd::function
tipo que sobrecarregaoperator()
para que as chamadas do método de chamada do objeto pareçam chamadas de função, textualmente. :-)Onde a nova proposta para Java fica confusa é no uso de tipagem estrutural para permitir atribuir esse método a outro objeto, como um
Comparator
que possua um único método principal com um nome diferente . Enquanto isso é conceitualmente icky em alguns aspectos, ele faz significa que o objeto resultante pode ser passado para as funções existentes que levam um objeto para representar um callback, um comparador, e assim por diante, e esperar para ser capaz de chamar um único bem definida método como#compare
ou#callback
. O truque do C ++ de substituir comoperator()
perfeição esse problema mesmo antes do C ++ 11, já que todas essas opções de retorno de chamada eram passíveis de chamar da mesma maneira e, portanto, o STL não exigia nenhum ajuste para permitir que lambdas do C ++ 11 fossem usadas comsort
e assim por diante. O Java, sem ter usado uma nomeação padrão para esses objetos no passado (talvez porque nenhum truque como a sobrecarga de operadores tenha tornado óbvia uma única abordagem), não tem tanta sorte, portanto esse hack impede que eles precisem alterar muitas APIs existentes. .fonte
java.util.Function2<T1, T2, R>
no Java 1.0, não haveriaComparator
interface, em vez disso, todos esses métodos receberiam umFunction2<T1, T2, int>
. (Bem, não teria ser uma faixa inteira de interfaces, comoFunction2TT_int<T1, T2>
causa de primitivas, mas você começa o meu ponto.)