@@ -15,50 +15,17 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
15
15
*
16
16
* The following set of sections represent the exercises contained in the book "Functional Programming in Scala",
17
17
* written by Paul Chiusano and Rúnar Bjarnason and published by Manning. This content library is meant to be used
18
- * in tandem with the book, although excerpts of the theory needed to complete them have been added to guide you .
18
+ * in tandem with the book. We use the same numeration for the exercises for you to follow them .
19
19
*
20
20
* For more information about "Functional Programming in Scala" please visit its
21
21
* <a href="https://www.manning.com/books/functional-programming-in-scala">official website</a>.
22
22
*
23
23
* = The Option data type =
24
24
*
25
- * Exceptions break referential transparency and introduce context dependence. Moreover, they are not type-safe,
26
- * hiding information about the fact that they may occur, to the developer and the compiler. We're going to explore
27
- * an alternative to exceptions without these drawbacks, without losing out on the primary benefit of exceptions:
28
- * they allow us to `consolidate and centralize error-handling logic`. The technique we use is based on an old idea:
29
- * instead of throwing an exception, we return a value indicating that an exceptional condition has occurred. instead
30
- * of using error codes, we introduce a new generic type for these “possibly defined values” and use higher-order
31
- * functions to encapsulate common patterns of handling and propagating errors.
25
+ * <b>Exercise 4.1</b>:
32
26
*
33
- * We're going to introduce a new type, `Option`. As with the previously explored `List`, this type also exists in
34
- * the Scala standard library, but we're re-creating it here for pedagogical purposes:
35
- *
36
- * {{{
37
- * sealed trait Option[+A]
38
- * case class Some[+A](get: A) extends Option[A]
39
- * case object None extends Option[Nothing]
40
- * }}}
41
- *
42
- * Option has two cases: it can be defined, in which case it will be a `Some`, or it can be undefined, in which case
43
- * it will be `None`.
44
- *
45
- * Let's consider an example on how we can use our new type. We're defining a function `mean` that computes the mean
46
- * of a list, which is undefined if the list is empty:
47
- */
48
-
49
- def optionMeanAssert (res0 : Option [Double ]): Unit = {
50
- def mean (xs : Seq [Double ]): Option [Double ] =
51
- if (xs.isEmpty) None
52
- else Some (xs.sum / xs.length)
53
-
54
- mean(Seq (1 , 2 , 3 , 4 , 5 )) shouldBe Some (3.0 )
55
- mean(Seq .empty) shouldBe res0
56
- }
57
-
58
- /**
59
- * `Option` can be thought of like a `List` that can contain at most one element, and many of the `List` functions we
60
- * saw earlier have analogous functions on `Option`. We're going to look at some of these functions, starting by `map`,
61
- * that applies a function `f` in the `Option` is not `None`:
27
+ * We're going to look at some of the functions available in the `Option`, starting by `map`, that applies a function
28
+ * `f` in the `Option` is not `None`:
62
29
*
63
30
* {{{
64
31
* def map[B](f: A => B): Option[B] = this match {
@@ -98,8 +65,7 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
98
65
* def flatMap[B](f: A => Option[B]): Option[B] = map(f) getOrElse None
99
66
* }}}
100
67
*
101
- * By using `flatMap` we can chain operations that can also fail, as in the following example. Try to find out who is
102
- * managing each employee, if applicable:
68
+ * Try to find out who is managing each employee, if applicable:
103
69
*/
104
70
105
71
def optionFlatMapAssert (res0 : (Option [Employee ]) => Option [String ]): Unit = {
@@ -111,10 +77,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
111
77
}
112
78
113
79
/**
114
- * The function `getOrElse` used above, tries to get the value contained in the Option, but if it's a `None`, it will
115
- * return the default value provided by the caller. The `B >: A` in the declaration tells that the `B` type parameter
116
- * must be a supertype of `A`. Furthermore, `default : => B` indicates that the argument is of type B, but won’t be
117
- * evaluated until it’s needed by the function.
80
+ * The function `getOrElse` tries to get the value contained in the Option, but if it's a `None`, it will
81
+ * return the default value provided by the caller:
118
82
*
119
83
* {{{
120
84
* def getOrElse[B>:A](default: => B): B = this match {
@@ -129,6 +93,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
129
93
* {{{
130
94
* def orElse[B>:A](ob: => Option[B]): Option[B] = this map (Some(_)) getOrElse ob
131
95
* }}}
96
+ *
97
+ * Check how it works in the following exercise:
132
98
*/
133
99
134
100
def optionOrElseAssert (res0 : Some [String ], res1 : Some [String ], res2 : Some [String ]): Unit = {
@@ -139,7 +105,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
139
105
getManager(lookupByName(" Foo" )).orElse(Some (" Mr. CEO" )) shouldBe res2
140
106
}
141
107
142
- /** Finally, we can implement a `filter` function that will turn any `Option` into a `None` if it doesn't satisfy the
108
+ /**
109
+ * Finally, we can implement a `filter` function that will turn any `Option` into a `None` if it doesn't satisfy the
143
110
* provided predicate:
144
111
*
145
112
* {{{
@@ -159,6 +126,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
159
126
}
160
127
161
128
/**
129
+ * <b>Exercise 4.2:</b>
130
+ *
162
131
* Let's implement the `variance` function in terms of `flatMap`. If the mean of a sequence is `m`, the variance
163
132
* is the mean of `math.pow(x - m, 2)` for each element in the sequence:
164
133
*
@@ -169,39 +138,22 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
169
138
*/
170
139
171
140
/**
172
- * We may find in some situations when we need to combine two `Option` values using a binary function, so that if any
173
- * of those values is `None`, the result value is too; and otherwise it will be the result of applying the provided
174
- * function. We'll call this function `map2`, take a look at its implementation:
141
+ * <b>Exercise 4.3:</b>
142
+ *
143
+ * Let's write a generic function to combine two `Option` values , so that if any of those values is `None`, the
144
+ * result value is too; and otherwise it will be the result of applying the provided function:
175
145
*
176
146
* {{{
177
147
* def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] =
178
148
* a flatMap (aa => b map (bb => f(aa, bb)))
179
149
* }}}
180
150
*
181
- * Let's see an example of its use. Let's write a function `parseInsuranceRateQuote` which takes the age and a number
182
- * of speeding tickets as strings, and attempts to call another function called `insuranceRateQuote` if parsing both
183
- * values is valid:
184
- *
185
- * {{{
186
- * def Try[A](a: => A): Option[A] =
187
- * try Some(a)
188
- * catch { case e: Exception => None }
189
- *
190
- * def parseInsuranceRateQuote( age: String, numberOfSpeedingTickets: String): Option[Double] = {
191
- * val optAge: Option[Int] = Try { age.toInt }
192
- * val optTickets: Option[Int] = Try { numberOfSpeedingTickets.toInt } map2(optAge, optTickes)(insuranceRateQuote)
193
- * }
194
- * }}}
151
+ * <b>Exercise 4.4:</b>
195
152
*
196
- * As `Try` will return an `Option` containing the value of the operation it encapsulates (or a `None` if it returns
197
- * an exception), to combine both values we need to make use of the new `map2` function we just implemented.
198
- */
199
-
200
- /**
201
153
* Let's continue by looking at a few other similar cases. For instance, the `sequence` function, which combines a list
202
- * of `Option`s into one `Option` containing a list of all the `Some` values in the original list . If the original
203
- * list contains `None` even once, the result of the function should be `None`. Otherwise the result should be a `Some`
204
- * with a list of all the values:
154
+ * of `Option`s into another `Option` containing a list of all the `Some`s in the original one . If the original
155
+ * list contains `None` at least once, the result of the function should be `None`. If not, the result should be a
156
+ * `Some` with a list of all the values:
205
157
*
206
158
* {{{
207
159
* def sequence(a: List[Option[A]]): Option[List[A]] = a match {
@@ -219,10 +171,10 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
219
171
}
220
172
221
173
/**
174
+ * <b>Exercise 4.5:</b>
175
+ *
222
176
* The last `Option` function we're going to explore is `traverse`, that will allow us to map over a list using a
223
- * function that might fail, returning `None` if applying it to any element of the list returns `None`. Note that we
224
- * want to avoid traversing the list twice (first to apply the provided function to each element, and another to
225
- * combine these `Option` values into an optional `List`:
177
+ * function that might fail, returning `None` if applying it to any element of the list returns `None`:
226
178
*
227
179
* {{{
228
180
* def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match {
@@ -263,39 +215,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
263
215
/**
264
216
* = The Either data type =
265
217
*
266
- * One thing you may have noticed with `Option` is that it doesn’t tell us anything about what went wrong in the case
267
- * of an exceptional condition. All it can do is give us `None`, indicating that there’s no value to be had. But
268
- * sometimes we want to know more. For example, we might want a `String` that gives more information, or if an
269
- * exception was raised, we might want to know what that error actually was.
270
- *
271
- * In this section, we’ll walk through a simple extension to `Option`, the `Either` data type, which lets us track a
272
- * reason for the failure. Let’s look at its definition:
273
- *
274
- * {{{
275
- * sealed trait Either[+E, +A]
276
- * case class Left[+E](value: E) extends Either[E, Nothing]
277
- * case class Right[+A](value: A) extends Either[Nothing, A]
278
- * }}}
279
- *
280
- * `Either` only has two cases, just like `Option`. The essential difference is that both cases carry a value. When
281
- * we use it to indicate success or failure, by convention the `Right` constructor is reserved for the success case
282
- * (a pun on “right,” meaning correct), and `Left` is used for failure.
218
+ * <b>Exercise 4.6:</b>
283
219
*
284
- * Let's look at the `mean` example again, this time returning a `String` in case of failure:
285
- */
286
-
287
- def eitherMeanAssert (res0 : Right [Double ], res1 : Left [String ]): Unit = {
288
- def mean (xs : IndexedSeq [Double ]): Either [String , Double ] =
289
- if (xs.isEmpty)
290
- Left (" mean of empty list!" )
291
- else
292
- Right (xs.sum / xs.length)
293
-
294
- mean(IndexedSeq (1.0 , 2.0 , 3.0 , 4.0 , 5.0 )) shouldBe res0
295
- mean(IndexedSeq .empty) shouldBe res1
296
- }
297
-
298
- /**
299
220
* As we did with `Option`, let's implement versions of `map`, `flatMap`, `orElse` and `map2` on `Either` that
300
221
* operate on the `Right` value, starting with `map`:
301
222
*
@@ -409,8 +330,10 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
409
330
}
410
331
411
332
/**
412
- * `sequence` and `traverse` can also be implemented for `Either`. These should return the first error that's
413
- * encountered, if there is one.
333
+ * <b>Exercise 4.7:</b>
334
+ *
335
+ * `sequence` and `traverse` can also be implemented for `Either`. Those functions should return the first error that
336
+ * can be found, if there is one.
414
337
*
415
338
* {{{
416
339
* def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = es match {
0 commit comments