diff options
author | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-07-06 04:45:39 -0700 |
---|---|---|
committer | Adriaan Moors <adriaan.moors@epfl.ch> | 2012-07-06 04:45:39 -0700 |
commit | c39c7276c38f9ef66fd7054609ef33627efe5177 (patch) | |
tree | b76ae78c4dbefe5b144701fb53d9b15b806b5060 /src/library | |
parent | 12e1a9a5826e6b76687d65520929f41c9498777b (diff) | |
parent | ab87bbe8a5728d198057824d3a8c32cfbe20978a (diff) | |
download | scala-c39c7276c38f9ef66fd7054609ef33627efe5177.tar.gz scala-c39c7276c38f9ef66fd7054609ef33627efe5177.tar.bz2 scala-c39c7276c38f9ef66fd7054609ef33627efe5177.zip |
Merge pull request #830 from heathermiller/topic/tryeither-fixes
SI-5981, SI-5979, SI-5973, SI-5890 Closed. Maintenance to Try.
Diffstat (limited to 'src/library')
-rw-r--r-- | src/library/scala/concurrent/Future.scala | 3 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/ExecutionContextImpl.scala | 1 | ||||
-rw-r--r-- | src/library/scala/concurrent/impl/Future.scala | 1 | ||||
-rw-r--r-- | src/library/scala/package.scala | 9 | ||||
-rw-r--r-- | src/library/scala/util/Either.scala (renamed from src/library/scala/Either.scala) | 2 | ||||
-rw-r--r-- | src/library/scala/util/Try.scala | 202 | ||||
-rw-r--r-- | src/library/scala/util/control/NonFatal.scala (renamed from src/library/scala/concurrent/impl/NonFatal.scala) | 23 |
7 files changed, 148 insertions, 93 deletions
diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala index 2a52d0ac0b..2de0c57253 100644 --- a/src/library/scala/concurrent/Future.scala +++ b/src/library/scala/concurrent/Future.scala @@ -18,8 +18,9 @@ import java.{ lang => jl } import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean } import scala.concurrent.util.Duration -import scala.concurrent.impl.NonFatal +import scala.util.control.NonFatal import scala.Option +import scala.util.{Try, Success, Failure} import scala.annotation.tailrec import scala.collection.mutable.Stack diff --git a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala index 7549bf8314..4c6347dce0 100644 --- a/src/library/scala/concurrent/impl/ExecutionContextImpl.scala +++ b/src/library/scala/concurrent/impl/ExecutionContextImpl.scala @@ -15,6 +15,7 @@ import java.util.Collection import scala.concurrent.forkjoin._ import scala.concurrent.{ ExecutionContext, Awaitable } import scala.concurrent.util.Duration +import scala.util.control.NonFatal diff --git a/src/library/scala/concurrent/impl/Future.scala b/src/library/scala/concurrent/impl/Future.scala index 6a3487adde..8012ea6a93 100644 --- a/src/library/scala/concurrent/impl/Future.scala +++ b/src/library/scala/concurrent/impl/Future.scala @@ -13,6 +13,7 @@ package scala.concurrent.impl import scala.concurrent.util.Duration import scala.concurrent.{Awaitable, ExecutionContext, CanAwait} import scala.collection.mutable.Stack +import scala.util.control.NonFatal private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] { diff --git a/src/library/scala/package.scala b/src/library/scala/package.scala index e3890d7a9d..c4a8585563 100644 --- a/src/library/scala/package.scala +++ b/src/library/scala/package.scala @@ -105,6 +105,15 @@ package object scala { type PartialOrdering[T] = scala.math.PartialOrdering[T] type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T] + type Either[+A, +B] = scala.util.Either[A, B] + val Either = scala.util.Either + + type Left[+A, +B] = scala.util.Left[A, B] + val Left = scala.util.Left + + type Right[+A, +B] = scala.util.Right[A, B] + val Right = scala.util.Right + // Annotations which we might move to annotation.* /* type SerialVersionUID = annotation.SerialVersionUID diff --git a/src/library/scala/Either.scala b/src/library/scala/util/Either.scala index b35d8a7c8a..1a2e2d48d5 100644 --- a/src/library/scala/Either.scala +++ b/src/library/scala/util/Either.scala @@ -8,7 +8,7 @@ -package scala +package scala.util import language.implicitConversions diff --git a/src/library/scala/util/Try.scala b/src/library/scala/util/Try.scala index 8faba236f0..988f68bc18 100644 --- a/src/library/scala/util/Try.scala +++ b/src/library/scala/util/Try.scala @@ -11,36 +11,77 @@ package scala.util import collection.Seq - +import scala.util.control.NonFatal /** - * The `Try` type represents a computation that may either result in an exception, - * or return a success value. It's analagous to the `Either` type. + * The `Try` type represents a computation that may either result in an exception, or return a + * successfully computed value. It's similar to, but semantically different from the [[scala.Either]] type. + * + * Instances of `Try[T]`, are either an instance of [[scala.util.Success]][T] or [[scala.util.Failure]][T]. + * + * For example, `Try` can be used to perform division on a user-defined input, without the need to do explicit + * exception-handling in all of the places that an exception might occur. + * + * Example: + * {{{ + * import scala.util.{Try, Success, Failure} + * + * def divide: Try[Int] = { + * val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt) + * val divisor = Try(Console.readLine("Enter an Int that you'd like to divide by:\n").toInt) + * val problem = dividend.flatMap(x => divisor.map(y => x/y)) + * problem match { + * case Success(v) => + * println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v) + * Success(v) + * case Failure(e) => + * println("You must've divided by zero or entered something that's not an Int. Try again!") + * println("Info from the exception: " + e.getMessage) + * divide + * } + * } + * + * }}} + * + * An important property of `Try` shown in the above example is its ability to ''pipeline'', or chain, operations, + * catching exceptions along the way. The `flatMap` and `map` combinators in the above example each essentially + * pass off either their successfully completed value, wrapped in the `Success` type for it to be further operated + * upon by the next combinator in the chain, or the exception wrapped in the `Failure` type usually to be simply + * passed on down the chain. Combinators such as `rescue` and `recover` are designed to provide some type of + * default behavior in the case of failure. + * + * ''Note'': only non-fatal exceptions are caught by the combinators on `Try` (see [[scala.util.control.NonFatal]]). + * Serious system errors, on the other hand, will be thrown. + * + * `Try` comes to the Scala standard library after years of use as an integral part of Twitter's stack. + * + * @since 2.10 */ sealed abstract class Try[+T] { - /** - * Returns true if the `Try` is a `Failure`, false otherwise. + + /** Returns `true` if the `Try` is a `Failure`, `false` otherwise. */ def isFailure: Boolean - /** - * Returns true if the `Try` is a `Success`, false otherwise. + /** Returns `true` if the `Try` is a `Success`, `false` otherwise. */ def isSuccess: Boolean - /** - * Returns the value from this `Success` or the given argument if this is a `Failure`. + /** Returns the value from this `Success` or the given `default` argument if this is a `Failure`. */ def getOrElse[U >: T](default: => U) = if (isSuccess) get else default - /** - * Returns the value from this `Success` or throws the exception if this is a `Failure`. + /** Returns this `Try` if it's a `Success` or the given `default` argument if this is a `Failure`. + */ + def orElse[U >: T](default: => Try[U]) = if (isSuccess) this else default + + /** Returns the value from this `Success` or throws the exception if this is a `Failure`. */ def get: T /** - * Applies the given function f if this is a Result. + * Applies the given function `f` if this is a `Success`, otherwise returns `Unit` if this is a `Failure`. */ def foreach[U](f: T => U): Unit @@ -54,39 +95,35 @@ sealed abstract class Try[+T] { */ def map[U](f: T => U): Try[U] - def collect[U](pf: PartialFunction[T, U]): Try[U] - - def exists(p: T => Boolean): Boolean - /** * Converts this to a `Failure` if the predicate is not satisfied. */ def filter(p: T => Boolean): Try[T] /** - * Converts this to a `Failure` if the predicate is not satisfied. - */ - def filterNot(p: T => Boolean): Try[T] = filter(x => !p(x)) - - /** - * Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception. + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like `flatMap` for the exception. */ - def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] + def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] /** - * Calls the exceptionHandler with the exception if this is a `Failure`. This is like map for the exception. + * Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`. + * This is like map for the exception. */ - def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] + def recover[U >: T](f: PartialFunction[Throwable, U]): Try[U] /** * Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`. */ def toOption = if (isSuccess) Some(get) else None + /** + * Returns an empty `Seq` (usually a `List`) if this is a `Failure` or a `Seq` containing the value if this is a `Success`. + */ def toSeq = if (isSuccess) Seq(get) else Seq() /** - * Returns the given function applied to the value from this Success or returns this if this is a `Failure`. + * Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`. * Alias for `flatMap`. */ def andThen[U](f: T => Try[U]): Try[U] = flatMap(f) @@ -97,42 +134,76 @@ sealed abstract class Try[+T] { */ def flatten[U](implicit ev: T <:< Try[U]): Try[U] + /** + * Completes this `Try` with an exception wrapped in a `Success`. The exception is either the exception that the + * `Try` failed with (if a `Failure`) or an `UnsupportedOperationException`. + */ def failed: Try[Throwable] + + /** Completes this `Try` by applying the function `f` to this if this is of type `Failure`, or conversely, by applying + * `s` if this is a `Success`. + */ + def transform[U](f: Throwable => Try[U], s: T => Try[U]): Try[U] = this match { + case Success(v) => s(v) + case Failure(e) => f(e) + } + } +object Try { + + implicit def try2either[T](tr: Try[T]): Either[Throwable, T] = { + tr match { + case Success(v) => Right(v) + case Failure(t) => Left(t) + } + } -final class Failure[+T](val exception: Throwable) extends Try[T] { - def isFailure: Boolean = true - def isSuccess: Boolean = false - def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = { - try { - if (rescueException.isDefinedAt(exception)) rescueException(exception) else this - } catch { - case e2 => Failure(e2) + implicit def either2try[T](ei: Either[Throwable, T]): Try[T] = { + ei match { + case Right(v) => Success(v) + case Left(t) => Failure(t) + } + } + + def apply[T](r: => T): Try[T] = { + try { Success(r) } catch { + case NonFatal(e) => Failure(e) } } + +} + +final case class Failure[+T](val exception: Throwable) extends Try[T] { + def isFailure: Boolean = true + def isSuccess: Boolean = false + def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = + if (f.isDefinedAt(exception)) f(exception) else this def get: T = throw exception def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](exception) def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](exception) def foreach[U](f: T => U): Unit = {} def map[U](f: T => U): Try[U] = Failure[U](exception) - def collect[U](pf: PartialFunction[T, U]): Try[U] = Failure[U](exception) def filter(p: T => Boolean): Try[T] = this - def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = - if (rescueException.isDefinedAt(exception)) { - Try(rescueException(exception)) - } else { - this + def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = { + try { + if (rescueException.isDefinedAt(exception)) { + Try(rescueException(exception)) + } else { + this + } + } catch { + case NonFatal(e) => Failure(e) } - def exists(p: T => Boolean): Boolean = false + } def failed: Try[Throwable] = Success(exception) } -final class Success[+T](value: T) extends Try[T] { +final case class Success[+T](value: T) extends Try[T] { def isFailure: Boolean = false def isSuccess: Boolean = true - def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value) + def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value) def get = value def flatMap[U](f: T => Try[U]): Try[U] = try f(value) @@ -142,43 +213,14 @@ final class Success[+T](value: T) extends Try[T] { def flatten[U](implicit ev: T <:< Try[U]): Try[U] = value def foreach[U](f: T => U): Unit = f(value) def map[U](f: T => U): Try[U] = Try[U](f(value)) - def collect[U](pf: PartialFunction[T, U]): Try[U] = - if (pf isDefinedAt value) Success(pf(value)) - else Failure[U](new NoSuchElementException("Partial function not defined at " + value)) - def filter(p: T => Boolean): Try[T] = - if (p(value)) this - else Failure(new NoSuchElementException("Predicate does not hold for " + value)) - def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this - def exists(p: T => Boolean): Boolean = p(value) - def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed")) -} - -object Failure { - def apply[T](e: Throwable): Failure[T] = new Failure(e) - def unapply(scrutinizee: Any): Option[Throwable] = scrutinizee match { - case Right(_) => None - case Left(e) => Some(e.asInstanceOf[Throwable]) - case s: Success[_] => None - case f: Failure[_] => Some(f.exception) - } -} - -object Success { - def apply[T](value: T): Success[T] = new Success(value) - def unapply[T](scrutinizee: Any): Option[T] = scrutinizee match { - case Right(v) => Some(v.asInstanceOf[T]) - case Left(_) => None - case s: Success[_] => Some(s.get.asInstanceOf[T]) - case f: Failure[Throwable] => None - } -} - -object Try { - - def apply[T](r: => T): Try[T] = { - try { Success(r) } catch { - case e => Failure(e) + def filter(p: T => Boolean): Try[T] = { + try { + if (p(value)) this + else Failure(new NoSuchElementException("Predicate does not hold for " + value)) + } catch { + case NonFatal(e) => Failure(e) } } - + def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this + def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed")) } diff --git a/src/library/scala/concurrent/impl/NonFatal.scala b/src/library/scala/util/control/NonFatal.scala index bc509e664c..9da2f63307 100644 --- a/src/library/scala/concurrent/impl/NonFatal.scala +++ b/src/library/scala/util/control/NonFatal.scala @@ -6,32 +6,33 @@ ** |/ ** \* */ -package scala.concurrent -package impl +package scala.util.control /** - * Extractor of non-fatal Throwables. Will not match fatal errors - * like VirtualMachineError (OutOfMemoryError) - * ThreadDeath, LinkageError and InterruptedException. - * StackOverflowError is matched, i.e. considered non-fatal. + * Extractor of non-fatal Throwables. Will not match fatal errors like `VirtualMachineError` + * (for example, `OutOfMemoryError`, a subclass of `VirtualMachineError`), `ThreadDeath`, + * `LinkageError`, `InterruptedException`, `ControlThrowable`, or `NotImplementedError`. + * However, `StackOverflowError` is matched, i.e. considered non-fatal. * - * Usage to catch all harmless throwables: + * Note that [[scala.util.control.ControlThrowable]], an internal Throwable, is not matched by + * `NonFatal` (and would therefore be thrown). + * + * For example, all harmless Throwables can be caught by: * {{{ * try { * // dangerous stuff * } catch { - * case NonFatal(e) => log.error(e, "Something not that bad") + * case NonFatal(e) => log.error(e, "Something not that bad.") * } * }}} */ -private[concurrent] object NonFatal { +object NonFatal { def unapply(t: Throwable): Option[Throwable] = t match { case e: StackOverflowError ⇒ Some(e) // StackOverflowError ok even though it is a VirtualMachineError // VirtualMachineError includes OutOfMemoryError and other fatal errors - case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError ⇒ None + case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable | _: NotImplementedError => None case e ⇒ Some(e) } } - |