Inherited from Functor.map.
Applies the pure (binary) function f
to the effectful values
fa
and fb
.
map2
can be seen as a binary version of Functor.map.
Captures the idea of composing independent effectful values.
It is of particular interest when taken together with Functor.
Where Functor captures the idea of applying a unary pure
function to an effectful value, calling product
with map
allows one to apply a function of arbitrary arity to multiple
independent effectful values.
This operation is equivalent with:
map2(fa, fb, (a, b) => [a, b])
Generated using TypeDoc
The
Apply
type class, a weaker version of Applicative, exposingap
(apply), but notpure
.This type class is exposed in addition to
Applicative
because there are data types for which we can't implementpure
, but that could still benefit from anap
definition. For example in case of aMap<K, ?>
we couldn't definepure
for it because we don't have aK
key.MUST obey the laws defined in ApplyLaws.
Note that having an
Apply
instance implies that a Functor implementation is also available, which is whyApply
is a subtype ofFunctor
.Implementation notes
Even though in TypeScript the Funfix library is using
abstract class
to express type classes, when implementing this type class it is recommended that you implement it as a mixin using "implements
", instead of extending it directly with "extends
". See TypeScript: Mixins for details and note that we already haveapplyMixins
defined.Implementation example:
import { HK, Apply, registerTypeClassInstance, applyMixins } from "funfix" // Type alias defined for readability. // HK is our encoding for higher-kinded types. type BoxK<T> = HK<Box<any>, T> class Box<T> implements HK<Box<any>, T> { constructor(public value: T) {} // Implements HK<Box<any>, A>, not really needed, but useful in order // to avoid type casts. Note these can and should be undefined: readonly _funKindF: Box<any> readonly _funKindA: T } class BoxApply implements Apply<Box<any>> { map<A, B>(fa: BoxK<A>, f: (a: A) => B): Box<B> { const a = (fa as Box<A>).value return new Box(f(a)) } ap<A, B>(fa: BoxK<A>, ff: BoxK<(a: A) => B>): Box<B> { const a = (fa as Box<A>).value const f = (ff as Box<(a: A) => B>).value return new Box(f(a)) } // Mixed-in, as these have default implementations map2: <A, B, Z>(fa: BoxK<A>, fb: BoxK<B>, f: (a: A, b: B) => Z) => Box<Z> product: <A, B> (fa: BoxK<A>, fb: BoxK<B>) => Box<[A, B]> } // Call needed in order to implement `map2` and `product` using // the default implementations defined by `Apply`, because // we are using `implements` instead of `extends` above and // because in this sample we want the default implementations, // but note that you can always provide your own definitions applyMixins(BoxApply, [Apply]) // Registering global Apply instance for Box, needed in order // for the `applyOf(Box)` calls to work registerTypeClassInstance(Apply)(Box, new BoxApply())
We are using
implements
in order to support multiple inheritance and to avoid inheriting anystatic
members. In the Flow definitions (e.g..js.flow
files) for Funfix these type classes are defined with "interface
", as they are meant to be interfaces that sometimes have default implementations and not classes.Credits
This type class is inspired by the equivalent in Haskell's standard library and the implementation is inspired by the Typelevel Cats project.