## Handling Error Without Exceptions

### Functional programming in Scala

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. We use the same numeration for the exercises for you to follow them.

### The Option data type

Exercise 4.1:

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 {
case None => None
case Some(a) => Some(f(a))
}``````

Let's try it out:

``````def lookupByName(name: String): Option[Employee] =
name match {
case "Joe" => Some(Employee("Joe", "Finances", Some("Julie")))
case "Mary" => Some(Employee("Mary", "IT", None))
case "Izumi" => Some(Employee("Izumi", "IT", Some("Mary")))
case _ => None
}

/*
We can look for our employees, and try to obtain their departments. We will assume that we won't find any errors,
and if it's the case, we don't have to worry as the computation will end there. Try to use `map` on the result of
calling `lookupByName` to create a function to obtain the department of each employee. Hint: to access the
optional employee, use Scala's underscore notation. i.e.:

_.getOrElse(Employee("John", "Doe", None))

Employee is defined as:

case class Employee(name: String, department: String, manager: Option[String])
*/
def getDepartment: (Option[Employee]) => Option[String] = res0

getDepartment(lookupByName("Joe")) shouldBe Some("Finances")
getDepartment(lookupByName("Mary")) shouldBe Some("IT")
getDepartment(lookupByName("Foo")) shouldBe None``````

We can also implement `flatMap`, which applies a function `f` which may also fail, to the `Option` if not `None`:

``def flatMap[B](f: A => Option[B]): Option[B] = map(f) getOrElse None``

Try to find out who is managing each employee, if applicable:

``````def getManager: (Option[Employee]) => Option[String] = res0

getManager(lookupByName("Joe")) shouldBe Some("Julie")
getManager(lookupByName("Mary")) shouldBe None
getManager(lookupByName("Foo")) shouldBe None``````

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 {
case None => default
case Some(a) => a
}``````

`orElse` returns the original `Option` if not `None`, or returns the provided `Option` as an alternative in that case:

``def orElse[B >: A](ob: => Option[B]): Option[B] = this map (Some(_)) getOrElse ob``

Check how it works in the following exercise:

``````def getManager(employee: Option[Employee]): Option[String] = employee.flatMap(_.manager)

getManager(lookupByName("Joe")).orElse(Some("Mr. CEO")) shouldBe res0
getManager(lookupByName("Mary")).orElse(Some("Mr. CEO")) shouldBe res1
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 provided predicate:

``````def filter(f: A => Boolean): Option[A] = this match {
case Some(a) if f(a) => this
case _ => None
}``````

Test it out by discarding those employees who belong to the IT department:

``````lookupByName("Joe").filter(_.department != "IT") shouldBe res0
lookupByName("Mary").filter(_.department != "IT") shouldBe res1
lookupByName("Foo").filter(_.department != "IT") shouldBe res2``````

Exercise 4.2:

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:

``````def variance(xs: Seq[Double]): Option[Double] =
mean(xs) flatMap (m => mean(xs.map(x => math.pow(x - m, 2))))``````

Exercise 4.3:

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)))``````

Exercise 4.4:

Let's continue by looking at a few other similar cases. For instance, the `sequence` function, which combines a list 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 {
case Nil => Some(Nil)
case h :: t => h flatMap (hh => sequence(t) map (hh :: _))
}``````

After taking a look at the implementation, see how it works in the following exercise:

``````sequence(List(Some(1), Some(2), Some(3))) shouldBe res0
sequence(List(Some(1), Some(2), None)) shouldBe res1``````

Exercise 4.5:

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`:

``````def traverse[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] = a match {
case Nil => Some(Nil)
case h :: t => map2(f(h), traverse(t)(f))(_ :: _)
}``````

We can also implement `traverse` in terms of `foldRight`:

``````def traverse_1[A, B](a: List[A])(f: A => Option[B]): Option[List[B]] =
a.foldRight[Option[List[B]]](Some(Nil))((h, t) => map2(f(h), t)(_ :: _))``````

We can even re-implement `sequence` in terms of `traverse`:

``def sequenceViaTraverse[A](a: List[Option[A]]): Option[List[A]] = traverse(a)(x => x)``

Let's try `traverse` out, by trying to parse a `List[String]` into a `Option[List[Int]]`:

``````val list1 = List("1", "2", "3")
val list2 = List("I", "II", "III", "IV")

def parseInt(a: String): Option[Int] =
Try(a.toInt) match {
case Success(r) => Some(r)
case _ => None
}

traverse(list1)(i => parseInt(i)) shouldBe res0
traverse(list2)(i => parseInt(i)) shouldBe res1``````

### The Either data type

Exercise 4.6:

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`:

``````def map[B](f: A => B): Either[E, B] = this match {
case Right(a) => Right(f(a))
case Left(e) => Left(e)
}``````

In the same fashion as `Option`, `map` allows us to chain operations on an `Either` without worrying about the possible errors that may arise, as the chain will stop if any error occurs. Let's try it out, by improving the employee lookup function we implemented before, to use `Either` instead of `Option`. Try to use `map` on the `Either` type to obtain the department of each employee:

``````def lookupByNameViaEither(name: String): Either[String, Employee] =
name match {
case "Joe" => Right(Employee("Joe", "Finances", Some("Julie")))
case "Mary" => Right(Employee("Mary", "IT", None))
case "Izumi" => Right(Employee("Izumi", "IT", Some("Mary")))
}

def getDepartment: (Either[String, Employee]) => Either[String, String] = res0

getDepartment(lookupByNameViaEither("Joe")) shouldBe Right("Finances")
getDepartment(lookupByNameViaEither("Mary")) shouldBe Right("IT")

`flatMap` behaves the same in `Either` as it does in `Option`, allowing us to chain operations that may also fail. Use it to try to obtain the managers from each employee. Note that when calling our `getManager` function, we can find two different errors in its execution:

``````def getManager(employee: Either[String, Employee]): Either[String, String] =
employee.flatMap(e =>
e.manager match {
case Some(e) => Right(e)
})

getManager(lookupByNameViaEither("Joe")) shouldBe res0
getManager(lookupByNameViaEither("Mary")) shouldBe res1
getManager(lookupByNameViaEither("Foo")) shouldBe res2``````

`orElse` works the same as in `Option`s, returning the original `Either` when it contains a `Right`, or the provided alternative in case it's a `Left`:

``````def orElse[EE >: E, AA >: A](b: => Either[EE, AA]): Either[EE, AA] = this match {
case Left(_) => b
case Right(a) => Right(a)
}``````

Let's check out how it behaves. Let's assume that everyone inside our company ends up responding to a "Mr. CEO" manager. We can provide that logic with `orElse`:

``````def getManager(employee: Either[String, Employee]): Either[String, String] =
employee.flatMap(e =>
e.manager match {
case Some(e) => Right(e)
})

getManager(lookupByNameViaEither("Joe")).orElse(Right("Mr. CEO")) shouldBe res0
getManager(lookupByNameViaEither("Mary")).orElse(Right("Mr. CEO")) shouldBe res1
getManager(lookupByNameViaEither("Foo")).orElse(Right("Mr. CEO")) shouldBe res2``````

In the same fashion as with `Option`s, `map2` lets us combine two `Either`s using a binary function. Note that we will use for-comprehensions instead of a chain of `flatMap` and `map` calls:

``````def map2[EE >: E, B, C](b: Either[EE, B])(f: (A, B) => C): Either[EE, C] =
for {
a <- this;
b1 <- b
} yield f(a, b1)``````

In this implementation, we can't report errors on both sides. To do that, we would need a new data type that can hold a list of errors:

``````trait Partial[+A, +B]
case class Errors[+A](get: Seq[A]) extends Partial[A, Nothing]
case class Success[+B](get: B) extends Partial[Nothing, B]``````

This data type is really similar to Scalaz' `Validation` type.

In any case, let's test `map2` on the following exercise, to find out if two employees share a department by using an specific function:

``````def employeesShareDepartment(employeeA: Employee, employeeB: Employee) =
employeeA.department == employeeB.department

lookupByNameViaEither("Joe").map2(lookupByNameViaEither("Mary"))(
employeesShareDepartment) shouldBe res0
lookupByNameViaEither("Mary").map2(lookupByNameViaEither("Izumi"))(
employeesShareDepartment) shouldBe res1
lookupByNameViaEither("Foo").map2(lookupByNameViaEither("Izumi"))(
employeesShareDepartment) shouldBe res2``````

Exercise 4.7:

`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 {
case Nil => Right(Nil)
case h :: t => (f(h) map2 traverse(t)(f))(_ :: _)
}``````
``def sequence[E, A](es: List[Either[E, A]]): Either[E, List[A]] = traverse(es)(x => x)``

We can attempt to obtain a record of employees names by looking up a list of `Employees`:

``````val employees = List("Joe", "Mary")
val employeesAndOutsources = employees :+ "Foo"

Either.traverse(employees)(lookupByNameViaEither) shouldBe res0
Either.traverse(employeesAndOutsources)(lookupByNameViaEither) shouldBe res1``````

As for `sequence`, we can create a `List` of the employees we looked up by using the `lookupByNameViaEither`, and find out if we were looking for a missing person:

``````val employees = List(lookupByNameViaEither("Joe"), lookupByNameViaEither("Mary"))
val employeesAndOutsources = employees :+ lookupByNameViaEither("Foo")

Either.sequence(employees) shouldBe res0
Either.sequence(employeesAndOutsources) shouldBe res1``````