ExamplesBy LevelBy TopicLearning Paths
246 Expert

Adjunctions

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Adjunctions" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. An adjunction between two functors `F` and `G` is a natural bijection between morphisms: `Hom(F A, B) ≅ Hom(A, G B)`. Key difference from OCaml: | Aspect | Rust | OCaml |

Tutorial

The Problem

An adjunction between two functors F and G is a natural bijection between morphisms: Hom(F A, B) ≅ Hom(A, G B). Adjunctions are the categorical generalization of "best approximations" and appear everywhere in functional programming: currying is an adjunction, the product/exponent adjunction underlies closures, and many monad/comonad pairs arise from adjunctions. This example implements adjunctions in Rust and shows how the State and Env comonad/monad pair emerges from the product adjunction.

🎯 Learning Outcomes

  • • Understand adjunctions via the unit and counit natural transformations
  • • Implement the product/diagonal adjunction giving rise to Reader/Writer
  • • Derive a monad from the composition G ∘ F using adjunction data
  • • Derive a comonad from the composition F ∘ G using adjunction data
  • • Compare Rust's trait-encoded adjunctions with OCaml's module functors
  • Code Example

    #![allow(clippy::all)]
    // Stub — awaiting conversion from OCaml source.

    Key Differences

    AspectRustOCaml
    Functor encodingtrait + structmodule type + module
    Type familiesassociated typesmodule type parameters
    Adjunction lawsruntime testsmodule coherence
    Higher-kindedsimulated via GATsmodule polymorphism
    Monad derivationmanual bindcan derive via functor composition

    Adjunctions provide a systematic way to derive monads and comonads: every adjunction gives rise to a monad G ∘ F and a comonad F ∘ G.

    OCaml Approach

    OCaml uses functors (module-level) for adjunctions:

    module type ADJUNCTION = sig
      type 'a left_f   (* F A *)
      type 'a right_g  (* G B *)
      val unit   : 'a -> 'a left_f right_g
      val counit : 'a right_g left_f -> 'a
    end
    
    (* Product adjunction: F = (E *), G = (E ->) *)
    module ProdAdj (E : sig type t val e : t end) = struct
      type 'a left_f  = E.t * 'a
      type 'a right_g = E.t -> 'a
      let unit a e = (e, a)
      let counit (e, f) = f e
    end
    

    OCaml functors map cleanly to categorical functors; Rust traits simulate the same with more verbosity.

    Full Source

    #![allow(clippy::all)]
    // Stub — awaiting conversion from OCaml source.

    Deep Comparison

    OCaml vs Rust: Adjunctions

    Overview

    See the example.rs and example.ml files for detailed implementations.

    Key Differences

    AspectOCamlRust
    Type systemHindley-MilnerOwnership + traits
    MemoryGCZero-cost abstractions
    MutabilityExplicit refmut keyword
    Error handlingOption/ResultResult<T, E>

    See README.md for detailed comparison.

    Exercises

  • Implement the diagonal adjunction Δ ⊣ × (diagonal functor left adjoint to product), which gives rise to the tuple monad.
  • Verify both triangle identities programmatically: G(ε) ∘ η_G = id_G and ε_F ∘ F(η) = id_F.
  • Derive the Writer<W, A> monad from the adjunction between (- × W) and (W →) where W is a monoid.
  • Show that the List monad arises from the free/forgetful adjunction between Set and Mon (monoids).
  • Implement the adjoint transpose: given f: F A -> B, produce f̂: A -> G B using unit and fmap.
  • Open Source Repos