Skip to content

Commit c39c727

Browse files
author
Adriaan Moors
committed
Merge pull request scala#830 from heathermiller/topic/tryeither-fixes
SI-5981, SI-5979, SI-5973, SI-5890 Closed. Maintenance to Try.
2 parents 12e1a9a + ab87bbe commit c39c727

File tree

12 files changed

+478
-173
lines changed

12 files changed

+478
-173
lines changed

src/library/scala/concurrent/Future.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ import java.{ lang => jl }
1818
import java.util.concurrent.atomic.{ AtomicReferenceFieldUpdater, AtomicInteger, AtomicBoolean }
1919

2020
import scala.concurrent.util.Duration
21-
import scala.concurrent.impl.NonFatal
21+
import scala.util.control.NonFatal
2222
import scala.Option
23+
import scala.util.{Try, Success, Failure}
2324

2425
import scala.annotation.tailrec
2526
import scala.collection.mutable.Stack

src/library/scala/concurrent/impl/ExecutionContextImpl.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import java.util.Collection
1515
import scala.concurrent.forkjoin._
1616
import scala.concurrent.{ ExecutionContext, Awaitable }
1717
import scala.concurrent.util.Duration
18+
import scala.util.control.NonFatal
1819

1920

2021

src/library/scala/concurrent/impl/Future.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ package scala.concurrent.impl
1313
import scala.concurrent.util.Duration
1414
import scala.concurrent.{Awaitable, ExecutionContext, CanAwait}
1515
import scala.collection.mutable.Stack
16+
import scala.util.control.NonFatal
1617

1718

1819
private[concurrent] trait Future[+T] extends scala.concurrent.Future[T] with Awaitable[T] {

src/library/scala/package.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ package object scala {
105105
type PartialOrdering[T] = scala.math.PartialOrdering[T]
106106
type PartiallyOrdered[T] = scala.math.PartiallyOrdered[T]
107107

108+
type Either[+A, +B] = scala.util.Either[A, B]
109+
val Either = scala.util.Either
110+
111+
type Left[+A, +B] = scala.util.Left[A, B]
112+
val Left = scala.util.Left
113+
114+
type Right[+A, +B] = scala.util.Right[A, B]
115+
val Right = scala.util.Right
116+
108117
// Annotations which we might move to annotation.*
109118
/*
110119
type SerialVersionUID = annotation.SerialVersionUID

src/library/scala/Either.scala renamed to src/library/scala/util/Either.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99

1010

11-
package scala
11+
package scala.util
1212

1313
import language.implicitConversions
1414

src/library/scala/util/Try.scala

Lines changed: 122 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -11,36 +11,77 @@ package scala.util
1111

1212

1313
import collection.Seq
14-
14+
import scala.util.control.NonFatal
1515

1616

1717
/**
18-
* The `Try` type represents a computation that may either result in an exception,
19-
* or return a success value. It's analagous to the `Either` type.
18+
* The `Try` type represents a computation that may either result in an exception, or return a
19+
* successfully computed value. It's similar to, but semantically different from the [[scala.Either]] type.
20+
*
21+
* Instances of `Try[T]`, are either an instance of [[scala.util.Success]][T] or [[scala.util.Failure]][T].
22+
*
23+
* For example, `Try` can be used to perform division on a user-defined input, without the need to do explicit
24+
* exception-handling in all of the places that an exception might occur.
25+
*
26+
* Example:
27+
* {{{
28+
* import scala.util.{Try, Success, Failure}
29+
*
30+
* def divide: Try[Int] = {
31+
* val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt)
32+
* val divisor = Try(Console.readLine("Enter an Int that you'd like to divide by:\n").toInt)
33+
* val problem = dividend.flatMap(x => divisor.map(y => x/y))
34+
* problem match {
35+
* case Success(v) =>
36+
* println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v)
37+
* Success(v)
38+
* case Failure(e) =>
39+
* println("You must've divided by zero or entered something that's not an Int. Try again!")
40+
* println("Info from the exception: " + e.getMessage)
41+
* divide
42+
* }
43+
* }
44+
*
45+
* }}}
46+
*
47+
* An important property of `Try` shown in the above example is its ability to ''pipeline'', or chain, operations,
48+
* catching exceptions along the way. The `flatMap` and `map` combinators in the above example each essentially
49+
* pass off either their successfully completed value, wrapped in the `Success` type for it to be further operated
50+
* upon by the next combinator in the chain, or the exception wrapped in the `Failure` type usually to be simply
51+
* passed on down the chain. Combinators such as `rescue` and `recover` are designed to provide some type of
52+
* default behavior in the case of failure.
53+
*
54+
* ''Note'': only non-fatal exceptions are caught by the combinators on `Try` (see [[scala.util.control.NonFatal]]).
55+
* Serious system errors, on the other hand, will be thrown.
56+
*
57+
* `Try` comes to the Scala standard library after years of use as an integral part of Twitter's stack.
58+
*
59+
* @since 2.10
2060
*/
2161
sealed abstract class Try[+T] {
22-
/**
23-
* Returns true if the `Try` is a `Failure`, false otherwise.
62+
63+
/** Returns `true` if the `Try` is a `Failure`, `false` otherwise.
2464
*/
2565
def isFailure: Boolean
2666

27-
/**
28-
* Returns true if the `Try` is a `Success`, false otherwise.
67+
/** Returns `true` if the `Try` is a `Success`, `false` otherwise.
2968
*/
3069
def isSuccess: Boolean
3170

32-
/**
33-
* Returns the value from this `Success` or the given argument if this is a `Failure`.
71+
/** Returns the value from this `Success` or the given `default` argument if this is a `Failure`.
3472
*/
3573
def getOrElse[U >: T](default: => U) = if (isSuccess) get else default
3674

37-
/**
38-
* Returns the value from this `Success` or throws the exception if this is a `Failure`.
75+
/** Returns this `Try` if it's a `Success` or the given `default` argument if this is a `Failure`.
76+
*/
77+
def orElse[U >: T](default: => Try[U]) = if (isSuccess) this else default
78+
79+
/** Returns the value from this `Success` or throws the exception if this is a `Failure`.
3980
*/
4081
def get: T
4182

4283
/**
43-
* Applies the given function f if this is a Result.
84+
* Applies the given function `f` if this is a `Success`, otherwise returns `Unit` if this is a `Failure`.
4485
*/
4586
def foreach[U](f: T => U): Unit
4687

@@ -54,39 +95,35 @@ sealed abstract class Try[+T] {
5495
*/
5596
def map[U](f: T => U): Try[U]
5697

57-
def collect[U](pf: PartialFunction[T, U]): Try[U]
58-
59-
def exists(p: T => Boolean): Boolean
60-
6198
/**
6299
* Converts this to a `Failure` if the predicate is not satisfied.
63100
*/
64101
def filter(p: T => Boolean): Try[T]
65102

66103
/**
67-
* Converts this to a `Failure` if the predicate is not satisfied.
68-
*/
69-
def filterNot(p: T => Boolean): Try[T] = filter(x => !p(x))
70-
71-
/**
72-
* Calls the exceptionHandler with the exception if this is a `Failure`. This is like `flatMap` for the exception.
104+
* Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`.
105+
* This is like `flatMap` for the exception.
73106
*/
74-
def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U]
107+
def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U]
75108

76109
/**
77-
* Calls the exceptionHandler with the exception if this is a `Failure`. This is like map for the exception.
110+
* Applies the given function `f` if this is a `Failure`, otherwise returns this if this is a `Success`.
111+
* This is like map for the exception.
78112
*/
79-
def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U]
113+
def recover[U >: T](f: PartialFunction[Throwable, U]): Try[U]
80114

81115
/**
82116
* Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`.
83117
*/
84118
def toOption = if (isSuccess) Some(get) else None
85119

120+
/**
121+
* Returns an empty `Seq` (usually a `List`) if this is a `Failure` or a `Seq` containing the value if this is a `Success`.
122+
*/
86123
def toSeq = if (isSuccess) Seq(get) else Seq()
87124

88125
/**
89-
* Returns the given function applied to the value from this Success or returns this if this is a `Failure`.
126+
* Returns the given function applied to the value from this `Success` or returns this if this is a `Failure`.
90127
* Alias for `flatMap`.
91128
*/
92129
def andThen[U](f: T => Try[U]): Try[U] = flatMap(f)
@@ -97,42 +134,76 @@ sealed abstract class Try[+T] {
97134
*/
98135
def flatten[U](implicit ev: T <:< Try[U]): Try[U]
99136

137+
/**
138+
* Completes this `Try` with an exception wrapped in a `Success`. The exception is either the exception that the
139+
* `Try` failed with (if a `Failure`) or an `UnsupportedOperationException`.
140+
*/
100141
def failed: Try[Throwable]
142+
143+
/** Completes this `Try` by applying the function `f` to this if this is of type `Failure`, or conversely, by applying
144+
* `s` if this is a `Success`.
145+
*/
146+
def transform[U](f: Throwable => Try[U], s: T => Try[U]): Try[U] = this match {
147+
case Success(v) => s(v)
148+
case Failure(e) => f(e)
149+
}
150+
101151
}
102152

153+
object Try {
154+
155+
implicit def try2either[T](tr: Try[T]): Either[Throwable, T] = {
156+
tr match {
157+
case Success(v) => Right(v)
158+
case Failure(t) => Left(t)
159+
}
160+
}
103161

104-
final class Failure[+T](val exception: Throwable) extends Try[T] {
105-
def isFailure: Boolean = true
106-
def isSuccess: Boolean = false
107-
def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = {
108-
try {
109-
if (rescueException.isDefinedAt(exception)) rescueException(exception) else this
110-
} catch {
111-
case e2 => Failure(e2)
162+
implicit def either2try[T](ei: Either[Throwable, T]): Try[T] = {
163+
ei match {
164+
case Right(v) => Success(v)
165+
case Left(t) => Failure(t)
166+
}
167+
}
168+
169+
def apply[T](r: => T): Try[T] = {
170+
try { Success(r) } catch {
171+
case NonFatal(e) => Failure(e)
112172
}
113173
}
174+
175+
}
176+
177+
final case class Failure[+T](val exception: Throwable) extends Try[T] {
178+
def isFailure: Boolean = true
179+
def isSuccess: Boolean = false
180+
def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] =
181+
if (f.isDefinedAt(exception)) f(exception) else this
114182
def get: T = throw exception
115183
def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](exception)
116184
def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](exception)
117185
def foreach[U](f: T => U): Unit = {}
118186
def map[U](f: T => U): Try[U] = Failure[U](exception)
119-
def collect[U](pf: PartialFunction[T, U]): Try[U] = Failure[U](exception)
120187
def filter(p: T => Boolean): Try[T] = this
121-
def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] =
122-
if (rescueException.isDefinedAt(exception)) {
123-
Try(rescueException(exception))
124-
} else {
125-
this
188+
def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = {
189+
try {
190+
if (rescueException.isDefinedAt(exception)) {
191+
Try(rescueException(exception))
192+
} else {
193+
this
194+
}
195+
} catch {
196+
case NonFatal(e) => Failure(e)
126197
}
127-
def exists(p: T => Boolean): Boolean = false
198+
}
128199
def failed: Try[Throwable] = Success(exception)
129200
}
130201

131202

132-
final class Success[+T](value: T) extends Try[T] {
203+
final case class Success[+T](value: T) extends Try[T] {
133204
def isFailure: Boolean = false
134205
def isSuccess: Boolean = true
135-
def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value)
206+
def rescue[U >: T](f: PartialFunction[Throwable, Try[U]]): Try[U] = Success(value)
136207
def get = value
137208
def flatMap[U](f: T => Try[U]): Try[U] =
138209
try f(value)
@@ -142,43 +213,14 @@ final class Success[+T](value: T) extends Try[T] {
142213
def flatten[U](implicit ev: T <:< Try[U]): Try[U] = value
143214
def foreach[U](f: T => U): Unit = f(value)
144215
def map[U](f: T => U): Try[U] = Try[U](f(value))
145-
def collect[U](pf: PartialFunction[T, U]): Try[U] =
146-
if (pf isDefinedAt value) Success(pf(value))
147-
else Failure[U](new NoSuchElementException("Partial function not defined at " + value))
148-
def filter(p: T => Boolean): Try[T] =
149-
if (p(value)) this
150-
else Failure(new NoSuchElementException("Predicate does not hold for " + value))
151-
def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this
152-
def exists(p: T => Boolean): Boolean = p(value)
153-
def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed"))
154-
}
155-
156-
object Failure {
157-
def apply[T](e: Throwable): Failure[T] = new Failure(e)
158-
def unapply(scrutinizee: Any): Option[Throwable] = scrutinizee match {
159-
case Right(_) => None
160-
case Left(e) => Some(e.asInstanceOf[Throwable])
161-
case s: Success[_] => None
162-
case f: Failure[_] => Some(f.exception)
163-
}
164-
}
165-
166-
object Success {
167-
def apply[T](value: T): Success[T] = new Success(value)
168-
def unapply[T](scrutinizee: Any): Option[T] = scrutinizee match {
169-
case Right(v) => Some(v.asInstanceOf[T])
170-
case Left(_) => None
171-
case s: Success[_] => Some(s.get.asInstanceOf[T])
172-
case f: Failure[Throwable] => None
173-
}
174-
}
175-
176-
object Try {
177-
178-
def apply[T](r: => T): Try[T] = {
179-
try { Success(r) } catch {
180-
case e => Failure(e)
216+
def filter(p: T => Boolean): Try[T] = {
217+
try {
218+
if (p(value)) this
219+
else Failure(new NoSuchElementException("Predicate does not hold for " + value))
220+
} catch {
221+
case NonFatal(e) => Failure(e)
181222
}
182223
}
183-
224+
def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this
225+
def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed"))
184226
}

src/library/scala/concurrent/impl/NonFatal.scala renamed to src/library/scala/util/control/NonFatal.scala

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,33 @@
66
** |/ **
77
\* */
88

9-
package scala.concurrent
10-
package impl
9+
package scala.util.control
1110

1211
/**
13-
* Extractor of non-fatal Throwables. Will not match fatal errors
14-
* like VirtualMachineError (OutOfMemoryError)
15-
* ThreadDeath, LinkageError and InterruptedException.
16-
* StackOverflowError is matched, i.e. considered non-fatal.
12+
* Extractor of non-fatal Throwables. Will not match fatal errors like `VirtualMachineError`
13+
* (for example, `OutOfMemoryError`, a subclass of `VirtualMachineError`), `ThreadDeath`,
14+
* `LinkageError`, `InterruptedException`, `ControlThrowable`, or `NotImplementedError`.
15+
* However, `StackOverflowError` is matched, i.e. considered non-fatal.
1716
*
18-
* Usage to catch all harmless throwables:
17+
* Note that [[scala.util.control.ControlThrowable]], an internal Throwable, is not matched by
18+
* `NonFatal` (and would therefore be thrown).
19+
*
20+
* For example, all harmless Throwables can be caught by:
1921
* {{{
2022
* try {
2123
* // dangerous stuff
2224
* } catch {
23-
* case NonFatal(e) => log.error(e, "Something not that bad")
25+
* case NonFatal(e) => log.error(e, "Something not that bad.")
2426
* }
2527
* }}}
2628
*/
27-
private[concurrent] object NonFatal {
29+
object NonFatal {
2830

2931
def unapply(t: Throwable): Option[Throwable] = t match {
3032
case e: StackOverflowError Some(e) // StackOverflowError ok even though it is a VirtualMachineError
3133
// VirtualMachineError includes OutOfMemoryError and other fatal errors
32-
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError None
34+
case _: VirtualMachineError | _: ThreadDeath | _: InterruptedException | _: LinkageError | _: ControlThrowable | _: NotImplementedError => None
3335
case e Some(e)
3436
}
3537

3638
}
37-

0 commit comments

Comments
 (0)