summaryrefslogtreecommitdiff
path: root/src/library/scala/util/Try.scala
blob: 5dc5ede1ccbb681b8b52e986daadbcc632991109 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2008-2011, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */



package scala.util

/**
 * 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. 
 *
 */
sealed abstract class Try[+T] {
  /**
   * Returns true if the Try is a Failure, false otherwise.
   */
  def isFailure: Boolean

  /**
   * 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.
   */
  def getOrElse[U >: T](default: => U) = if (isSuccess) apply() else default

  /**
   * Returns the value from this Success or throws the exception if this is a Failure
   */
  def apply(): T

  /**
   * Returns the value from this Success or throws the exception if this is a Failure.
   * Alias for apply()
   */
  def get = apply()

  /**
   * Applies the given function f if this is a Result.
   */
  def foreach[U](f: T => U) { onSuccess(f) }

  /**
   * Returns the given function applied to the value from this Success or returns this if this is a Failure.
   *
   */
  def flatMap[U](f: T => Try[U]): Try[U]

  /**
   * Maps the given function to the value from this Success or returns this if this is a Failure
   */
  def map[U](f: T => U): Try[U]

  /**
   * Converts this to a Failure if the predicate is not satisfied.
   */
  def filter(p: T => Boolean): Try[T]

  /**
   * Calls the exceptionHandler with the exception if this is a Failure. This is like flatMap for the exception.
   */
  def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U]

  /**
   * Calls the exceptionHandler with the exception if this is a Failure. This is like map for the exception.
   */
  def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U]

  /**
   * Invoked only if the computation was successful.  
   */
  def onSuccess[U](f: T => U): Try[T]

  /**
   * Invoked only if the computation failed.  
   */
  def onFailure[U](rescueException: Throwable => U): Try[T]

  /**
   * Invoked regardless of whether the computation completed
   * successfully or unsuccessfully.  Implemented in terms of
   * `respond` so that subclasses control evaluation order.  Returns a
   * chained `this` as in `respond`.
   */
  def ensure[U](f: => U): Try[T] =
    respond { _ => f }

  /**
   * Returns None if this is a Failure or a Some containing the value if this is a Success
   */
  def toOption = if (isSuccess) Some(apply()) else None

  /**
   * Invokes the given closure when the value is available.  Returns
   * another 'This[R]' that is guaranteed to be available only *after*
   * 'k' has run.  This enables the enforcement of invocation ordering.
   *
   * This is overridden by subclasses.
   */
  def respond[U](k: Try[T] => U): Try[T] = { 
    k(this)
    this
  }

  /**
   * Invokes the given transformation when the value is available,
   * returning the transformed value. This method is like a combination
   * of flatMap and rescue. This method is typically used for more
   * imperative control-flow than flatMap/rescue which often exploits
   * the Null Object Pattern.
   *
   * This is overridden by subclasses.
   */
  def transform[U](f: Try[T] => Try[U]): Try[U] =
    f(this)

  /**
   * 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]) = flatMap(f)

  /**
   * Transforms a nested Try, i.e., a Try of type `Try[Try[T]]`, 
   * into an un-nested Try, i.e., a Try of type `Try[T]`
   */
  def flatten[U](implicit ev: T <:< Try[U]): Try[U]
}

final case class Failure[+T](e: Throwable) extends Try[T] {
  def isFailure = true
  def isSuccess = false
  def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = {
    try {
      if (rescueException.isDefinedAt(e)) rescueException(e) else this
    } catch {
      case e2 => Failure(e2)
    }   
  }
  def apply(): T = throw e
  def flatMap[U](f: T => Try[U]): Try[U] = Failure[U](e)
  def flatten[U](implicit ev: T <:< Try[U]): Try[U] = Failure[U](e)
  def map[U](f: T => U): Try[U] = Failure[U](e)
  def filter(p: T => Boolean): Try[T] = this
  def onFailure[U](rescueException: Throwable => U): Try[T] = {
    rescueException(e)
    this
  }
  def onSuccess[U](f: T => U): Try[T] = this
  def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = 
    if (rescueException.isDefinedAt(e)) {
      Try(rescueException(e))
    } else {
      this
    }
}

final case class Success[+T](r: T) extends Try[T] {
  def isFailure = false
  def isSuccess = true
  def rescue[U >: T](rescueException: PartialFunction[Throwable, Try[U]]): Try[U] = Success(r)
  def apply() = r
  def flatMap[U](f: T => Try[U]): Try[U] = 
    try f(r) 
    catch { 
      case e => Failure(e) 
    }
  def flatten[U](implicit ev: T <:< Try[U]): Try[U] = r
  def map[U](f: T => U): Try[U] = Try[U](f(r))
  def filter(p: T => Boolean): Try[T] = 
    if (p(apply())) this 
    else Failure(new NoSuchElementException("Predicate does not hold"))
  def onFailure[U](rescueException: Throwable => U): Try[T] = this  
  def onSuccess[U](f: T => U): Try[T] = {
    f(r)
    this
  }
  def handle[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this
}

object Try {

  def apply[T](r: => T): Try[T] = {
    try { Success(r) } catch {
      case e => Failure(e)
    }
  }

}