BasicsDecCodeStory_Monads.html
copyright © James Fawcett
Revised: 05/11/2026
8.0 Prologue
A monad is a design pattern for sequencing computations that carry
context - the possibility of absence, failure, multiple results, or side effects.
Monads provide two operations: return (or pure) which wraps
a plain value in the context, and bind (>>=) which
chains computations, threading the context automatically.
You do not need to understand category theory to use monads productively. Every Rust
developer who uses ? on a Result, or chains
and_then on an Option, is already using monadic composition.
8.1 Option / Maybe
The Option (Rust) or Maybe (Haskell) monad represents
a computation that may produce no result. Chaining with and_then or
>>= short-circuits to None/Nothing at
the first missing value, eliminating null checks.
-- Haskell: Maybe chains with >>= (bind)
lookupUser :: Id -> Maybe User
lookupEmail :: User -> Maybe Email
sendEmail :: Email -> Maybe Receipt
pipeline :: Id -> Maybe Receipt
pipeline uid = lookupUser uid >>= lookupEmail >>= sendEmail
-- do-notation desugars to the same bind chain
pipeline uid = do
user <- lookupUser uid
email <- lookupEmail user
sendEmail email
-- Rust: and_then is monadic bind for Option
let receipt = lookup_user(uid)
.and_then(lookup_email)
.and_then(send_email);
8.2 Result / Either
The Result (Rust, F#) or Either (Haskell) monad
carries either a success value or an error value. Chaining propagates the error
automatically, eliminating nested error checks.
-- Haskell: Either String is a monad; Left is failure, Right is success
parseAge :: String -> Either String Int
validateAge :: Int -> Either String Int
process :: String -> Either String Int
process s = do
age <- parseAge s
validated <- validateAge age
return (validated * 2)
-- Rust: ? operator is syntactic sugar for monadic bind on Result
fn process(s: &str) -> Result<i32, String> {
let age = parse_age(s)?;
let validated = validate_age(age)?;
Ok(validated * 2)
}
-- F#: result computation expression
let process s = result {
let! age = parseAge s
let! validated = validateAge age
return validated * 2
}
8.3 List / Sequence Monad
The list monad models nondeterministic computation: a function
returning a list represents a computation with multiple possible results. Binding
applies the next step to every result, producing all combinations.
-- Haskell: list monad produces all Pythagorean triples up to n
pythTriples n = do
c <- [1..n]
b <- [1..c]
a <- [1..b]
if a^2 + b^2 == c^2 then return (a,b,c) else []
-- Equivalent list comprehension (syntactic sugar for the list monad)
pythTriples n = [ (a,b,c) | c <- [1..n], b <- [1..c], a <- [1..b]
, a^2 + b^2 == c^2 ]
-- Python list comprehension (same semantics)
pythagorean = [(a,b,c) for c in range(1,n+1)
for b in range(1,c+1)
for a in range(1,b+1)
if a**2 + b**2 == c**2]
8.4 IO and State Monads
In Haskell, IO is the monad for sequencing side effects. A value of
type IO a is an action that, when executed, may perform I/O and then
produce an a. The type system prevents IO actions from being called in
pure code, maintaining referential transparency everywhere else.
-- Haskell: IO actions are values; do-notation sequences them
main :: IO ()
main = do
putStrLn "Enter your name:"
name <- getLine
putStrLn ("Hello, " ++ name ++ "!")
-- The State monad threads a state value through a computation purely
import Control.Monad.State
increment :: State Int ()
increment = modify (+1)
runThrice :: State Int Int
runThrice = do
increment; increment; increment
get
execState runThrice 0 -- 3
The State monad makes explicit the state that flows through a computation, preventing
the implicit shared-state bugs of imperative programming while remaining purely
functional.
8.5 Effect Systems and Handlers
Algebraic effects are a composable alternative to monads for
modeling side effects. An effect declaration names the operations an effect provides;
a handler provides an interpretation. Unlike monads, effects compose naturally without
monad transformer stacks.
-- Koka language: algebraic effects
effect state<s> {
fun get() : s
fun set(x : s) : ()
}
fun counter() : state<int> int {
set(get() + 1)
get()
}
-- handler provides the interpretation
handle counter() {
return(x) -> (x, s)
get() -> resume(s, s)
set(x) -> resume((), x)
} (0) // initial state
Effect systems are available in Koka, Eff, and Unison, and are being researched for
inclusion in OCaml (OCaml 5). They represent the current frontier of structuring
effectful computation in a principled, composable way.
8.6 Epilogue
Monads unify a diverse set of patterns - optional values, error propagation,
nondeterminism, state, and I/O - under a single interface. The practical payoff
is that the same >>= or do-notation works identically
across all of them. The next chapter examines type classes, the abstraction that makes
this uniformity possible.
8.7 References
Monad - Haskell Wiki
Monad - Wikipedia
Koka - Algebraic Effects
Rust Result - std