941 Identity Monad
Tutorial
The Problem
Implement the identity monad — the simplest possible monad — as a Rust struct. It wraps a value with no additional effects, serving as the base case in monad transformer stacks and as a teaching tool for monad laws. Implement of (pure/return), bind (>>=), and map (fmap), then verify the three monad laws: left identity, right identity, and associativity.
🎯 Learning Outcomes
of, bind, and map for a concrete monadic type in Rustreturn a >>= f = f a), right identity (m >>= return = m), and associativity ((m >>= f) >>= g = m >>= (|x| f x >>= g))bind encodes sequential composition of computationsStateT Identity = State)Code Example
#![allow(clippy::all)]
// 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
}
}
#[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);
}
}Key Differences
| Aspect | Rust | OCaml |
|---|---|---|
| HKT support | None — each monad is a standalone struct | Module functors enable generic monad abstractions |
| Syntax | Method chaining: .bind(...).bind(...) | let* / >>= operator |
| Law verification | Tests with assert_eq! | Property tests or equational reasoning |
| Monad transformers | No generic transformer stack possible | StateT, ReaderT, etc. via functors |
The identity monad is a pedagogical foundation. Understanding it makes other monads — Option (fail-fast), Result (error propagation), Vec (non-determinism) — easier to see as specializations of the same bind/return pattern.
OCaml Approach
(* OCaml with ppx_let for monadic syntax *)
module Identity = struct
type 'a t = Id of 'a
let return x = Id x
let bind (Id x) f = f x
let map f (Id x) = Id (f x)
let run (Id x) = x
end
(* Monad laws as expressions *)
let left_identity a f =
(* return a >>= f = f a *)
Identity.(bind (return a) f = f a)
let right_identity m =
(* m >>= return = m *)
let open Identity in
bind m return = m
(* With let* syntax (OCaml 4.08+) *)
let pipeline =
let open Identity in
let* x = return 10 in
let* y = return (x * 2) in
return (y + 1)
(* = Id 21 *)
OCaml's module system makes it natural to define a MONAD signature and prove that Identity satisfies it. The let* syntax (monadic bind) makes pipelines readable without requiring do-notation.
Full Source
#![allow(clippy::all)]
// 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
}
}
#[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);
}
}#[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);
}
}
Exercises
assert_eq! tests using concrete values.Applicative (ap) for Identity: ap(Identity(f), Identity(x)) = Identity(f(x)).join: Identity<Identity<A>> -> Identity<A> and show it equals |m| m.bind(|x| x).State monad and confirm that StateT<Identity> reduces to plain State.bind to sequence five transformations and compare to a direct function composition using |> or method chaining.