Skip to content
Applicative
step 1/5

Reading — step 1 of 5

Learn

~2 min readBeyond Functor

Applicative sits between Functor and Monad. Where Functor lets you map ONE pure function over a context, Applicative lets you apply a function-IN-CONTEXT to a value-IN-CONTEXT.

class Functor f => Applicative f where
    pure :: a -> f a
    (<*>) :: f (a -> b) -> f a -> f b

For Maybe:

Just (+1) <*> Just 5    -- Just 6
Just (+1) <*> Nothing   -- Nothing
Nothing <*> Just 5      -- Nothing

Useful pattern: applying multi-arg functions to wrapped values:

(+) :: Int -> Int -> Int

(+) <$> Just 3 <*> Just 4    -- Just 7
-- <$> is fmap (Functor)
-- <*> is the applicative apply

Reads as: "map (+) over Just 3 → Just (4+) → apply that to Just 4 → Just 7."

Multi-step with multi-arg:

f <$> arg1 <*> arg2 <*> arg3 <*> arg4

If any arg is Nothing/Failure/etc., the whole result is.

For Either e:

(+) <$> Right 3 <*> Right 4    -- Right 7
(+) <$> Right 3 <*> Left "err" -- Left "err"

For lists — Cartesian product:

[(+1), (*2)] <*> [10, 20]      -- [11, 21, 20, 40]

For IO — sequential effect composition:

main :: IO ()
main = do
    let total = (+) <$> readLn <*> readLn
    print =<< total
    -- Same as: do x <- readLn; y <- readLn; print (x + y)

liftA2 — sugar for f <$> a <*> b:

liftA2 (+) (Just 3) (Just 4)   -- Just 7

Why between Functor and Monad? Applicative is more powerful than Functor (multi-arg) but less than Monad (no dependent computation — second arg can't depend on first arg's result). Many useful types (parsers, validation accumulators) are Applicative but not Monad.

Validation type — Applicative but not Monad — accumulates errors instead of short-circuiting:

validate1 ((,) <$> validateName "" <*> validateAge "-5")
-- Errors from BOTH validations, not just first

This is why form validation libraries use Applicative.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…