75 lines
2.9 KiB
Scala
75 lines
2.9 KiB
Scala
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]]


}
