Dec Code Story

Chapter #8 – Monads & Effects

Option/Maybe, Result/Either, list monad, IO/state monads, effect systems

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