π Monad Tutorial & Playground
Understand monads visually β bind chains, type flows, and interactive exercises
What Is a Monad?
A monad is a design pattern for chaining computations that carry extra context (failure, multiple results, state, etc.). In OCaml, a monad provides:
return (pure/unit)
Wraps a plain value into the monadic context.
val return : 'a -> 'a t (* Example: Some 42, Ok "hello", [1; 2; 3] *)
bind (>>=)
Chains a monadic value through a function that returns a new monadic value. This is the core operation.
val bind : 'a t -> ('a -> 'b t) -> 'b t (* Unwrap β Apply function β Rewrap *)
Why Not Just Use Functions?
Without monads, you'd write nested pattern matches for every step:
(* Without monads β deeply nested *) match parse_int s with | None -> None | Some n -> match safe_div 100 n with | None -> None | Some r -> Some (string_of_int r) (* With monads β clean chain *) parse_int s >>= safe_div 100 >>= fun r -> return (string_of_int r)
Four Common Monads
πΈ Option Monad
Chains computations that might fail (return None). Short-circuits on first None.
let bind m f = match m with | None -> None | Some x -> f x let (>>=) = bind let return x = Some x
πΉ Result Monad
Like Option but carries error information. Short-circuits on first Error.
let bind m f = match m with | Error e -> Error e | Ok x -> f x let (>>=) = bind let return x = Ok x
π» List Monad
Models non-deterministic computation. Each step can produce multiple results.
let bind m f = List.concat_map f m let (>>=) = bind let return x = [x]
πΊ State Monad
Threads state through a computation without explicit state passing.
type ('a, 's) state = 's -> 'a * 's let bind m f = fun s -> let (a, s') = m s in f a s' let return x = fun s -> (x, s) let get = fun s -> (s, s) let put s' = fun _ -> ((), s')
Bind Chain Visualizer
Pick a monad and see how values flow through a bind chain step by step.
π§ͺ Interactive Bind Chain Builder
Build a chain of operations and see the result. Add steps, change the starting value, and watch the data flow.
The Three Monad Laws
Every monad must satisfy these laws. They ensure bind and return behave predictably.
1. Left Identity
Wrapping a value and immediately binding is the same as just calling the function.
(* Option: *) Some 5 >>= (fun x -> Some (x + 1)) = (fun x -> Some (x + 1)) 5 = Some 6 (* β *)
2. Right Identity
Binding with return does nothing β it's a no-op.
(* Option: *) Some 5 >>= (fun x -> Some x) = Some 5 (* β *) None >>= (fun x -> Some x) = None (* β *)
3. Associativity
The order you group binds doesn't matter β they compose the same way.
(* Both produce the same result: *) (Some 5 >>= double) >>= safe_sqrt Some 5 >>= (fun x -> double x >>= safe_sqrt) (* Both = Some ~3.16 β *)
βοΈ Law Verifier
Pick a monad and value to verify all three laws hold.
Exercises
Test your understanding. Fill in the expected output for each expression.
Monad Comparison
| Monad | Context | return x | bind None/Error/[] | Use Case |
|---|---|---|---|---|
| Option | Might be absent | Some x | β None (short-circuit) | Nullable values, lookups |
| Result | Might fail with info | Ok x | β Error e (propagate) | Validation, IO, parsing |
| List | Multiple possibilities | [x] | β [] (no results) | Search, permutations |
| State | Threading state | fun s β (x,s) | N/A (always succeeds) | Counters, accumulators |
When to Use Which?
Decision Guide
Can it be absent? β Option
Can it fail with a reason? β Result
Can there be multiple answers? β List
Do you need to thread state? β State
Side effects + sequencing? β IO (Lwt / Async in OCaml)
Real-World OCaml Equivalents
(* Option: built-in *) Option.bind (Some 42) (fun x -> Some (x + 1)) (* Result: built-in since OCaml 4.08 *) Result.bind (Ok 42) (fun x -> Ok (x + 1)) (* List: List.concat_map *) List.concat_map (fun x -> [x; x * 2]) [1; 2; 3] (* Async IO: Lwt library *) Lwt.bind (Lwt_io.read_line Lwt_io.stdin) (fun line -> ...) (* let* syntax (OCaml 4.08+) *) let* x = Some 5 in let* y = Some 10 in Some (x + y)