scala.util.control.Breaks.scala Maven / Gradle / Ivy
/*
* Scala (https://www.scala-lang.org)
*
* Copyright EPFL and Lightbend, Inc.
*
* Licensed under Apache License 2.0
* (http://www.apache.org/licenses/LICENSE-2.0).
*
* See the NOTICE file distributed with this work for
* additional information regarding copyright ownership.
*/
package scala.util.control
/** Provides the `break` control abstraction.
*
* The `break` method uses a `ControlThrowable` to transfer
* control up the stack to an enclosing `breakable`.
*
* It is typically used to abruptly terminate a `for` loop,
* but can be used to return from an arbitrary computation.
*
* Control resumes after the `breakable`.
*
* If there is no matching `breakable`, the `BreakControl`
* thrown by `break` is handled in the usual way: if not
* caught, it may terminate the current `Thread`.
*
* `BreakControl` carries no stack trace, so the default
* exception handler does not print useful diagnostic
* information; there is no compile-time warning if there
* is no matching `breakable`.
*
* A catch clause using `NonFatal` is safe to use with
* `break`; it will not short-circuit the transfer
* of control to the enclosing `breakable`.
*
* A `breakable` matches a call to `break` if the methods
* were invoked on the same receiver object, which may be the
* convenience value `Breaks`.
*
* Example usage:
* {{{
* val mybreaks = new Breaks
* import mybreaks.{break, breakable}
*
* breakable {
* for (x <- xs) {
* if (done) break()
* f(x)
* }
* }
* }}}
* Calls to `break` from one instance of `Breaks` will never
* resume at the `breakable` of some other instance.
*
* Any intervening exception handlers should use `NonFatal`,
* or use `Try` for evaluation:
* {{{
* val mybreaks = new Breaks
* import mybreaks.{break, breakable}
*
* breakable {
* for (x <- xs) Try { if (quit) break else f(x) }.foreach(println)
* }
* }}}
*/
class Breaks {
private[this] val breakException = new BreakControl
/** A block from which one can exit with a `break`. The `break` may be
* executed further down in the call stack provided that it is called on the
* exact same instance of `Breaks`.
*/
def breakable(op: => Unit): Unit =
try op catch { case ex: BreakControl if ex eq breakException => }
sealed trait TryBlock[T] {
def catchBreak(onBreak: => T): T
}
/** Try a computation that produces a value, supplying a default
* to be used if the computation terminates with a `break`.
*
* {{{
* tryBreakable {
* (1 to 3).map(i => if (math.random < .5) break else i * 2)
* } catchBreak {
* Vector.empty
* }
* }}}
*/
def tryBreakable[T](op: => T): TryBlock[T] =
new TryBlock[T] {
def catchBreak(onBreak: => T) =
try op catch { case ex: BreakControl if ex eq breakException => onBreak }
}
/** Break from the dynamically closest enclosing breakable block that also uses
* this `Breaks` instance.
*
* @note This might be different from the statically closest enclosing block!
* @note Invocation without parentheses relies on the conversion to "empty application".
*/
def break(): Nothing = throw breakException
}
/** An object that can be used for the break control abstraction.
*
* Example usage:
* {{{
* import Breaks.{break, breakable}
*
* breakable {
* for (...) {
* if (...) break
* }
* }
* }}}
*/
object Breaks extends Breaks
private class BreakControl extends ControlThrowable