Applicative vs Monad
"Independent things should run independently."
Monads chain sequential steps where each step may depend on the previous result. Applicatives combine independent operations — operations that don't depend on each other's results. The key difference: Applicative can run things in parallel and collect ALL results (including ALL errors); Monad runs things sequentially and short-circuits on first failure.
Making breakfast. Toasting bread and boiling eggs are independent — you do both simultaneously (Applicative). But frying an egg in the toast drippings requires the toast to finish first — that's a dependency (Monad). Using a Monad when operations are independent wastes time. Using Applicative when there are real dependencies produces incorrect results.
// MONAD validation — sequential, fail-fast, stops at first error
function validateUserMonad(data) {
const name = validateName(data.name);
if (!name.ok) return { ok: false, errors: [name.error] };
// email check never runs if name fails!
const email = validateEmail(data.email);
if (!email.ok) return { ok: false, errors: [email.error] };
const age = validateAge(data.age);
if (!age.ok) return { ok: false, errors: [age.error] };
return { ok: true, user: { name: name.value, email: email.value, age: age.value } };
}
// User gets: "Name is required" → fixes it → submits again
// Gets: "Email is invalid" → fixes it → submits again ← bad UX
// APPLICATIVE validation — parallel, collect ALL errors
function validateUserApplicative(data) {
const results = [
validateName(data.name),
validateEmail(data.email),
validateAge(data.age),
];
const errors = results
.filter(r => !r.ok)
.map(r => r.error);
if (errors.length > 0) return { ok: false, errors };
return { ok: true, user: {
name: results[0].value,
email: results[1].value,
age: results[2].value,
}};
}
// User gets ALL 3 errors at once → fixes everything → submits once ← good UX
// Independent API calls — Applicative (parallel)
const [user, settings, perms] = await Promise.all([
fetchUser(id), // independent
fetchSettings(id), // independent
fetchPermissions(id), // independent
]);