#### 75 lines 2.9 KiB Scala Raw Permalink Blame History

 ```package green.thisfieldwas.embracingnondeterminism.control ``` ``` ``` ```/** Monad is a specialization of Functor where the type contained within the ``` ``` * context, `A` in `F[A]`, is known specifically to have some type of `F[A]`. ``` ``` * That is, the Functor is nested as `F[F[A]]`. The Monad defines a function ``` ``` * called `flatten()` which joins the inner `F[A]` with the outer `F[_]`, and ``` ``` * defines a specialization of the `map()` function called `flatMap()` which ``` ``` * permits the usage of a function producing a context: `f: A => F[B]`. ``` ``` * ``` ``` * By permitting the context to nest within itself, the inner context may alter ``` ``` * the outer context's case when flattened. This allows for the function `f: A ``` ``` * \=> F[B]` to control whether computation proceeds or halts against the ``` ``` * context. ``` ``` * ``` ``` * Monads must define one or both of `flatten()` or `flatMap()`, as one is used ``` ``` * to define the other. ``` ``` * ``` ``` * @tparam F ``` ``` * The context type. ``` ``` */ ``` ```trait Monad[F[_]] extends Applicative[F] { ``` ``` ``` ``` /** Map the contents of this context with a function which returns its results ``` ``` * in a new instance of the context. This new context is joined with the ``` ``` * current context. ``` ``` * ``` ``` * If the context `fa` is in the desired case, two things can happen based on ``` ``` * the output of function `f`: ``` ``` * ``` ``` * 1. If `f` returns `F[B]` in the desired case, then `flatMap()` returns ``` ``` * `F[B]` in the desired case. ``` ``` * 1. If `f` returns `F[B]` in the undesired case, then `flatMap()` returns ``` ``` * `F[B]` in the undesired case and further computation is halted. ``` ``` * ``` ``` * If the context `fa` is in the undesired case, then `f` does not apply and ``` ``` * no computation occurs. ``` ``` * ``` ``` * @param fa ``` ``` * The context to map. ``` ``` * @param f ``` ``` * The function to map the contents of the context. ``` ``` * @tparam A ``` ``` * The term of the instances within the context. ``` ``` * @tparam B ``` ``` * The term of the instances within the context produced by the function. ``` ``` * @return ``` ``` * The context produced by the function joined with the context `fa`. ``` ``` */ ``` ``` def flatMap[A, B](fa: F[A])(f: A => F[B]): F[B] = flatten(map(fa)(f)) ``` ``` ``` ``` /** Flattens a nested context. ``` ``` * ``` ``` * Based on the states of the contexts, a few things may happen: ``` ``` * 1. If the outer context is in the undesired case, then a flattened ``` ``` * context in the undesired case is produced. ``` ``` * 1. If the inner context is in the undesired case, then a flattened ``` ``` * context in the undesired case is produced. 3. If both contexts are in ``` ``` * the desired case, then a flattened context in the desired case is ``` ``` * produced. ``` ``` * ``` ``` * @param ffa ``` ``` * The nested context. ``` ``` * @tparam A ``` ``` * The term contained by the inner context. ``` ``` * @return ``` ``` * A flattened context containing term `A`. ``` ``` */ ``` ``` def flatten[A](ffa: F[F[A]]): F[A] = flatMap(ffa)(fa => fa) ``` ```} ``` ``` ``` ```object Monad { ``` ``` ``` ``` def apply[F[_]: Monad]: Monad[F] = implicitly[Monad[F]] ``` ```} ```