funciones de orden superior scala
Scala devuelve declaraciones en funciones anĂ³nimas. (2)
¿Por qué una declaración de retorno explícita (una que usa la palabra clave de return
) en una función anónima regresa de la función con nombre adjunto, y no solo de la función anónima en sí misma?
Por ejemplo, el siguiente programa da como resultado un error de tipo:
def foo: String = {
((x: Integer) => return x)
"foo"
}
Sé que se recomienda evitar la palabra clave de return
, pero me interesa saber por qué las declaraciones de retorno explícitas e implícitas tienen una semántica diferente en funciones anónimas.
En el siguiente ejemplo, la declaración de retorno "sobrevive" después de que m
haya terminado de ejecutarse, y el programa produce una excepción de tiempo de ejecución. Si las funciones anónimas no regresaran de la función adjunta, no sería posible compilar ese código.
def main(args: Array[String]) {
m(3)
}
def m: (Integer => Unit) =
(x: Integer) => return (y: Integer) => 2
La declaración formal se define como la devolución siempre desde el método denominado más cercano que contiene
Una expresión de retorno, retorno e, debe ocurrir dentro del cuerpo de algún método o función con nombre adjunto. El método o función con nombre más interno que encierra en un programa fuente, f, debe tener un tipo de resultado declarado explícitamente, y el tipo de e debe ajustarse a él. La expresión de retorno evalúa la expresión e y devuelve su valor como resultado de f. La evaluación de cualquier declaración o expresión después de la expresión de retorno se omite.
Así que no tiene semántica diferente en un lambda. La diferencia es que, a diferencia de un método normal, un cierre creado a partir de un lambda puede escapar de una llamada al método adjunto y usted puede obtener una excepción si hay un retorno en dicho cierre.
Si la expresión de retorno es parte de una función anónima, es posible que la instancia adjunta de f ya haya regresado antes de que se ejecute la expresión de retorno. En ese caso, la excepción scala.runtime.NonLocalReturnException lanzada no se detectará y propagará la pila de llamadas.
Ahora, en cuanto a "por qué". Una razón menor es la estética: las lambdas son expresiones y es agradable cuando una expresión y toda su subexpresión tienen el mismo significado sin importar la estructura de anidación. Neal Gafter habla de eso en http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html
Sin embargo, la razón principal por la que existe es que le permite simular fácilmente formas de flujo de control comúnmente utilizadas en la programación imperativa, pero aún así le permite abstraer cosas en funciones de orden superior. Como ejemplo de juguete, la construcción foreach de Java ( for (x : xs) { yada; }
) permite un retorno dentro del bucle. Scala no tiene un nivel de idioma para cada uno. En su lugar, pone foreach en la biblioteca (sin contar "por expresión" sin rendimiento, ya que simplemente se desugaron para foreach). Tener un retorno no local significa que puede tomar un Java foreach y traducir directamente a un Scala foreach.
BTW, Ruby, Smalltalk y Common Lisp (desde la parte superior de mi cabeza) también tienen retornos "no locales" similares.
La palabra clave de return
está reservada para los métodos (de clase), no se puede usar en funciones. Usted puede probar fácilmente que:
object Foo {
val bar = (i: Int) => return i + i
}
Esto da
<console>:42: error: return outside method definition
object Foo { val bar = (i: Int) => return i + i }
^
Principalmente, puede tratar los métodos y las funciones como iguales, debido a que el método de apply
de la función se comporta sintácticamente como llamar a un método, y la llamada expansión eta permite que un método se pase como un argumento de función.
En este caso, hace una diferencia. Al definir como método, es legal:
object Foo {
def bar(i: Int): Int = return i + i
}
En resumen, solo debe usar la return
en métodos que permitan return
condicionales (anticipadas). Vea esta publicación para una discusión sobre métodos versus funciones.