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]]
|
|
}
|