¿Por qué Scala tiene retorno pero no se rompe y continúa?

22

Scala no tiene breako continue, por lo que algunos comportamientos en bucle requieren un poco más de pensamiento.

Terminar un ciclo temprano requiere recursión de cola, excepciones o scala.util.control.Breaks(que usa excepciones).

La razón de esto es que, como goto, son construcciones de flujo que oscurecen el flujo, y se pueden lograr de maneras mejores y menos sorprendentes.

Pero parece que esos mismos argumentos podrían usarse return.

¿Por qué Scala omitió deliberadamente breaky continue, pero no return?

Paul Draper
fuente
1
Me imagino que los autores del lenguaje consideran la recursividad de cola como la forma de construir la iteración. Me lo imagino breaky continuenecesito maquinaria de limpieza adicional. OTOH returnes una forma de terminar una función de manera ordenada, y cualquier maquinaria de limpieza ya está allí de todos modos.
9000
1
No hay breakable { for { break; } }más que una idea de último momento, y probablemente lejos de ser eficiente.
Joop Eggen
Porque con las funciones en realidad no hay razón para ello. Es lo mismo en Python. Cada vez que use un bucle for con break, puede escribir una función, poner su bucle en la función y usar return. No puedo pensar en una situación en la que esta no sea una buena idea con respecto al código limpio. Para el rendimiento, una limpieza puede ser mejor, pero el rendimiento no tiene la máxima prioridad en scala.
valenterry
2
Parece que esta pregunta tiene una respuesta aquí: stackoverflow.com/questions/3770989/…
Michael Shaw
3
@PaulDraper: La respuesta breaky continueestá contenida en su pregunta y en el enlace de su pregunta. La pregunta para returnes exactamente de qué se trataba la pregunta que vinculé, y fue respondida, al menos en la respuesta aceptada y más votada. Si las dos respuestas juntas no responden a su pregunta, tal vez podría editar la pregunta para aclararla.
Michael Shaw

Respuestas:

16

Romper y continuar:

En una charla sobre Scala , Martin Odersky dio 3 razones para no incluir el descanso o continuar en la diapositiva 22:

  • Son un poco imperativos; mejor usar muchas funciones más pequeñas.
  • Cuestiona cómo interactuar con los cierres.
  • ¡No son necesarios!

Y luego dice: "Podemos apoyarlos únicamente en las bibliotecas". En la diapositiva 23, da un código que implementa break. Aunque no conozco a Scala lo suficientemente bien como para estar seguro, parece que el fragmento corto en esa diapositiva es todo lo que se necesita para implementar break, y eso continuepodría implementarse en un código que es igualmente corto.

Poder implementar cosas como esta en las bibliotecas simplifica el lenguaje central.

En 'Programación en Scala, segunda edición', de Martin Odersky, Lex Spoon y Bill Venners, se brinda la siguiente explicación:

Es posible que haya notado que no se ha mencionado breako continue. Scala omite estos comandos porque no encajan bien con los literales de función ... Está claro lo que continuesignifica dentro de un whilebucle, pero ¿qué significaría dentro de un literal de función? ... Hay muchas formas de programar sin breaky continue, y si aprovecha los literales de función, esas alternativas a menudo pueden ser más cortas que el código original.

Regreso:

Los retornos podrían considerarse un poco imperativos en el estilo, ya que el retorno es un verbo, un comando para hacer algo. Pero también se pueden ver de una manera puramente funcional / declarativa: definen cuál es el valor de retorno de la función (incluso si, en una función con múltiples retornos, solo cada uno da una definición parcial).

En el mismo libro, dicen lo siguiente sobre return:

En ausencia de una returndeclaración explícita , un método Scala devuelve el último valor calculado por el método. El estilo recomendado para los métodos es, de hecho, evitar tener returndeclaraciones explícitas, y especialmente múltiples . En cambio, piense en cada método como una expresión que produce un valor, que se devuelve.

Los métodos finalizan y devuelven un valor, incluso si returnno se usa una declaración, por lo que no puede haber problemas con los cierres, ya que de lo contrario los cierres no funcionarían.

Tampoco puede haber problemas para combinar bien con los literales de función, ya que la función tiene que devolver un valor de todos modos.

Michael Shaw
fuente
2
Con respecto al regreso, parece haber algunos peligros leves: tpolecat.github.io/2014/05/09/return.html
bbarker
0

Creo que las respuestas anteriores hacen justicia a los problemas de definir la semántica para breakocontinue para Scala en todo el lenguaje para Scala, con contextos relativamente sin restricciones.

Escribí una pequeña biblioteca que define breakycontinue en un contexto más restringido: iteración sobre secuencias a través de Scala para comprensiones. Al centrarme en ese contexto, creo que la semántica se vuelve inequívoca y fácil de razonar.

La biblioteca está disponible aquí: https://github.com/erikerlandson/breakable

Aquí hay un ejemplo simple de cómo se ve en el código:

scala> import com.manyangled.breakable._
import com.manyangled.breakable._

scala> val bkb2 = for {
     |   (x, xLab) <- Stream.from(0).breakable   // create breakable sequence with a method
     |   (y, yLab) <- breakable(Stream.from(0))  // create with a function
     |   if (x % 2 == 1) continue(xLab)          // continue to next in outer "x" loop
     |   if (y % 2 == 0) continue(yLab)          // continue to next in inner "y" loop
     |   if (x > 10) break(xLab)                 // break the outer "x" loop
     |   if (y > x) break(yLab)                  // break the inner "y" loop
     | } yield (x, y)
bkb2: com.manyangled.breakable.Breakable[(Int, Int)] = com.manyangled.breakable.Breakable@34dc53d2

scala> bkb2.toVector
res0: Vector[(Int, Int)] = Vector((2,1), (4,1), (4,3), (6,1), (6,3), (6,5), (8,1), (8,3), (8,5), (8,7), (10,1), (10,3), (10,5), (10,7), (10,9))
eje
fuente