Skip to content

Commit 0db65ea

Browse files
janekdblrytz
authored andcommitted
Add examples to Exception object and group members (scala#5111)
- Extend main comment with additional examples - Group methods from a user perspective - List exceptions special cased by shouldRethrow - Include overlooked withTry in opt, either notes.
1 parent 4c4c5e6 commit 0db65ea

File tree

1 file changed

+165
-25
lines changed

1 file changed

+165
-25
lines changed

src/library/scala/util/control/Exception.scala

Lines changed: 165 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,136 @@ package control
1313
import scala.reflect.{ ClassTag, classTag }
1414
import scala.language.implicitConversions
1515

16-
1716
/** Classes representing the components of exception handling.
18-
* Each class is independently composable. Some example usages:
17+
*
18+
* Each class is independently composable.
19+
*
20+
* This class differs from [[scala.util.Try]] in that it focuses on composing exception handlers rather than
21+
* composing behavior. All behavior should be composed first and fed to a [[Catch]] object using one of the
22+
* `opt`, `either` or `withTry` methods. Taken together the classes provide a DSL for composing catch and finally
23+
* behaviors.
24+
*
25+
* === Examples ===
26+
*
27+
* Create a `Catch` which handles specified exceptions.
1928
* {{{
2029
* import scala.util.control.Exception._
2130
* import java.net._
2231
*
2332
* val s = "http://www.scala-lang.org/"
24-
* val x1 = catching(classOf[MalformedURLException]) opt new URL(s)
25-
* val x2 = catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s)
33+
*
34+
* // Some(http://www.scala-lang.org/)
35+
* val x1: Option[URL] = catching(classOf[MalformedURLException]) opt new URL(s)
36+
*
37+
* // Right(http://www.scala-lang.org/)
38+
* val x2: Either[Throwable,URL] =
39+
* catching(classOf[MalformedURLException], classOf[NullPointerException]) either new URL(s)
40+
*
41+
* // Success(http://www.scala-lang.org/)
42+
* val x3: Try[URL] = catching(classOf[MalformedURLException], classOf[NullPointerException]) withTry new URL(s)
43+
*
44+
* val defaultUrl = new URL("http://example.com")
45+
* // URL(http://example.com) because htt/xx throws MalformedURLException
46+
* val x4: URL = failAsValue(classOf[MalformedURLException])(defaultUrl)(new URL("htt/xx"))
47+
* }}}
48+
*
49+
* Create a `Catch` which logs exceptions using `handling` and `by`.
50+
* {{{
51+
* def log(t: Throwable): Unit = t.printStackTrace
52+
*
53+
* val withThrowableLogging: Catch[Unit] = handling(classOf[MalformedURLException]) by (log)
54+
*
55+
* def printUrl(url: String) : Unit = {
56+
* val con = new URL(url) openConnection()
57+
* val source = scala.io.Source.fromInputStream(con.getInputStream())
58+
* source.getLines.foreach(println)
59+
* }
60+
*
61+
* val badUrl = "htt/xx"
62+
* // Prints stacktrace,
63+
* // java.net.MalformedURLException: no protocol: htt/xx
64+
* // at java.net.URL.<init>(URL.java:586)
65+
* withThrowableLogging { printUrl(badUrl) }
66+
*
67+
* val goodUrl = "http://www.scala-lang.org/"
68+
* // Prints page content,
69+
* // &lt;!DOCTYPE html&gt;
70+
* // &lt;html&gt;
71+
* withThrowableLogging { printUrl(goodUrl) }
72+
* }}}
73+
*
74+
* Use `unwrapping` to create a `Catch` that unwraps exceptions before rethrowing.
75+
* {{{
76+
* class AppException(cause: Throwable) extends RuntimeException(cause)
77+
*
78+
* val unwrappingCatch: Catch[Nothing] = unwrapping(classOf[AppException])
79+
*
80+
* def calcResult: Int = throw new AppException(new NullPointerException)
81+
*
82+
* // Throws NPE not AppException,
83+
* // java.lang.NullPointerException
84+
* // at .calcResult(&lt;console&gt;:17)
85+
* val result = unwrappingCatch(calcResult)
2686
* }}}
2787
*
28-
* This class differs from `scala.util.Try` in that it focuses on composing exception handlers rather than
29-
* composing behavior. All behavior should be composed first and fed to a `Catch` object using one of the
30-
* `opt` or `either` methods.
88+
* Use `failAsValue` to provide a default when a specified exception is caught.
89+
*
90+
* {{{
91+
* val inputDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0)
92+
* val candidatePick = "seven" // scala.io.StdIn.readLine()
93+
*
94+
* // Int = 0
95+
* val pick = inputDefaulting(candidatePick.toInt)
96+
* }}}
97+
*
98+
* Compose multiple `Catch`s with `or` to build a `Catch` that provides default values varied by exception.
99+
* {{{
100+
* val formatDefaulting: Catch[Int] = failAsValue(classOf[NumberFormatException])(0)
101+
* val nullDefaulting: Catch[Int] = failAsValue(classOf[NullPointerException])(-1)
102+
* val otherDefaulting: Catch[Int] = nonFatalCatch withApply(_ => -100)
103+
*
104+
* val combinedDefaulting: Catch[Int] = formatDefaulting or nullDefaulting or otherDefaulting
105+
*
106+
* def p(s: String): Int = s.length * s.toInt
107+
*
108+
* // Int = 0
109+
* combinedDefaulting(p("tenty-nine"))
110+
*
111+
* // Int = -1
112+
* combinedDefaulting(p(null: String))
113+
*
114+
* // Int = -100
115+
* combinedDefaulting(throw new IllegalStateException)
116+
*
117+
* // Int = 22
118+
* combinedDefaulting(p("11"))
119+
* }}}
120+
*
121+
* @groupname composition-catch Catch behavior composition
122+
* @groupprio composition-catch 10
123+
* @groupdesc composition-catch Build Catch objects from exception lists and catch logic
124+
*
125+
* @groupname composition-finally Finally behavior composition
126+
* @groupprio composition-finally 20
127+
* @groupdesc composition-finally Build Catch objects from finally logic
128+
*
129+
* @groupname canned-behavior General purpose catch objects
130+
* @groupprio canned-behavior 30
131+
* @groupdesc canned-behavior Catch objects with predefined behavior. Use combinator methods to compose additional behavior.
132+
*
133+
* @groupname dsl DSL behavior composition
134+
* @groupprio dsl 40
135+
* @groupdesc dsl Expressive Catch behavior composition
136+
*
137+
* @groupname composition-catch-promiscuously Promiscuous Catch behaviors
138+
* @groupprio composition-catch-promiscuously 50
139+
* @groupdesc composition-catch-promiscuously Useful if catching `ControlThrowable` or `InterruptedException` is required.
140+
*
141+
* @groupname logic-container Logic Containers
142+
* @groupprio logic-container 60
143+
* @groupdesc logic-container Containers for catch and finally behavior.
144+
*
145+
* @define protectedExceptions `ControlThrowable` or `InterruptedException`
31146
*
32147
* @author Paul Phillips
33148
*/
@@ -51,6 +166,7 @@ object Exception {
51166

52167
/** !!! Not at all sure of every factor which goes into this,
53168
* and/or whether we need multiple standard variations.
169+
* @return true if `x` is $protectedExceptions otherwise false.
54170
*/
55171
def shouldRethrow(x: Throwable): Boolean = x match {
56172
case _: ControlThrowable => true
@@ -70,7 +186,9 @@ object Exception {
70186
override def toString() = name + "(" + desc + ")"
71187
}
72188

73-
/** A container class for finally code. */
189+
/** A container class for finally code.
190+
* @group logic-container
191+
*/
74192
class Finally private[Exception](body: => Unit) extends Described {
75193
protected val name = "Finally"
76194

@@ -87,6 +205,7 @@ object Exception {
87205
* @param pf Partial function used when applying catch logic to determine result value
88206
* @param fin Finally logic which if defined will be invoked after catch logic
89207
* @param rethrow Predicate on throwables determining when to rethrow a caught [[Throwable]]
208+
* @group logic-container
90209
*/
91210
class Catch[+T](
92211
val pf: Catcher[T],
@@ -153,23 +272,30 @@ object Exception {
153272
final def nonFatalCatcher[T]: Catcher[T] = mkThrowableCatcher({ case NonFatal(_) => true; case _ => false }, throw _)
154273
final def allCatcher[T]: Catcher[T] = mkThrowableCatcher(_ => true, throw _)
155274

156-
/** The empty `Catch` object. */
275+
/** The empty `Catch` object.
276+
* @group canned-behavior
277+
**/
157278
final val noCatch: Catch[Nothing] = new Catch(nothingCatcher) withDesc "<nothing>"
158279

159-
/** A `Catch` object which catches everything. */
280+
/** A `Catch` object which catches everything.
281+
* @group canned-behavior
282+
**/
160283
final def allCatch[T]: Catch[T] = new Catch(allCatcher[T]) withDesc "<everything>"
161284

162-
/** A `Catch` object which catches non-fatal exceptions. */
285+
/** A `Catch` object which catches non-fatal exceptions.
286+
* @group canned-behavior
287+
**/
163288
final def nonFatalCatch[T]: Catch[T] = new Catch(nonFatalCatcher[T]) withDesc "<non-fatal>"
164289

165290
/** Creates a `Catch` object which will catch any of the supplied exceptions.
166291
* Since the returned `Catch` object has no specific logic defined and will simply
167-
* rethrow the exceptions it catches, you will typically want to call `opt` or
168-
* `either` on the return value, or assign custom logic by calling "withApply".
292+
* rethrow the exceptions it catches, you will typically want to call `opt`,
293+
* `either` or `withTry` on the return value, or assign custom logic by calling "withApply".
169294
*
170295
* Note that `Catch` objects automatically rethrow `ControlExceptions` and others
171296
* which should only be caught in exceptional circumstances. If you really want
172297
* to catch exactly what you specify, use `catchingPromiscuously` instead.
298+
* @group composition-catch
173299
*/
174300
def catching[T](exceptions: Class[_]*): Catch[T] =
175301
new Catch(pfFromExceptions(exceptions : _*)) withDesc (exceptions map (_.getName) mkString ", ")
@@ -178,42 +304,56 @@ object Exception {
178304

179305
/** Creates a `Catch` object which will catch any of the supplied exceptions.
180306
* Unlike "catching" which filters out those in shouldRethrow, this one will
181-
* catch whatever you ask of it: `ControlThrowable`, `InterruptedException`,
182-
* `OutOfMemoryError`, you name it.
307+
* catch whatever you ask of it including $protectedExceptions.
308+
* @group composition-catch-promiscuously
183309
*/
184310
def catchingPromiscuously[T](exceptions: Class[_]*): Catch[T] = catchingPromiscuously(pfFromExceptions(exceptions : _*))
185311
def catchingPromiscuously[T](c: Catcher[T]): Catch[T] = new Catch(c, None, _ => false)
186312

187-
/** Creates a `Catch` object which catches and ignores any of the supplied exceptions. */
313+
/** Creates a `Catch` object which catches and ignores any of the supplied exceptions.
314+
* @group composition-catch
315+
*/
188316
def ignoring(exceptions: Class[_]*): Catch[Unit] =
189317
catching(exceptions: _*) withApply (_ => ())
190318

191-
/** Creates a `Catch` object which maps all the supplied exceptions to `None`. */
319+
/** Creates a `Catch` object which maps all the supplied exceptions to `None`.
320+
* @group composition-catch
321+
*/
192322
def failing[T](exceptions: Class[_]*): Catch[Option[T]] =
193323
catching(exceptions: _*) withApply (_ => None)
194324

195-
/** Creates a `Catch` object which maps all the supplied exceptions to the given value. */
325+
/** Creates a `Catch` object which maps all the supplied exceptions to the given value.
326+
* @group composition-catch
327+
*/
196328
def failAsValue[T](exceptions: Class[_]*)(value: => T): Catch[T] =
197329
catching(exceptions: _*) withApply (_ => value)
198330

331+
class By[T,R](f: T => R) {
332+
def by(x: T): R = f(x)
333+
}
334+
199335
/** Returns a partially constructed `Catch` object, which you must give
200-
* an exception handler function as an argument to `by`. Example:
336+
* an exception handler function as an argument to `by`.
337+
* @example
201338
* {{{
202-
* handling(ex1, ex2) by (_.printStackTrace)
339+
* handling(classOf[MalformedURLException], classOf[NullPointerException]) by (_.printStackTrace)
203340
* }}}
341+
* @group dsl
204342
*/
205-
class By[T,R](f: T => R) {
206-
def by(x: T): R = f(x)
207-
}
343+
// TODO: Add return type
208344
def handling[T](exceptions: Class[_]*) = {
209345
def fun(f: Throwable => T) = catching(exceptions: _*) withApply f
210346
new By[Throwable => T, Catch[T]](fun _)
211347
}
212348

213-
/** Returns a `Catch` object with no catch logic and the argument as `Finally`. */
349+
/** Returns a `Catch` object with no catch logic and the argument as the finally logic.
350+
* @group composition-finally
351+
*/
214352
def ultimately[T](body: => Unit): Catch[T] = noCatch andFinally body
215353

216-
/** Creates a `Catch` object which unwraps any of the supplied exceptions. */
354+
/** Creates a `Catch` object which unwraps any of the supplied exceptions.
355+
* @group composition-catch
356+
*/
217357
def unwrapping[T](exceptions: Class[_]*): Catch[T] = {
218358
def unwrap(x: Throwable): Throwable =
219359
if (wouldMatch(x, exceptions) && x.getCause != null) unwrap(x.getCause)

0 commit comments

Comments
 (0)