You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.3 KiB
102 lines
3.3 KiB
package green.thisfieldwas.embracingnondeterminism.control
|
|
|
|
import green.thisfieldwas.embracingnondeterminism.data.Functor
|
|
|
|
/** Applicative is a specialization of Functor where the type contained within
|
|
* the context, `A` in `F[A]`, in known specifically to have some type of `A =>
|
|
* B`. That is, the Functor contains a function and has the form of `F[A =>
|
|
* B]`. This make the Functor an ''Applicative'' Functor, which allows it to be
|
|
* treated as a lifted function.
|
|
*
|
|
* @tparam F
|
|
* The context type.
|
|
*/
|
|
trait Applicative[F[_]] extends Functor[F] {
|
|
|
|
/** Applicative provides a default map() implementation. This can be
|
|
* overridden if it makes sense to.
|
|
*
|
|
* @param fa
|
|
* The instance of the structure to apply the function to.
|
|
* @param f
|
|
* The function to apply, if the structure is in the desired case.
|
|
* @tparam A
|
|
* The type of instances contained within the structure.
|
|
* @tparam B
|
|
* The type to map the instances into.
|
|
* @return
|
|
* The resulting structure.
|
|
*/
|
|
override def map[A, B](fa: F[A])(f: A => B): F[B] = ap(pure(f))(fa)
|
|
|
|
/** Lifts the pure value of a computation into the context. This is an
|
|
* abstracted constructor for the `Applicative` and concretely for contexts
|
|
* `Option` it is `Some`, and for `Either` it is `Right`. It always creates a
|
|
* new context in the desired case.
|
|
*
|
|
* @param a
|
|
* The value to lift.
|
|
* @tparam A
|
|
* The type of the value.
|
|
* @return
|
|
* A new context in the desired case.
|
|
*/
|
|
def pure[A](a: A): F[A]
|
|
|
|
/** Pronounced "apply", this function takes an applicative functor of the form
|
|
* `F[A => B]` and applies it to the functor `F[A]`, giving the result of
|
|
* `F[B]`.
|
|
*
|
|
* @param ff
|
|
* The applicative functor, or lifted function.
|
|
* @param fa
|
|
* The argument context, or lifted argument.
|
|
* @tparam A
|
|
* The type of the argument.
|
|
* @tparam B
|
|
* The type of the result.
|
|
* @return
|
|
* The lifted result.
|
|
*/
|
|
def ap[A, B](ff: F[A => B])(fa: F[A]): F[B]
|
|
|
|
/** A two-argument analog of `map()`.
|
|
*
|
|
* @param fa
|
|
* The first argument context.
|
|
* @param fb
|
|
* The second argument context.
|
|
* @param f
|
|
* The function to apply to the contents of the arguments.
|
|
* @tparam A
|
|
* The type of the first argument.
|
|
* @tparam B
|
|
* The type of the second argument.
|
|
* @tparam C
|
|
* The type of the result.
|
|
* @return
|
|
* The lifted result.
|
|
*/
|
|
def map2[A, B, C](fa: F[A], fb: F[B])(f: (A, B) => C): F[C] =
|
|
ap(ap(pure(f.curried))(fa))(fb)
|
|
|
|
/** Take a list of contexts and transform them into a context containing a
|
|
* list of their contents. If all contexts are in their desired case, then
|
|
* the output context will be a desired case containing a list of their
|
|
* contents. Otherwise, the output context will be in the undesired case if
|
|
* any context is in the undesired case.
|
|
*
|
|
* @param listFa
|
|
* The list of functors.
|
|
* @tparam A
|
|
* The type contained within the functors.
|
|
* @return
|
|
*/
|
|
def sequence[A](listFa: List[F[A]]): F[List[A]] =
|
|
listFa.foldLeft(pure(List[A]()))((fListA, fa) => map2(fa, fListA)(_ :: _))
|
|
}
|
|
|
|
object Applicative {
|
|
|
|
def apply[F[_]: Applicative]: Applicative[F] = implicitly[Applicative[F]]
|
|
}
|
|
|