โ† back to index

Kleisli Categories

Bartosz Milewski ยท 2014 ยท Category Theory for Programmers, Ch. 4

Prereqs: ๐Ÿž Chapter 1 (Category), ๐Ÿž Chapter 2 (Types and Functions). 7 min.

Side effects are not side effects if you make them part of the return type. A Kleisli category re-defines morphisms as functions a โ†’ m(b) and composition as the fish operator (>=>). The category laws still hold. You get purity and logging (or failure, or nondeterminism) at the same time.

a b f a M(b) f plain morphism Kleisli arrow
a M(b) M(c) f g g ▷ f (fish)

The problem: composing embellished functions

You have functions that return a value plus a log string. You can't compose them with ordinary function composition because the types don't line up: a โ†’ (b, string) followed by b โ†’ (c, string) needs someone to unpack the pair and concatenate the logs. That "someone" is Kleisli composition.

Scheme

The Writer type and Kleisli composition

The Writer type wraps a value with a log: (value . log). Kleisli composition (>=>, the "fish operator") extracts the value from the first function's result, feeds it to the second, and concatenates the logs. This is the composition rule for morphisms in the Kleisli category.

Scheme

Identity and the category laws

Every category needs an identity morphism. In the Kleisli category for Writer, identity returns the value unchanged and contributes an empty log. With identity and fish, we can verify the three category laws: left identity, right identity, and associativity.

Scheme

Logging as a real use case

The Writer monad lets you add logging to pure functions without mutation. Each function declares what it logs in its return type. Composition handles the plumbing. No global mutable log. No side effects. Just functions and types.

Scheme

Beyond Writer: the Kleisli pattern

Writer is just one Kleisli category. Replace (value . log) with (value | nothing) and you get the Maybe/Optional monad for partial functions. Replace it with a list and you get nondeterminism. The pattern is always the same: define a type embellishment, a composition rule, and an identity. If the laws hold, you have a Kleisli category.

Scheme

Notation reference

Blog / Haskell Scheme Meaning
Writer a = (a, String)(cons val log)Value paired with log
(>=>) :: (aโ†’m b) โ†’ (bโ†’m c) โ†’ (aโ†’m c)(fish f g)Kleisli composition
return :: a โ†’ m a(writer-id x)Kleisli identity
Maybe avalue | 'nothingOptional/partial result
compose m1 m2(fish f g)Same as >=>
Neighbors

Milewski chapters

Paper pages that use Kleisli categories

  • ๐Ÿž Fritz 2020 โ€” Markov categories: Kleisli category of the distribution monad is the main example. See jkAmbient Category for how Fritz's framework gives stochastic maps their own compositional semantics.
  • ๐Ÿž Staton 2025 โ€” probabilistic programs compose via Kleisli arrows

Foundations (Wikipedia)

Translation notes

The blog post uses C++ templates and Haskell. This page uses Scheme cons pairs as the Writer type and a plain fish function for Kleisli composition. The monoid used for logs is list append. The general construction works for any monoid (strings, numbers under addition, etc.), and for any monad, not just Writer. The Optional/Maybe example at the end shows the same pattern with a different embellishment.

Ready for the real thing? Read the blog post. Start with the C++ examples, then try the Haskell fish operator.