ExamplesBy LevelBy TopicLearning Paths
244 Expert

Comonad Laws

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Comonad Laws" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. Just as monads must satisfy left identity, right identity, and associativity, comonads must satisfy three dual laws. Key difference from OCaml: | Aspect | Rust | OCaml |

Tutorial

The Problem

Just as monads must satisfy left identity, right identity, and associativity, comonads must satisfy three dual laws. These laws ensure that extract and extend compose predictably, making comonadic abstractions composable and refactorable without hidden surprises. This example formalizes and verifies the comonad laws in Rust using property-based tests on concrete comonad instances.

🎯 Learning Outcomes

  • • State the three comonad laws precisely: left identity, right identity, and associativity
  • • Understand each law as the categorical dual of the corresponding monad law
  • • Write property-based tests that verify the laws hold for Identity, Env, and Store comonads
  • • Recognize when a candidate extend implementation violates a law
  • • Connect comonad laws to the coherence conditions of comonadic algebras
  • Code Example

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

    Key Differences

    AspectRustOCaml
    Law encodingunit tests + genericsmodule signature + qcheck
    Trait object limitsimpl Fn not object-safefirst-class polymorphism
    Verificationcompile-time + runtimemostly runtime via qcheck
    PartialEq constraintrequired for equality testsstructural equality built in
    Laws as typesnot enforceablenot enforceable (same limitation)

    Neither language can enforce comonad laws at the type level — they remain runtime-verifiable contracts. This mirrors the situation with monad laws.

    OCaml Approach

    In OCaml the laws are typically stated as signatures in a functor:

    module type COMONAD = sig
      type 'a t
      val extract : 'a t -> 'a
      val extend  : ('a t -> 'b) -> 'a t -> 'b t
      (* Laws (as comments, verified by quickcheck) *)
      (* extend extract = Fun.id *)
      (* extract (extend f x) = f x *)
      (* extend f (extend g x) = extend (fun y -> f (extend g y)) x *)
    end
    

    QuickCheck/qcheck handles property verification; Rust uses proptest or hand-written loops.

    Full Source

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

    Deep Comparison

    OCaml vs Rust: Comonad Laws

    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 laws for the Store<usize, i32> comonad using a fixed finite array as the getter.
  • Find an extend-like operation that satisfies laws 1 and 2 but not 3 (associativity); explain which law breaks and why.
  • Write a QuickCheck/proptest harness that checks all three laws for Identity<i32> with random values.
  • Prove algebraically (on paper) that duplicate defined as extend id always satisfies the comonad laws if extend does.
  • Implement a ComonadLaws<W> trait with associated functions that check all three laws given a sample value, and implement it for both Identity and Env.
  • Open Source Repos