Today we look at certain kinds of computations from a truly functional perspective:

• computations that might or might not have a value
• computations that might fail
• computations that will finish at some point

But we won't get into math and category theory for this.
We'll see what a monad is from a programming perspective, directly in the Scala language.

You can find today's contents here.

## Practice

Let's implement some of our own monads.

1. Let's write a monad equivalent with `Option`. Let's call this `Maybe`, as in Haskell.
• create a trait `Maybe[+A]` (remember the `[A]` is equivalent to Java's `<A>` syntax for generics and ignore the + for now)
• add some functional primitives as methods to `Maybe`:
• the monad requirements: `map`, `flatMap`, `filter`
• `zipWith`, which receives a `Maybe[B]` and returns a `Maybe[(A,B)]`
The signature is just a tiny bit more complex:
`def zipWith[B](other: Maybe[B]): Maybe[(A,B)]`

If either `this` or `other` is empty, the result will be an empty `Maybe`.

• `exists`, which receives a predicate (a function of A to Boolean) and returns true if this instance contains an element which satisfies the predicate
• `orElse`, a special method for `Maybe`, which returns this instance if it's not empty, or an alternative otherwise; the signature will be
`def orElse[B >: A](alternative: Maybe[B]): Maybe[B]`

(ignore the `>:` for now)
How can you delay the computation of the alternative until you actually need it? (hint: call by value/call by name)

• add some utility methods to the `Maybe` trait:
• `isEmpty`
• `toList`
• `size`
• create two case subclasses: `Just[A]` and `Empty` and implement the methods
• what is `Empty`, actually? a case class? a case object?
• methods should be quick to write and probably take two lines of code, at most
2. An equivalent to Scala's `Try` monad, call this `Attempt[A]`.
• as before, create a trait `Attempt[A]` and two case subclasses, `Success[A]` and `Fail`
• `Success` has a member of type `A`, Fail has a member of type `Throwable`
• add some functional primitives to your `Attempt` and implement them in the subclasses:
• `map`, `flatMap`, `filter`
• `zipWith`
• `exists`
• `isEmpty`, `toList`, `size`
• a special method - `recoverWith`; the method returns an alternative `Attempt` computation if this one fails
3. Talk about the Future[T] (pun intended) if you finish early! 