Reading — step 1 of 5
Learn
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…