103 lines
3.3 KiB
Scala
103 lines
3.3 KiB
Scala
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 twoargument 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]]


}
