summaryrefslogtreecommitdiff
path: root/src/library
diff options
context:
space:
mode:
authorAdriaan Moors <adriaan.moors@epfl.ch>2012-07-06 04:45:39 -0700
committerAdriaan Moors <adriaan.moors@epfl.ch>2012-07-06 04:45:39 -0700
commitc39c7276c38f9ef66fd7054609ef33627efe5177 (patch)
treeb76ae78c4dbefe5b144701fb53d9b15b806b5060 /src/library
parent12e1a9a5826e6b76687d65520929f41c9498777b (diff)
parentab87bbe8a5728d198057824d3a8c32cfbe20978a (diff)
downloadscala-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.scala3
-rw-r--r--src/library/scala/concurrent/impl/ExecutionContextImpl.scala1
-rw-r--r--src/library/scala/concurrent/impl/Future.scala1
-rw-r--r--src/library/scala/package.scala9
-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.scala202
-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)
}
}
-