Applicative apply combines an effectful function, F[A => B], with an effectful argument, F[A], producing F[B]. It extracts the function and argument through the shared context, applies the function, and preserves that context. section 12.2 section 12.9
Implementing apply with map2
def apply[A, B](fab: F[A => B])(fa: F[A]): F[B] = fab.map2(fa)((f, a) => f(a)) The two inner values are combined by map2; the resulting value remains inside F. Conversely, map can lift a pure function with unit and pass it to apply.
Apply becomes especially useful when several effectful arguments feed one pure function. Currying turns a multi-argument function into successive unary functions, so each additional argument adds another layer of apply. section 12.9
Building map2, map3, and map4
def map2[B, C](fb: F[B])(f: (A, B) => C): F[C] = apply(apply(unit(f.curried))(fa))(fb) def map3[B, C, D](fb: F[B], fc: F[C]) (f: (A, B, C) => D): F[D] = apply(apply(apply(unit(f.curried))(fa))(fb))(fc) The pattern extends to map4 and higher-arity combinators.
