He estado tratando de entender por qué el JDK 8 Lambda Expert Group (EG) decidió no incluir un nuevo tipo de función en el lenguaje de programación Java.
Al repasar la lista de correo encontré un hilo con la discusión sobre la eliminación de los tipos de función .
Muchas de las declaraciones son ambiguas para mí, tal vez por la falta de contexto y, en algunos casos, por mi conocimiento limitado sobre la implementación de sistemas de tipos.
Sin embargo, hay un par de preguntas que creo que puedo formular de manera segura en este sitio para ayudarme a comprender mejor lo que significan.
Sé que podría hacer las preguntas en la lista de correo, pero el hilo es viejo y todas las decisiones ya están tomadas, por lo que es probable que me ignoren, sobre todo viendo que estos tipos ya se están retrasando con sus planes.
En su respuesta para apoyar la eliminación de los tipos de función y respaldar el uso de los tipos de SAM, Brian Goetz dice:
Sin reificación. Hubo un largo hilo sobre lo útil que sería reificar los tipos de función. Sin reificación, los tipos de funciones se ven afectados.
No pude encontrar el hilo que menciona. Ahora, puedo entender que la introducción de un tipo de función estructural puede implicar ciertas complicaciones en el sistema de tipos mayormente nominal de Java, lo que no puedo entender es cómo los tipos SAM parametrizados son diferentes en términos de reificación.
¿No están ambos sujetos a los mismos problemas de reificación? ¿Alguien entiende cómo los tipos de funciones son diferentes a los tipos SAM parametrizados en términos de reificación?
En otro comentario, Goetz dice:
Hay dos enfoques básicos para escribir: nominal y estructural. La identidad de un nominal se basa en su nombre; la identidad de un tipo estructural se basa en lo que está compuesto (como "tupla de int, int" o "función de int a flotante"). La mayoría de los idiomas escogen principalmente nominal o mayormente estructural; no hay muchos lenguajes que combinen con éxito la tipificación nominal y estructural, excepto "alrededor de los bordes". Java es casi completamente nominal (con algunas excepciones: las matrices son de tipo estructural, pero en la parte inferior siempre hay un tipo de elemento nominal; los genéricos también tienen una mezcla de nominal y estructural, y esto es, de hecho, parte de la fuente de muchos de las quejas de la gente sobre los genéricos.) Injerto de un sistema de tipo estructural (tipos de función) en Java ' s sistema de tipo nominal significa nueva complejidad y casos extremos. ¿Vale la pena el beneficio de los tipos de función?
Aquellos de ustedes con experiencia en la implementación de sistemas de tipos. ¿Conoces algunos ejemplos de estas complejidades o casos extremos que él menciona aquí?
Honestamente, me confunden estas acusaciones cuando considero que un lenguaje de programación como Scala, que se basa completamente en la JVM, tiene soporte para tipos estructurales como funciones y tuplas, incluso con los problemas de reificación de la plataforma subyacente.
No me malinterpreten, no estoy diciendo que un tipo de función debería ser mejor que los tipos de SAM. Solo quiero entender por qué tomaron esta decisión.
fuente
Respuestas:
El enfoque SAM es en realidad algo similar a lo que Scala (y C ++ 11) hacen con funciones anónimas (creadas con el
=>
operador de Scala o la[]()
sintaxis de C ++ 11 (lambda)).La primera pregunta a responder en el lado de Java es si el tipo de retorno de una declaración lambda es un nuevo tipo primitve, like
int
obyte
, o un tipo de objeto de algún tipo. En Scala, allí son no hay tipos primitivos - incluso un número entero es un objeto de la claseInt
- y las funciones no son diferentes que son objetos de la clase,Function1
,Function2
, y así sucesivamente, dependiendo del número de los argumentos de la función toma.C ++ 11, ruby y python tienen expresiones lambda que devuelven un objeto invocable de manera explícita o implícita. El objeto devuelto tiene algún método estándar (como el
#call
que se puede usar para llamarlo como una función. C ++ 11, por ejemplo, usa unstd::function
tipo que se sobrecargaoperator()
para que las llamadas del método de llamada del objeto incluso parezcan llamadas de función, textualmente. :-)Donde la nueva propuesta para Java se vuelve desordenada es en el uso de la tipificación estructural para permitir asignar dicho método a otro objeto, como el
Comparator
que tiene un único método principal con un nombre diferente . Si bien esto es conceptualmente repulsivo en cierto modo, lo hace significa que el objeto resultante puede suministrarse a las funciones existentes que tienen un objeto para representar una devolución de llamada, un comparador, y así sucesivamente, y espera ser capaz de llamar a un único bien definido método como#compare
o#callback
. El truco de C ++ de anularoperator()
cuidadosamente esquivó este problema incluso antes de C ++ 11, ya que todas esas opciones de devolución de llamada se podían llamar de la misma manera, y por lo tanto, el STL no requería ningún ajuste para permitir el uso de lambdas de C ++ 11 consort
y así. Java, al no haber utilizado un nombre estándar para tales objetos en el pasado (tal vez porque ningún truco como la sobrecarga del operador hizo obvio un enfoque único), no es tan afortunado, por lo que este truco les impide tener que cambiar una gran cantidad de API existentes .fuente
java.util.Function2<T1, T2, R>
en Java 1.0, entonces no habríaComparator
interfaz, en cambio todos esos métodos tomarían unFunction2<T1, T2, int>
. (Bueno, habría una gran cantidad de interfaces, comoFunction2TT_int<T1, T2>
por las primitivas, pero entiendes mi punto.)