๐Ÿฆ€ Functional Rust

071: Identity Monad

Difficulty: โญโญ Level: Intermediate A monad that does nothing โ€” stripped bare so you can see the structure clearly.

The Problem This Solves

When you learn about monads through `Option` and `Result`, the monad machinery is mixed together with the interesting behavior (short-circuiting on `None`, carrying error types). It's hard to tell what's "the monad part" and what's "the `Option` part." Beginners often ask: "So what exactly does a monad add, independent of any specific effect?" It's a reasonable question. The answer is buried inside types that have a lot going on. The Identity monad is a monad that does nothing extra. No short-circuiting. No error carrying. No effects whatsoever. It wraps a value, lets you chain operations on it, and unwraps cleanly. The only thing it provides is the monadic structure itself. This sounds useless โ€” and for production code, it mostly is. But for understanding, it's exactly what you need: the monad skeleton with nothing obscuring it. And in advanced Rust, it becomes useful as a base case for monad transformers (stacking multiple effects together). The Identity monad exists to solve exactly that pain: it makes the abstract concrete by removing everything except the pattern itself.

The Intuition

Imagine a box. You put a value in. You can apply functions to the value inside. You can chain those functions. At the end, you take the value out. That's it. The box doesn't do anything else. It doesn't skip steps. It doesn't carry errors. It's just a box that supports chaining.
put in โ†’ transform โ†’ transform โ†’ transform โ†’ take out
Identity::of(10) โ†’ double โ†’ add_one โ†’ to_string โ†’ .run()
Compare to `Option`: the box might be empty, and if it's empty, all transforms are skipped. Compare to `Result`: the box might contain an error, and transforms are skipped. `Identity`: the box always has a value, transforms always run. The minimal interface every monad has: 1. `of(value)` โ€” put a value into the monad ("return" or "pure" in theory) 2. `bind(f)` โ€” apply a function that returns a wrapped value, flatten the result ("bind" or `>>=`) 3. `run()` โ€” extract the final value (only possible for Identity since there's no effect to unwrap)

How It Works in Rust

The struct โ€” as simple as possible
#[derive(Debug, Clone, PartialEq)]
struct Identity<A>(A);  // just a newtype wrapper
The three core operations
impl<A> Identity<A> {
 // "return" / "pure" โ€” put a value into Identity
 fn of(x: A) -> Self {
     Identity(x)
 }

 // "bind" / ">>=" โ€” apply f, get back Identity<B>
 // f must return Identity<B>, not plain B
 // This is what distinguishes bind from map
 fn bind<B, F: FnOnce(A) -> Identity<B>>(self, f: F) -> Identity<B> {
     f(self.0)   // unwrap, apply, return whatever f returned
 }

 // "map" โ€” apply a plain function (not monadic)
 fn map<B, F: FnOnce(A) -> B>(self, f: F) -> Identity<B> {
     Identity(f(self.0))   // unwrap, apply, re-wrap
 }

 // extract the final value
 fn run(self) -> A {
     self.0
 }
}
Chaining operations
let result = Identity::of(10)
 .bind(|x| Identity::of(x * 2))   // 10 โ†’ 20, wrapped in Identity
 .bind(|x| Identity::of(x + 1))   // 20 โ†’ 21, wrapped in Identity
 .run();                           // unwrap: 21

assert_eq!(result, 21);
`bind` vs `map` โ€” the key distinction
// map: the function returns a plain value โ†’ Identity wraps it
Identity(5).map(|x| x * 2)                    // Identity(10)

// bind: the function returns Identity โ†’ no double-wrapping
Identity(5).bind(|x| Identity::of(x * 2))     // Identity(10), not Identity(Identity(10))
`bind` flattens the result. That flattening is the essence of "monad" โ€” you can compose functions that each return a wrapped value, and the wrapping doesn't accumulate. The laws hold for Identity too
// Left Identity: Identity::of(a).bind(f) == f(a)
let f = |x: i32| Identity::of(x * 3);
assert_eq!(Identity::of(5).bind(f), f(5));    // Identity(15) == Identity(15)

// Right Identity: m.bind(Identity::of) == m
let m = Identity(42);
assert_eq!(m.clone().bind(Identity::of), m);  // Identity(42) == Identity(42)
These feel trivially true for Identity โ€” because there are no effects to get in the way. That's the point: Identity shows you the laws in their purest form.

What This Unlocks

Key Differences

ConceptOCamlRust
Type definition`type 'a t = Identity of 'a``struct Identity<A>(A)` (newtype)
`return` / `of``let return x = Identity x``fn of(x: A) -> Self`
`bind``let bind (Identity x) f = f x``fn bind<B, F>(self, f: F) -> Identity<B>`
Extraction`let run (Identity x) = x``fn run(self) -> A`
Use in practiceCommon as monad transformer baseRare directly; useful for understanding
// Identity monad โ€” the simplest possible monad.
// Wraps a value with zero extra effects.
// Useful as a base case in monad transformers.

#[derive(Debug, Clone, PartialEq)]
struct Identity<A>(A);

impl<A> Identity<A> {
    /// monadic `return` / `pure` โ€” lift a value into Identity
    fn of(x: A) -> Self {
        Identity(x)
    }

    /// `bind` (>>=) โ€” sequence computations
    fn bind<B, F: FnOnce(A) -> Identity<B>>(self, f: F) -> Identity<B> {
        f(self.0)
    }

    /// Functor `map`
    fn map<B, F: FnOnce(A) -> B>(self, f: F) -> Identity<B> {
        Identity(f(self.0))
    }

    /// Extract the wrapped value
    fn run(self) -> A {
        self.0
    }
}

fn main() {
    // Functor law: map id = id
    let v = Identity(42);
    assert_eq!(v.clone().map(|x| x), v);
    println!("Identity monad laws hold");

    // Monad: sequence computations
    let result = Identity::of(10)
        .bind(|x| Identity::of(x * 2))
        .bind(|x| Identity::of(x + 1));
    assert_eq!(result.run(), 21);
    println!("chain: {}", Identity::of(10)
        .bind(|x| Identity::of(x * 2))
        .bind(|x| Identity::of(x + 1))
        .run());

    // Demonstrate map
    let doubled = Identity(21).map(|x| x * 2);
    println!("mapped: {}", doubled.run());

    // Works with any type, not just integers
    let s = Identity("hello")
        .map(|s| s.to_uppercase())
        .bind(|s| Identity::of(s.len()));
    println!("string chain: {}", s.run());
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_functor_identity_law() {
        // map id = id
        let v = Identity(42);
        assert_eq!(v.clone().map(|x| x), v);
    }

    #[test]
    fn test_bind_chain() {
        let result = Identity::of(10)
            .bind(|x| Identity::of(x * 2))
            .bind(|x| Identity::of(x + 1));
        assert_eq!(result.run(), 21);
    }

    #[test]
    fn test_monad_left_identity() {
        // left identity: return a >>= f = f a
        let f = |x: i32| Identity::of(x * 3);
        let lhs = Identity::of(5).bind(f);
        let rhs = f(5);
        assert_eq!(lhs, rhs);
    }

    #[test]
    fn test_monad_right_identity() {
        // right identity: m >>= return = m
        let m = Identity(42);
        let result = m.clone().bind(Identity::of);
        assert_eq!(result, m);
    }

    #[test]
    fn test_map_composition_law() {
        // map (f . g) = map f . map g
        let v = Identity(5);
        let f = |x: i32| x + 1;
        let g = |x: i32| x * 2;
        let lhs = v.clone().map(|x| f(g(x)));
        let rhs = v.map(g).map(f);
        assert_eq!(lhs, rhs);
    }
}
(* Identity monad โ€” the simplest possible monad.
   Wraps a value with zero extra effects.
   Useful as a base case in monad transformers. *)

type 'a t = Identity of 'a

let return x = Identity x

let bind (Identity x) f = f x

let (>>=) m f = bind m f

let map f (Identity x) = Identity (f x)

let run (Identity x) = x

(* Functor law: map id = id *)
let () =
  let v = Identity 42 in
  assert (map (fun x -> x) v = v);
  Printf.printf "Identity monad laws hold\n"

(* Monad: sequence computations *)
let () =
  let result =
    return 10
    >>= (fun x -> return (x * 2))
    >>= (fun x -> return (x + 1))
  in
  assert (run result = 21);
  Printf.printf "chain: %d\n" (run result)