EmergentBit
Blog
Functional ProgrammingRustType Theory

Monads Are Not Burritos: A Practical Introduction

·2 min read·Kristan Uccello

Every monad tutorial eventually compares them to burritos, boxes, or containers. These analogies are charming and completely useless for understanding the actual mechanic.

A monad is simpler than the metaphors make it sound.

The Core Idea

A monad is a type with two operations:

  1. return (or pure) — wrap a value in a context
  2. bind (or >>=) — chain operations that produce contextualized values

That's it. The "monad laws" just ensure these operations compose predictably.

In Rust: The Option Monad

Rust doesn't have a Monad trait (the type system isn't expressive enough without HKTs), but Option<T> behaves monadically:

fn find_user(id: u64) -> Option<User> { /* ... */ }
fn get_profile(user: &User) -> Option<Profile> { /* ... */ }

// Monadic chaining with and_then (this is bind)
let profile = find_user(42)
    .and_then(|user| get_profile(&user));

and_then is bind. It sequences computations, short-circuiting on None. No null checks, no nested if-lets.

Why This Matters for LLM Pipelines

When building an LLM pipeline, you chain many fallible steps:

  • Tokenize input
  • Embed the tokens
  • Retrieve from a vector store
  • Format the prompt
  • Run inference
  • Parse the output

Each step can fail. Monadic composition (via Result::and_then) lets you write that as a clean pipeline rather than a pyramid of error handling.

The Real Payoff

Once you internalize this pattern, you stop writing defensive imperative code. You write pipelines. The type system enforces the happy path, and errors flow automatically.

This is the core insight behind functional programming: make the structure of your computation explicit in the types.