diff options
author | Heather Miller <heather.miller@epfl.ch> | 2012-07-05 16:49:40 +0200 |
---|---|---|
committer | Heather Miller <heather.miller@epfl.ch> | 2012-07-05 16:49:40 +0200 |
commit | d2fd2922e71dddb2c22c1a5b0cf36614cd78e1b4 (patch) | |
tree | cd32e94c9dba1e1990ac8d738a7e2b958e7c0142 /src/library/scala/util/Try.scala | |
parent | 171a1d8bc859947d80d4728c251abe330dbe9802 (diff) | |
download | scala-d2fd2922e71dddb2c22c1a5b0cf36614cd78e1b4.tar.gz scala-d2fd2922e71dddb2c22c1a5b0cf36614cd78e1b4.tar.bz2 scala-d2fd2922e71dddb2c22c1a5b0cf36614cd78e1b4.zip |
SI-5981, SI-5979, SI-5973 Closed. Maintenance to Try.
Diffstat (limited to 'src/library/scala/util/Try.scala')
-rw-r--r-- | src/library/scala/util/Try.scala | 202 |
1 files changed, 122 insertions, 80 deletions
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")) } |