Think of any wrapped context in Haskell (like Maybe) as a sealed plastic Lego storage box:
- Data (like the number 5) is a red Lego block inside.
- A Pure Function is an instruction sheet ("Attach a blue block").
Pure Function
No Lego boxes here—just instructions applied to raw blocks sitting on desk.
It only looks at the inputs explicitly passed. No database calls, no reading system clocks, no side effects. The same input always yields the exact same output.

addOne :: Int -> Int
addOne x = x + 1
Functor (fmap / <$>)
Got some building instructions, but the Lego block is locked inside a sealed plastic box.
A Functor acts like a pair of reach-in gloves: it applies the instructions to the wrapped block without breaking the box.

fmap (+1) (Just 5) -- Returns Just 6
Applicative (<*>)
What if both instructions AND Lego blocks are sealed in separate plastic boxes?
An Applicative opens both boxes, applies the boxed instructions to the boxed blocks, and seals the result inside a new third box.

(+) <$> Just 3 <*> Just 4 -- Returns Just 7
Monad (>>=)
For building a sequential pipeline where Step 2 depends on what comes out of Step 1; and if any box is empty (Nothing), process should abort immediately (aka handle errors/exceptions alongside)-
a Monad acts as the assembly manager: it opens Box 1, checks the contents, picks the next instruction dynamically, and handles missing pieces without crashing the app.
calculate :: Double -> Maybe Double
calculate input = do
step1 <- safeSqrt input -- Step 1
step2 <- safeSqrt (step1 + 5) -- Step 2 (depends on step1)
pure step2

Top comments (0)