-
Notifications
You must be signed in to change notification settings - Fork 3.1k
/
Option.scala
348 lines (314 loc) · 12.1 KB
/
Option.scala
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/* __ *\
** ________ ___ / / ___ Scala API **
** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL **
** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
** /____/\___/_/ |_/____/_/ | | **
** |/ **
\* */
package scala
object Option {
import scala.language.implicitConversions
/** An implicit conversion that converts an option to an iterable value
*/
implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList
/** An Option factory which creates Some(x) if the argument is not null,
* and None if it is null.
*
* @param x the value
* @return Some(value) if value != null, None if value == null
*/
def apply[A](x: A): Option[A] = if (x == null) None else Some(x)
/** An Option factory which returns `None` in a manner consistent with
* the collections hierarchy.
*/
def empty[A] : Option[A] = None
}
/** Represents optional values. Instances of `Option`
* are either an instance of $some or the object $none.
*
* The most idiomatic way to use an $option instance is to treat it
* as a collection or monad and use `map`,`flatMap`, `filter`, or
* `foreach`:
*
* {{{
* val name: Option[String] = request getParameter "name"
* val upper = name map { _.trim } filter { _.length != 0 } map { _.toUpperCase }
* println(upper getOrElse "")
* }}}
*
* Note that this is equivalent to {{{
* val upper = for {
* name <- request getParameter "name"
* trimmed <- Some(name.trim)
* upper <- Some(trimmed.toUpperCase) if trimmed.length != 0
* } yield upper
* println(upper getOrElse "")
* }}}
*
* Because of how for comprehension works, if $none is returned
* from `request.getParameter`, the entire expression results in
* $none
*
* This allows for sophisticated chaining of $option values without
* having to check for the existence of a value.
*
* A less-idiomatic way to use $option values is via pattern matching: {{{
* val nameMaybe = request getParameter "name"
* nameMaybe match {
* case Some(name) =>
* println(name.trim.toUppercase)
* case None =>
* println("No name value")
* }
* }}}
*
* @note Many of the methods in here are duplicative with those
* in the Traversable hierarchy, but they are duplicated for a reason:
* the implicit conversion tends to leave one with an Iterable in
* situations where one could have retained an Option.
*
* @author Martin Odersky
* @author Matthias Zenger
* @version 1.1, 16/01/2007
* @define none `None`
* @define some [[scala.Some]]
* @define option [[scala.Option]]
* @define p `p`
* @define f `f`
* @define coll option
* @define Coll `Option`
* @define orderDependent
* @define orderDependentFold
* @define mayNotTerminateInf
* @define willNotTerminateInf
* @define collectExample
* @define undefinedorder
* @define thatinfo the class of the returned collection. In the standard library configuration, `That` is `Iterable[B]`
* @define bfinfo an implicit value of class `CanBuildFrom` which determines the result class `That` from the current
* representation type `Repr` and the new element type `B`.
*/
@SerialVersionUID(-114498752079829388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
sealed abstract class Option[+A] extends Product with Serializable {
self =>
/** Returns true if the option is $none, false otherwise.
*/
def isEmpty: Boolean
/** Returns true if the option is an instance of $some, false otherwise.
*/
def isDefined: Boolean = !isEmpty
/** Returns the option's value.
* @note The option must be nonEmpty.
* @throws java.util.NoSuchElementException if the option is empty.
*/
def get: A
/** Returns the option's value if the option is nonempty, otherwise
* return the result of evaluating `default`.
*
* @param default the default expression.
*/
@inline final def getOrElse[B >: A](default: => B): B =
if (isEmpty) default else this.get
/** Returns the option's value if it is nonempty,
* or `null` if it is empty.
* Although the use of null is discouraged, code written to use
* $option must often interface with code that expects and returns nulls.
* @example {{{
* val initialText: Option[String] = getInitialText
* val textField = new JComponent(initialText.orNull,20)
* }}}
*/
@inline final def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse ev(null)
/** Returns a $some containing the result of applying $f to this $option's
* value if this $option is nonempty.
* Otherwise return $none.
*
* @note This is similar to `flatMap` except here,
* $f does not need to wrap its result in an $option.
*
* @param f the function to apply
* @see flatMap
* @see foreach
*/
@inline final def map[B](f: A => B): Option[B] =
if (isEmpty) None else Some(f(this.get))
/** Returns the result of applying $f to this $option's
* value if the $option is nonempty. Otherwise, evaluates
* expression `ifEmpty`.
*
* @note This is equivalent to `$option map f getOrElse ifEmpty`.
*
* @param ifEmpty the expression to evaluate if empty.
* @param f the function to apply if nonempty.
*/
@inline final def fold[B](ifEmpty: => B)(f: A => B): B =
if (isEmpty) ifEmpty else f(this.get)
/** Returns the result of applying $f to this $option's value if
* this $option is nonempty.
* Returns $none if this $option is empty.
* Slightly different from `map` in that $f is expected to
* return an $option (which could be $none).
*
* @param f the function to apply
* @see map
* @see foreach
*/
@inline final def flatMap[B](f: A => Option[B]): Option[B] =
if (isEmpty) None else f(this.get)
def flatten[B](implicit ev: A <:< Option[B]): Option[B] =
if (isEmpty) None else ev(this.get)
/** Returns this $option if it is nonempty '''and''' applying the predicate $p to
* this $option's value returns true. Otherwise, return $none.
*
* @param p the predicate used for testing.
*/
@inline final def filter(p: A => Boolean): Option[A] =
if (isEmpty || p(this.get)) this else None
/** Returns this $option if it is nonempty '''and''' applying the predicate $p to
* this $option's value returns false. Otherwise, return $none.
*
* @param p the predicate used for testing.
*/
@inline final def filterNot(p: A => Boolean): Option[A] =
if (isEmpty || !p(this.get)) this else None
/** Returns false if the option is $none, true otherwise.
* @note Implemented here to avoid the implicit conversion to Iterable.
*/
final def nonEmpty = isDefined
/** Necessary to keep $option from being implicitly converted to
* [[scala.collection.Iterable]] in `for` comprehensions.
*/
@inline final def withFilter(p: A => Boolean): WithFilter = new WithFilter(p)
/** We need a whole WithFilter class to honor the "doesn't create a new
* collection" contract even though it seems unlikely to matter much in a
* collection with max size 1.
*/
class WithFilter(p: A => Boolean) {
def map[B](f: A => B): Option[B] = self filter p map f
def flatMap[B](f: A => Option[B]): Option[B] = self filter p flatMap f
def foreach[U](f: A => U): Unit = self filter p foreach f
def withFilter(q: A => Boolean): WithFilter = new WithFilter(x => p(x) && q(x))
}
/** Tests whether the option contains a given value as an element.
*
* @example {{{
* // Returns true because Some instance contains string "something" which equals "something".
* Some("something") contains "something"
*
* // Returns false because "something" != "anything".
* Some("something") contains "anything"
*
* // Returns false when method called on None.
* None contains "anything"
* }}}
*
* @param elem the element to test.
* @return `true` if the option has an element that is equal (as
* determined by `==`) to `elem`, `false` otherwise.
*/
final def contains[A1 >: A](elem: A1): Boolean =
!isEmpty && this.get == elem
/** Returns true if this option is nonempty '''and''' the predicate
* $p returns true when applied to this $option's value.
* Otherwise, returns false.
*
* @param p the predicate to test
*/
@inline final def exists(p: A => Boolean): Boolean =
!isEmpty && p(this.get)
/** Returns true if this option is empty '''or''' the predicate
* $p returns true when applied to this $option's value.
*
* @param p the predicate to test
*/
@inline final def forall(p: A => Boolean): Boolean = isEmpty || p(this.get)
/** Apply the given procedure $f to the option's value,
* if it is nonempty. Otherwise, do nothing.
*
* @param f the procedure to apply.
* @see map
* @see flatMap
*/
@inline final def foreach[U](f: A => U) {
if (!isEmpty) f(this.get)
}
/** Returns a $some containing the result of
* applying `pf` to this $option's contained
* value, '''if''' this option is
* nonempty '''and''' `pf` is defined for that value.
* Returns $none otherwise.
*
* @example {{{
* // Returns Some(HTTP) because the partial function covers the case.
* Some("http") collect {case "http" => "HTTP"}
*
* // Returns None because the partial function doesn't cover the case.
* Some("ftp") collect {case "http" => "HTTP"}
*
* // Returns None because the option is empty. There is no value to pass to the partial function.
* None collect {case value => value}
* }}}
*
* @param pf the partial function.
* @return the result of applying `pf` to this $option's
* value (if possible), or $none.
*/
@inline final def collect[B](pf: PartialFunction[A, B]): Option[B] =
if (!isEmpty) pf.lift(this.get) else None
/** Returns this $option if it is nonempty,
* otherwise return the result of evaluating `alternative`.
* @param alternative the alternative expression.
*/
@inline final def orElse[B >: A](alternative: => Option[B]): Option[B] =
if (isEmpty) alternative else this
/** Returns a singleton iterator returning the $option's value
* if it is nonempty, or an empty iterator if the option is empty.
*/
def iterator: Iterator[A] =
if (isEmpty) collection.Iterator.empty else collection.Iterator.single(this.get)
/** Returns a singleton list containing the $option's value
* if it is nonempty, or the empty list if the $option is empty.
*/
def toList: List[A] =
if (isEmpty) List() else new ::(this.get, Nil)
/** Returns a [[scala.util.Left]] containing the given
* argument `left` if this $option is empty, or
* a [[scala.util.Right]] containing this $option's value if
* this is nonempty.
*
* @param left the expression to evaluate and return if this is empty
* @see toLeft
*/
@inline final def toRight[X](left: => X) =
if (isEmpty) Left(left) else Right(this.get)
/** Returns a [[scala.util.Right]] containing the given
* argument `right` if this is empty, or
* a [[scala.util.Left]] containing this $option's value
* if this $option is nonempty.
*
* @param right the expression to evaluate and return if this is empty
* @see toRight
*/
@inline final def toLeft[X](right: => X) =
if (isEmpty) Right(right) else Left(this.get)
}
/** Class `Some[A]` represents existing values of type
* `A`.
*
* @author Martin Odersky
* @version 1.0, 16/07/2003
*/
@SerialVersionUID(1234815782226070388L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
final case class Some[+A](x: A) extends Option[A] {
def isEmpty = false
def get = x
}
/** This case object represents non-existent values.
*
* @author Martin Odersky
* @version 1.0, 16/07/2003
*/
@SerialVersionUID(5066590221178148012L) // value computed by serialver for 2.11.2, annotation added in 2.11.4
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
}