Skip to content

Removed excerpts of text from the sections #19

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 15, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 29 additions & 106 deletions src/main/scala/fpinscalalib/ErrorHandlingSection.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,50 +15,17 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
*
* The following set of sections represent the exercises contained in the book "Functional Programming in Scala",
* written by Paul Chiusano and Rúnar Bjarnason and published by Manning. This content library is meant to be used
* in tandem with the book, although excerpts of the theory needed to complete them have been added to guide you.
* in tandem with the book. We use the same numeration for the exercises for you to follow them.
*
* For more information about "Functional Programming in Scala" please visit its
* <a href="https://www.manning.com/books/functional-programming-in-scala">official website</a>.
*
* = The Option data type =
*
* Exceptions break referential transparency and introduce context dependence. Moreover, they are not type-safe,
* hiding information about the fact that they may occur, to the developer and the compiler. We're going to explore
* an alternative to exceptions without these drawbacks, without losing out on the primary benefit of exceptions:
* they allow us to `consolidate and centralize error-handling logic`. The technique we use is based on an old idea:
* instead of throwing an exception, we return a value indicating that an exceptional condition has occurred. instead
* of using error codes, we introduce a new generic type for these “possibly defined values” and use higher-order
* functions to encapsulate common patterns of handling and propagating errors.
* <b>Exercise 4.1</b>:
*
* We're going to introduce a new type, `Option`. As with the previously explored `List`, this type also exists in
* the Scala standard library, but we're re-creating it here for pedagogical purposes:
*
* {{{
* sealed trait Option[+A]
* case class Some[+A](get: A) extends Option[A]
* case object None extends Option[Nothing]
* }}}
*
* Option has two cases: it can be defined, in which case it will be a `Some`, or it can be undefined, in which case
* it will be `None`.
*
* Let's consider an example on how we can use our new type. We're defining a function `mean` that computes the mean
* of a list, which is undefined if the list is empty:
*/

def optionMeanAssert(res0: Option[Double]): Unit = {
def mean(xs: Seq[Double]): Option[Double] =
if (xs.isEmpty) None
else Some(xs.sum / xs.length)

mean(Seq(1, 2, 3, 4, 5)) shouldBe Some(3.0)
mean(Seq.empty) shouldBe res0
}

/**
* `Option` can be thought of like a `List` that can contain at most one element, and many of the `List` functions we
* saw earlier have analogous functions on `Option`. We're going to look at some of these functions, starting by `map`,
* that applies a function `f` in the `Option` is not `None`:
* We're going to look at some of the functions available in the `Option`, starting by `map`, that applies a function
* `f` in the `Option` is not `None`:
*
* {{{
* def map[B](f: A => B): Option[B] = this match {
Expand Down Expand Up @@ -98,8 +65,7 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
* def flatMap[B](f: A => Option[B]): Option[B] = map(f) getOrElse None
* }}}
*
* By using `flatMap` we can chain operations that can also fail, as in the following example. Try to find out who is
* managing each employee, if applicable:
* Try to find out who is managing each employee, if applicable:
*/

def optionFlatMapAssert(res0: (Option[Employee]) => Option[String]): Unit = {
Expand All @@ -111,10 +77,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
}

/**
* The function `getOrElse` used above, tries to get the value contained in the Option, but if it's a `None`, it will
* return the default value provided by the caller. The `B >: A` in the declaration tells that the `B` type parameter
* must be a supertype of `A`. Furthermore, `default : => B` indicates that the argument is of type B, but won’t be
* evaluated until it’s needed by the function.
* The function `getOrElse` tries to get the value contained in the Option, but if it's a `None`, it will
* return the default value provided by the caller:
*
* {{{
* def getOrElse[B>:A](default: => B): B = this match {
Expand All @@ -129,6 +93,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
* {{{
* def orElse[B>:A](ob: => Option[B]): Option[B] = this map (Some(_)) getOrElse ob
* }}}
*
* Check how it works in the following exercise:
*/

def optionOrElseAssert(res0: Some[String], res1: Some[String], res2: Some[String]): Unit = {
Expand All @@ -139,7 +105,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
getManager(lookupByName("Foo")).orElse(Some("Mr. CEO")) shouldBe res2
}

/** Finally, we can implement a `filter` function that will turn any `Option` into a `None` if it doesn't satisfy the
/**
* Finally, we can implement a `filter` function that will turn any `Option` into a `None` if it doesn't satisfy the
* provided predicate:
*
* {{{
Expand All @@ -159,6 +126,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
}

/**
* <b>Exercise 4.2:</b>
*
* Let's implement the `variance` function in terms of `flatMap`. If the mean of a sequence is `m`, the variance
* is the mean of `math.pow(x - m, 2)` for each element in the sequence:
*
Expand All @@ -169,39 +138,22 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
*/

/**
* We may find in some situations when we need to combine two `Option` values using a binary function, so that if any
* of those values is `None`, the result value is too; and otherwise it will be the result of applying the provided
* function. We'll call this function `map2`, take a look at its implementation:
* <b>Exercise 4.3:</b>
*
* Let's write a generic function to combine two `Option` values , so that if any of those values is `None`, the
* result value is too; and otherwise it will be the result of applying the provided function:
*
* {{{
* def map2[A,B,C](a: Option[A], b: Option[B])(f: (A, B) => C): Option[C] =
* a flatMap (aa => b map (bb => f(aa, bb)))
* }}}
*
* Let's see an example of its use. Let's write a function `parseInsuranceRateQuote` which takes the age and a number
* of speeding tickets as strings, and attempts to call another function called `insuranceRateQuote` if parsing both
* values is valid:
*
* {{{
* def Try[A](a: => A): Option[A] =
* try Some(a)
* catch { case e: Exception => None }
*
* def parseInsuranceRateQuote( age: String, numberOfSpeedingTickets: String): Option[Double] = {
* val optAge: Option[Int] = Try { age.toInt }
* val optTickets: Option[Int] = Try { numberOfSpeedingTickets.toInt } map2(optAge, optTickes)(insuranceRateQuote)
* }
* }}}
* <b>Exercise 4.4:</b>
*
* As `Try` will return an `Option` containing the value of the operation it encapsulates (or a `None` if it returns
* an exception), to combine both values we need to make use of the new `map2` function we just implemented.
*/

/**
* Let's continue by looking at a few other similar cases. For instance, the `sequence` function, which combines a list
* of `Option`s into one `Option` containing a list of all the `Some` values in the original list. If the original
* list contains `None` even once, the result of the function should be `None`. Otherwise the result should be a `Some`
* with a list of all the values:
* of `Option`s into another `Option` containing a list of all the `Some`s in the original one. If the original
* list contains `None` at least once, the result of the function should be `None`. If not, the result should be a
* `Some` with a list of all the values:
*
* {{{
* def sequence(a: List[Option[A]]): Option[List[A]] = a match {
Expand All @@ -219,10 +171,10 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
}

/**
* <b>Exercise 4.5:</b>
*
* The last `Option` function we're going to explore is `traverse`, that will allow us to map over a list using a
* function that might fail, returning `None` if applying it to any element of the list returns `None`. Note that we
* want to avoid traversing the list twice (first to apply the provided function to each element, and another to
* combine these `Option` values into an optional `List`:
* function that might fail, returning `None` if applying it to any element of the list returns `None`:
*
* {{{
* def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match {
Expand Down Expand Up @@ -263,39 +215,8 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
/**
* = The Either data type =
*
* One thing you may have noticed with `Option` is that it doesn’t tell us anything about what went wrong in the case
* of an exceptional condition. All it can do is give us `None`, indicating that there’s no value to be had. But
* sometimes we want to know more. For example, we might want a `String` that gives more information, or if an
* exception was raised, we might want to know what that error actually was.
*
* In this section, we’ll walk through a simple extension to `Option`, the `Either` data type, which lets us track a
* reason for the failure. Let’s look at its definition:
*
* {{{
* sealed trait Either[+E, +A]
* case class Left[+E](value: E) extends Either[E, Nothing]
* case class Right[+A](value: A) extends Either[Nothing, A]
* }}}
*
* `Either` only has two cases, just like `Option`. The essential difference is that both cases carry a value. When
* we use it to indicate success or failure, by convention the `Right` constructor is reserved for the success case
* (a pun on “right,” meaning correct), and `Left` is used for failure.
* <b>Exercise 4.6:</b>
*
* Let's look at the `mean` example again, this time returning a `String` in case of failure:
*/

def eitherMeanAssert(res0: Right[Double], res1: Left[String]): Unit = {
def mean(xs: IndexedSeq[Double]): Either[String, Double] =
if (xs.isEmpty)
Left("mean of empty list!")
else
Right(xs.sum / xs.length)

mean(IndexedSeq(1.0, 2.0, 3.0, 4.0, 5.0)) shouldBe res0
mean(IndexedSeq.empty) shouldBe res1
}

/**
* As we did with `Option`, let's implement versions of `map`, `flatMap`, `orElse` and `map2` on `Either` that
* operate on the `Right` value, starting with `map`:
*
Expand Down Expand Up @@ -409,8 +330,10 @@ object ErrorHandlingSection extends FlatSpec with Matchers with org.scalaexercis
}

/**
* `sequence` and `traverse` can also be implemented for `Either`. These should return the first error that's
* encountered, if there is one.
* <b>Exercise 4.7:</b>
*
* `sequence` and `traverse` can also be implemented for `Either`. Those functions should return the first error that
* can be found, if there is one.
*
* {{{
* def traverse[E,A,B](es: List[A])(f: A => Either[E, B]): Either[E, List[B]] = es match {
Expand Down
Loading