/* * Copyright 2021 Felix Wiemuth * * Use of this source code is governed by the Apache 2.0 license * that can be found in the LICENSE.md file. */////// Trait for types that support embedding values and applying embedded functions, and its functions.///pubmod Applicative {////// Trait for types that support embedding values and applying embedded functions.////// An applicative is a functor that allows one to:////// - Make an applicative value out of a normal value (embed it into a default context), e.g. embed `5` into `Some(5)`./// - Apply a function-type applicative to a matching argument-type applicative, resulting in an applicative of/// the function's result type.////// The meaning of the application realized by the `ap` function is defined by the respective instance./// Conceptually this can be understood as applying functions "contained" in the first applicative to arguments/// in the second applicative, where the possible quantity of functions/arguments depends on the type `m`./// For example, an `Option[a -> b]` can be `None`, or contain a function of type `a -> b`, and only in the/// latter case a function is applied. A `List[a -> b]` is an applicative that contains a list of functions,/// which are all to be applied to all arguments contained in the arguments list.////// A minimal implementation must define `point` and at least one of `ap` and `map2` (if `map2` is implemented,/// `ap` can be defined based on `map2` as shown below). If both `ap` and `map2` are defined,/// they must be equivalent to their default definitions:/// `ap(f: m[a -> b \ e], x: m[a]): m[b] \ ef = map2(identity, f, x)`/// `map2(f: a -> b -> c \ e, x: m[a], y: m[b]): m[c] \ ef = ap(Functor.map(f, x), y)`///pubtraitApplicative[m: Type -> Type] withFunctor[m] {////// Puts `x` into a default context.///pubdefpoint(x: a): m[a]////// Apply the function-type applicative `f` to the argument-type applicative `x`.///pubdefap(f: m[a -> b \ ef], x: m[a]): m[b] \ ef////// Lift a binary function to work on `Applicative`s./// Instances can define more efficient implementations than the default implementation/// (which is `Applicative.ap(Functor.map(f, x1), x2)`).///pubdefmap2(f: t1 -> t2 -> r \ ef, x1: m[t1], x2: m[t2]): m[r] \ ef = Applicative.ap(Functor.map(f, x1), x2)////// Lift a ternary function to work on `Applicative`s./// Instances can define more efficient implementations than the default implementation/// (which is `Applicative.ap(Applicative.map2(f, x1, x2), x3)`).///pubdefmap3(f: t1 -> t2 -> t3 -> r \ ef, x1: m[t1], x2: m[t2], x3: m[t3]): m[r] \ ef = Applicative.ap(Applicative.map2(f, x1, x2), x3)////// Lift a 4-ary function to work on `Applicative`s./// Instances can define more efficient implementations than the default implementation/// (which is `Applicative.ap(Applicative.map3(f, x1, x2, x3), x4)`).///pubdefmap4(f: t1 -> t2 -> t3 -> t4 -> r \ ef, x1: m[t1], x2: m[t2], x3: m[t3], x4: m[t4]): m[r] \ ef = Applicative.ap(Applicative.map3(f, x1, x2, x3), x4)////// Lift a 5-ary function to work on `Applicative`s./// Instances can define more efficient implementations than the default implementation/// (which is `Applicative.ap(Applicative.map3(f, x1, x2, x3), x4)`).///pubdefmap5(f: t1 -> t2 -> t3 -> t4 -> t5 -> r \ ef, x1: m[t1], x2: m[t2], x3: m[t3], x4: m[t4], x5: m[t5]): m[r] \ ef = Applicative.ap(Applicative.map4(f, x1, x2, x3, x4), x5) }////// Chain two applicative actions, returns the product of their results.///pubdefproduct(fa: m[a], fb: m[b]): m[(a, b)] withApplicative[m] =Applicative.map2((a, b) -> (a, b), fa, fb)////// Chain two applicative actions, return only the result of the first.///pubdefproductLeft(fa: m[a], fb: m[b]): m[a] withApplicative[m] =Applicative.map2((a, _) -> a, fa, fb)////// Chain two applicative actions, return only the result of the second.///pubdefproductRight(fa: m[a], fb: m[b]): m[b] withApplicative[m] =Applicative.map2((_, b) -> b, fa, fb)////// Chain three applicative actions, return the 3-tuple of their results.///pubdefproduct3(x1: m[t1], x2: m[t2], x3: m[t3]): m[(t1, t2, t3)] withApplicative[m] =Applicative.map3((a, b, c) -> (a, b, c), x1, x2, x3)////// Chain four applicative actions, return the 4-tuple of their results.///pubdefproduct4(x1: m[t1], x2: m[t2], x3: m[t3], x4: m[t4]): m[(t1, t2, t3, t4)] withApplicative[m] =Applicative.map4((a, b, c, d) -> (a, b, c, d), x1, x2, x3, x4)////// Chain five applicative actions, return the 5-tuple of their results.///pubdefproduct5(x1: m[t1], x2: m[t2], x3: m[t3], x4: m[t4], x5: m[t5]): m[(t1, t2, t3, t4, t5)] withApplicative[m] =Applicative.map5((a, b, c, d, e) -> (a, b, c, d, e), x1, x2, x3, x4, x5)////// `<*>` is an operator alias for `ap`.///pubdef<*>(mf: m[a -> b \ ef], ma: m[a]): m[b] \ efwithApplicative[m] = Applicative.ap(mf, ma)////// `<**>` is a variant of the operator `<*>` with its arguments flipped.////// The order of evaluation is `ma` then `mf`.///pubdef<**>(ma: m[a], mf: m[a -> b \ ef]): m[b] \ efwithApplicative[m] = Applicative.map2((a, f) -> f(a), ma, mf)////// `<*` is an operator alias for `productLeft`.///pubdef<*(ma: m[a], mb: m[b]): m[a] withApplicative[m] = productLeft(ma, mb)////// `*>` is an operator alias for `productRight`.///pubdef*>(ma: m[a], mb: m[b]): m[b] withApplicative[m] = productRight(ma, mb)}