@@ -11,36 +11,77 @@ package scala.util
11
11
12
12
13
13
import collection .Seq
14
-
14
+ import scala . util . control . NonFatal
15
15
16
16
17
17
/**
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
20
60
*/
21
61
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.
24
64
*/
25
65
def isFailure : Boolean
26
66
27
- /**
28
- * Returns true if the `Try` is a `Success`, false otherwise.
67
+ /** Returns `true` if the `Try` is a `Success`, `false` otherwise.
29
68
*/
30
69
def isSuccess : Boolean
31
70
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`.
34
72
*/
35
73
def getOrElse [U >: T ](default : => U ) = if (isSuccess) get else default
36
74
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`.
39
80
*/
40
81
def get : T
41
82
42
83
/**
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` .
44
85
*/
45
86
def foreach [U ](f : T => U ): Unit
46
87
@@ -54,39 +95,35 @@ sealed abstract class Try[+T] {
54
95
*/
55
96
def map [U ](f : T => U ): Try [U ]
56
97
57
- def collect [U ](pf : PartialFunction [T , U ]): Try [U ]
58
-
59
- def exists (p : T => Boolean ): Boolean
60
-
61
98
/**
62
99
* Converts this to a `Failure` if the predicate is not satisfied.
63
100
*/
64
101
def filter (p : T => Boolean ): Try [T ]
65
102
66
103
/**
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.
73
106
*/
74
- def rescue [U >: T ](rescueException : PartialFunction [Throwable , Try [U ]]): Try [U ]
107
+ def rescue [U >: T ](f : PartialFunction [Throwable , Try [U ]]): Try [U ]
75
108
76
109
/**
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.
78
112
*/
79
- def recover [U >: T ](rescueException : PartialFunction [Throwable , U ]): Try [U ]
113
+ def recover [U >: T ](f : PartialFunction [Throwable , U ]): Try [U ]
80
114
81
115
/**
82
116
* Returns `None` if this is a `Failure` or a `Some` containing the value if this is a `Success`.
83
117
*/
84
118
def toOption = if (isSuccess) Some (get) else None
85
119
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
+ */
86
123
def toSeq = if (isSuccess) Seq (get) else Seq ()
87
124
88
125
/**
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`.
90
127
* Alias for `flatMap`.
91
128
*/
92
129
def andThen [U ](f : T => Try [U ]): Try [U ] = flatMap(f)
@@ -97,42 +134,76 @@ sealed abstract class Try[+T] {
97
134
*/
98
135
def flatten [U ](implicit ev : T <:< Try [U ]): Try [U ]
99
136
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
+ */
100
141
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
+
101
151
}
102
152
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
+ }
103
161
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)
112
172
}
113
173
}
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
114
182
def get : T = throw exception
115
183
def flatMap [U ](f : T => Try [U ]): Try [U ] = Failure [U ](exception)
116
184
def flatten [U ](implicit ev : T <:< Try [U ]): Try [U ] = Failure [U ](exception)
117
185
def foreach [U ](f : T => U ): Unit = {}
118
186
def map [U ](f : T => U ): Try [U ] = Failure [U ](exception)
119
- def collect [U ](pf : PartialFunction [T , U ]): Try [U ] = Failure [U ](exception)
120
187
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)
126
197
}
127
- def exists ( p : T => Boolean ) : Boolean = false
198
+ }
128
199
def failed : Try [Throwable ] = Success (exception)
129
200
}
130
201
131
202
132
- final class Success [+ T ](value : T ) extends Try [T ] {
203
+ final case class Success [+ T ](value : T ) extends Try [T ] {
133
204
def isFailure : Boolean = false
134
205
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)
136
207
def get = value
137
208
def flatMap [U ](f : T => Try [U ]): Try [U ] =
138
209
try f(value)
@@ -142,43 +213,14 @@ final class Success[+T](value: T) extends Try[T] {
142
213
def flatten [U ](implicit ev : T <:< Try [U ]): Try [U ] = value
143
214
def foreach [U ](f : T => U ): Unit = f(value)
144
215
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)
181
222
}
182
223
}
183
-
224
+ def recover [U >: T ](rescueException : PartialFunction [Throwable , U ]): Try [U ] = this
225
+ def failed : Try [Throwable ] = Failure (new UnsupportedOperationException (" Success.failed" ))
184
226
}
0 commit comments