Why Applicative Isn't Enough: Sequencing Dependent Computations
Applicative combines independent effectful values, but many real computations are dependent: the second step needs to inspect the result of the first step before it can decide what to run next, and the shape of that second computation may vary based on that value. Monad captures this pattern of 'run an effectful computation, then feed its result into a function that produces the next effectful computation,' which Applicative's <*> -- being fixed to a single wrapped function -- cannot express.
Cricket analogy: Deciding whether to review an umpire's decision depends entirely on the outcome of the previous ball -- you can't decide to call for DRS until you know if the batter was given out, which is exactly the dependent sequencing Monad captures that Applicative can't.
The Monad Type Class: return and >>=
The class is class Applicative m => Monad m where (>>=) :: m a -> (a -> m b) -> m b, pronounced 'bind.' >>= takes a value in a monadic context, a function from a plain value to a new monadic context, runs the first computation, and feeds its unwrapped result into the function to get the next computation. Haskell's do-notation is syntactic sugar for chains of >>=: do { x <- m1; y <- f x; return y } desugars to m1 >>= \x -> f x >>= \y -> return y.
Cricket analogy: do { runs <- scoreBall; wickets <- checkWicket runs; return (runs, wickets) } is like a scorer who first records the runs off a ball, then, using that specific outcome, decides whether to log a wicket -- each step depends on the previous one's actual result, not just its type.
safeDiv :: Int -> Int -> Maybe Int
safeDiv _ 0 = Nothing
safeDiv x y = Just (x `div` y)
calc :: Int -> Int -> Int -> Maybe Int
calc a b c = do
step1 <- safeDiv a b
step2 <- safeDiv step1 c
return (step2 + 1)
main :: IO ()
main = do
print (calc 100 5 2) -- Just 11
print (calc 100 0 2) -- Nothingdo-notation is purely sugar over >>= and return/pure -- there is no special 'do magic'; the compiler mechanically rewrites every x <- action line into action >>= \x -> ... before type-checking, which is why do-blocks work identically for Maybe, lists, IO, or any other Monad instance.
Monad Instances: Maybe, List, and Either
For Maybe, >>= short-circuits on Nothing exactly like calc above, propagating failure automatically once any step fails. For lists, >>= maps a function producing a list over every element and concatenates the results -- [1,2,3] >>= \x -> [x, x*10] yields [1,10,2,20,3,30] -- which is how list comprehensions and nondeterministic search are expressed in do-notation. For Either e, >>= behaves like Maybe but threads a specific error value of type e through the Left case instead of a generic absence, letting calc-style pipelines return a meaningful error message on failure.
Cricket analogy: Maybe's bind short-circuiting on a failed run-chase calculation is like a required-run-rate calculator instantly giving up the moment overs remaining hits zero, propagating the 'match over' failure through every subsequent step without further computation.
Monad Laws
Every Monad instance must satisfy three laws: left identity, return a >>= f == f a; right identity, m >>= return == m; and associativity, (m >>= f) >>= g == m >>= (\x -> f x >>= g). These laws guarantee that chaining binds behaves consistently regardless of how the chain is grouped or how many trivial returns are inserted, which is what makes refactoring a do-block -- splitting one step into two, or inlining a helper -- safe.
Cricket analogy: The monad law m >>= return == m is like confirming that recording a boundary and then simply re-confirming it happened, with no extra edit, leaves the scoreboard exactly as it was before the redundant re-confirmation.
A common beginner mistake is trying to escape IO by imagining some 'unsafePerformMonad' to get a plain value out of IO a inside pure code -- but Monad doesn't unwrap values outside their context; >>= only ever lets you use the value inside another computation of the *same* monad. Once a value is in IO, ordinary pure code can't see it directly, which is by design, not a limitation to work around.
- Monad extends Applicative with >>= (bind), which sequences a computation into a function producing the next computation.
- >>= lets a later step depend on the result of an earlier step, unlike Applicative's <*>.
- do-notation is sugar for chains of >>= and return/pure, nothing more.
- Maybe's >>= short-circuits on Nothing; list's >>= implements nondeterministic mapping and concatenation; Either's >>= threads a typed error.
- The three monad laws (left identity, right identity, associativity) guarantee predictable, refactor-safe bind chains.
- return/pure lifts a plain value into the monad with no extra effect.
- A monadic value's content can't be pulled out into pure code -- only used inside another computation of the same monad.
Practice what you learned
1. What is the type signature of >>= ?
2. What does do-notation desugar into?
3. What does list's >>= do?
4. Which law states that (m >>= f) >>= g == m >>= (\x -> f x >>= g)?
5. What happens when you use >>= on a Nothing value in the Maybe monad?
Was this page helpful?
You May Also Like
Applicatives Explained
Learn how the Applicative type class extends Functor with pure and <*>, letting you combine several independent effectful values -- Maybe validations, list combinations, or IO actions -- with a plain function.
The IO Monad
Understand how Haskell models side effects with the IO monad -- IO values as pure descriptions of effects, do-notation sequencing, why you can't escape IO into pure code, and how to keep most logic pure.
Maybe and Either
How Haskell uses the Maybe and Either types to represent optional values and recoverable errors explicitly, without null or exceptions.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics