2011-05-26 14 views
33

Hace algún tiempo, Oracle decidió que agregar Cierres a Java 8 sería una buena idea. Me pregunto cómo se resuelven los problemas de diseño en comparación con Scala, que tenía cierres desde el primer día.Cierres en Scala vs Cierres en Java

Citando el Cuestiones pendientes de javac.info:

  1. Se puede Mangos Método usarse para los tipos de función? No es obvio cómo hacer que funcione. Un problema es que el método maneja los parámetros de tipo reify, pero de una manera que interfiere con la función de subtipado.

  2. ¿Podemos deshacernos de la declaración explícita de los parámetros del tipo "throws"? La idea sería usar inferencia de tipo disyuntiva siempre que el límite declarado sea un tipo de excepción comprobada. Esto no es estrictamente compatible con versiones anteriores, pero es poco probable que rompa el código real existente. Sin embargo, probablemente no podamos deshacernos de los "tiros" en el argumento tipo debido a la ambigüedad sintáctica.

  3. Disallow @Shared en viejo estilo variables de índice bucle

  4. interfaces de Handle como Comparador que definen más de un método, todos pero uno de los cuales se llevará a cabo por un método heredado de Object. La definición de "interfaz con un único método" debe contar solo con métodos que no se implementarían mediante un método en Object y debe contar varios métodos como uno si la implementación de uno de ellos los implementara a todos. Principalmente, esto requiere una especificación más precisa de lo que significa para una interfaz tener solo un único método abstracto.

  5. Especificar correspondencia de los tipos de función para interfaces: nombres, parámetros, etc. Debemos especificar plenamente la correspondencia de los tipos de función a las interfaces generadas por el sistema con precisión.

  6. Tipo de inferencia. Las reglas para la inferencia de tipos deben aumentarse para dar cabida a la inferencia de los parámetros del tipo de excepción. Del mismo modo, las relaciones de subtipo utilizadas por la conversión de cierre también deberían reflejarse.

  7. Parámetros del tipo de excepción elida para ayudar a la modificación de la transparencia de excepción. Quizás hacer que los parámetros del tipo de excepción sean el límite. Esto permite la actualización de interfaces genéricas existentes que no tienen un parámetro de tipo para la excepción, como java.util.concurrent.Callable, mediante la adición de un nuevo parámetro de excepción genérico.

  8. ¿Cómo se forman los literales de clase para los tipos de funciones formados? ¿Es #void(). Clase? Si es así, ¿cómo funciona si los tipos de objetos se borran? ¿Es #? (?). Clase?

  9. El cargador de clases del sistema debe generar dinámicamente interfaces de tipo de función. Las interfaces correspondientes a los tipos de funciones se deben generar a petición del cargador de clases de arranque, para que puedan compartirse entre todos los códigos de usuario.Para el prototipo, podemos hacer que javac genere estas interfaces para que el código generado por el prototipo pueda ejecutarse en máquinas virtuales (JDK5-6).

  10. ¿Debe la evaluación de una expresión lambda producir un objeto nuevo cada vez? Esperemos que no. Si una lambda no captura ninguna variable de un ámbito adjunto, por ejemplo, puede asignarse estáticamente. De manera similar, en otras situaciones una lambda podría moverse de un bucle interno si no captura ninguna de las variables declaradas dentro del bucle. Por lo tanto, sería mejor si la especificación no promete nada acerca de la identidad de referencia del resultado de una expresión lambda, por lo que el compilador puede realizar dichas optimizaciones.

Por lo que yo entiendo 2., 6. y 7. no son un problema en Scala, Scala, porque no utiliza excepciones comprobadas como una especie de "Shadow tipo de sistema" como Java.

¿Y el resto?

Respuesta

29

1) ¿Se pueden usar identificadores de método para los tipos de funciones?

Scala se dirige a JDK 5 y 6 que no tienen controles de método, por lo que aún no se ha tratado de resolver ese problema.

2) ¿Podemos deshacernos de la declaración explícita de los parámetros del tipo "throws"?

Scala no tiene excepciones marcadas.

3) Disallow @Shared en las variables de índice de bucle antiguas.

Scala no tiene variables de índice de bucle. Aún así, la misma idea se puede expresar con cierto tipo de ciclo while. La semántica de Scala es bastante estándar aquí. Los enlaces de símbolos se capturan y si el símbolo pasa a mapear a una celda de referencia mutable, entonces en tu propia cabeza ya sea.

4) Manija interfaces como Comparador que definen más de un método todos menos uno de los cuales proceden de objetos usuarios

Scala tienden a utilizar funciones (o funciones implícitas) para obligar a las funciones del tipo derecho a una interfaz. p.ej.

[implicit] def toComparator[A](f : (A, A) => Int) = new Comparator[A] { 
    def compare(x : A, y : A) = f(x, y) 
} 

5) Especificar correspondencia de los tipos de función para interfaces: biblioteca estándar

de Scala incluye rasgos FuncitonN para 0 < = N < = 22 y la especificación dice que los literales de función crear instancias de esos rasgos

6) Escriba inferencia. Las reglas para la inferencia de tipos deben ser aumentadas para acomodar la inferencia de los parámetros del tipo de excepción.

Desde Scala no haber comprobado excepciones que puede batea en todo este tema

7) elididas parámetros de tipo de excepción para ayudar a la transparencia excepción de adaptación.

Mismo trato, sin excepciones marcadas.

8) ¿Cómo se forman los literales de clase para los tipos de función? ¿Es #void(). Clase? Si es así, ¿cómo funciona si los tipos de objetos se borran? ¿Es #? (?). Clase?

classOf[A => B] //or, equivalently, 
classOf[Function1[A,B]] 

Tipo de borrado es el tipo de borrado. Los literales anteriores producen scala.lang.Function1 independientemente de la elección de A y B. Si lo prefiere, puede escribir

classOf[ _ => _ ] // or 
classOf[Function1[ _,_ ]] 

9) El cargador de clases del sistema debe generar dinámicamente interfaces de tipo función.

Scala limita arbitrariamente el número de argumentos como máximo a 22 para que no tenga que generar las clases de FunctionN dinámicamente.

10) Debe la evaluación de una expresión lambda producir un objeto nuevo cada vez?

especificación La Scala no dice que debe hacerlo. Pero a partir de 2.8.1, el compilador no optimiza el caso en el que una lambda no captura nada de su entorno. No he probado con 2.9.0 todavía.

12

Voy a abordar único número 4 aquí.

Una de las cosas que distingue a los "cierres" de Java de los cierres encontrados en otros lenguajes es que se pueden usar en lugar de la interfaz que no describe una función, por ejemplo, Runnable. Esto es lo que significa SAM, Método abstracto único.

Java hace esto porque estas interfaces abundan en la biblioteca Java, y abundan en la biblioteca Java porque Java se creó sin tipos de funciones o cierres. En su ausencia, cada código que necesitaba inversión de control tuvo que recurrir al uso de una interfaz SAM.

Por ejemplo, Arrays.sort toma un objeto Comparator que realizará la comparación entre los miembros de la matriz que se va a ordenar. Por el contrario, Scala puede ordenar una List[A] mediante la recepción de una función (A, A) => Int, que se pasa fácilmente a través de un cierre. Ver nota 1 al final, sin embargo.

Por lo tanto, como la biblioteca de Scala se creó para un idioma con tipos de funciones y cierres, no es necesario admitir cierres de SAM en Scala.

Por supuesto, hay una cuestión de interoperabilidad Scala/Java - mientras que la biblioteca de Scala puede no necesitar algo como SAM, biblioteca de Java. Hay dos formas que se pueden resolver. En primer lugar, como Scala admite cierres y tipos de funciones, es muy fácil crear métodos de ayuda. Por ejemplo:

def runnable(f:() => Unit) = new Runnable { 
    def run() = f() 
} 

runnable {() => println("Hello") } // creates a Runnable 

En realidad, este ejemplo en particular se puede hacer aún más corto mediante el uso de parámetros por nombre de Scala, pero eso no viene al caso. De todos modos, esto es algo que, podría decirse, Java podría haber hecho en lugar de lo que va a hacer. Dada la prevalencia de las interfaces SAM, no es tan sorprendente.

La otra forma en que Scala maneja esto es mediante conversiones implícitas.Simplemente anteponiendo implicit al método anterior runnable, se crea un método que se aplica automáticamente (nota 2) cada vez que se requiere Runnable, pero se proporciona una función () => Unit.

Los implicits son muy únicos, sin embargo, y aún son controvertidos en cierta medida.

Nota 1: En realidad, este ejemplo particular era elegir con cierta malicia ... Comparator tiene dos métodos abstractos en lugar de uno, que es todo el problema con él. Dado que uno de sus métodos puede implementarse en términos del otro, creo que simplemente "restarán" los métodos del defensor de la lista abstracta.

Y, en el lado Scala, a pesar de que hay un método para ordenar que utiliza (A, A) => Boolean, no (A, A) => Int, el estándar método de clasificación exige un objeto Ordering, que es bastante similar a Java de Comparator! En el caso de Scala, sin embargo, Ordering realiza el rol de tipo clase.

Nota 2: implícitos se aplican automáticamente, una vez que hayan sido importados en el alcance.