summaryrefslogtreecommitdiff
path: root/src/library/scala/util/Try.scala
blob: c9bde813170f48540ba9edf961684a48a0bd3c8e (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
/*                     __                                               *\
**     ________ ___   / /  ___     Scala API                            **
**    / __/ __// _ | / /  / _ |    (c) 2008-2011, LAMP/EPFL             **
**  __\ \/ /__/ __ |/ /__/ __ |    http://scala-lang.org/               **
** /____/\___/_/ |_/____/_/ | |                                         **
**                          |/                                          **
\*                                                                      */

package scala.util



import collection.Seq



/**
 * 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) get 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.
   */
  def foreach[U](f: T => U): Unit

  /**
   * 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]

  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.
   */
  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 recover[U >: T](rescueException: 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

  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`.
   * Alias for `flatMap`.
   */
  def andThen[U](f: T => Try[U]): 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]

  def failed: Try[Throwable]
}


final case class Failure[+T](val exception: 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(exception)) rescueException(exception) else this
    } catch {
      case e2 => Failure(e2)
    }
  }
  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 exists(p: T => Boolean): Boolean = false
  def failed: Try[Throwable] = Success(exception)
}


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 get = 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 foreach[U](f: T => U): Unit = f(r)
  def map[U](f: T => U): Try[U] = Try[U](f(r))
  def collect[U](pf: PartialFunction[T, U]): Try[U] =
    if (pf isDefinedAt r) Success(pf(r))
    else Failure[U](new NoSuchElementException("Partial function not defined at " + r))
  def filter(p: T => Boolean): Try[T] =
    if (p(r)) this
    else Failure(new NoSuchElementException("Predicate does not hold for " + r))
  def recover[U >: T](rescueException: PartialFunction[Throwable, U]): Try[U] = this
  def exists(p: T => Boolean): Boolean = p(r)
  def failed: Try[Throwable] = Failure(new UnsupportedOperationException("Success.failed"))
}


object Try {

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

}