ExamplesBy LevelBy TopicLearning Paths
245 Expert

Cofree Comonad

Functional Programming

Tutorial Video

Text description (accessibility)

This video demonstrates the "Cofree Comonad" functional Rust example. Difficulty level: Expert. Key concepts covered: Functional Programming. The Cofree comonad is the categorical dual of the Free monad. Key difference from OCaml: | Aspect | Rust | OCaml |

Tutorial

The Problem

The Cofree comonad is the categorical dual of the Free monad. Where Free builds up effects lazily for later interpretation, Cofree builds up a potentially infinite tree of values annotated with a functor at every node. It is the universal comonad: every comonad arises as a coalgebra for some Cofree instance. In Rust, Cofree enables elegant tree-shaped data structures where each node carries both a value and a collection of children described by a functor.

🎯 Learning Outcomes

  • • Understand Cofree as Cofree f a = (a, f (Cofree f a))
  • • Implement extract and extend for Cofree
  • • See how Cofree generalizes rose trees, streams, and annotated ASTs
  • • Use Cofree to annotate a recursive AST with type information
  • • Compare Cofree with OCaml's coinductive (lazy) equivalent
  • Code Example

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

    Key Differences

    AspectRustOCaml
    Recursion breakingBox<Cofree<A>>Lazy.t list
    Infinite structuresrequires Rc/arena tricksnatural with Lazy.t
    extend cloningrequires A: Clone + 'staticno explicit bounds
    Functor parameterbaked in as Vecpolymorphic over list functor
    Coinductionnot nativecodata via laziness

    A fully generic Cofree<F, A> in Rust requires HKT simulation (GATs), making the Vec-specialized version far more practical for most use cases.

    OCaml Approach

    OCaml's lazy evaluation makes Cofree natural for infinite structures:

    type ('f, 'a) cofree = Cofree of 'a * ('f, 'a) cofree list Lazy.t
    
    let extract (Cofree (a, _)) = a
    
    let rec extend f tree =
      let (Cofree (_, children)) = tree in
      Cofree (f tree, lazy (List.map (extend f) (Lazy.force children)))
    
    (* Infinite stream as Cofree over unit functor *)
    let rec nats n = Cofree (n, lazy [nats (n + 1)])
    

    OCaml's Lazy.t avoids the need for explicit Box and enables genuinely infinite Cofree structures without stack overflow.

    Full Source

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

    Deep Comparison

    OCaml vs Rust: Cofree Comonad

    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 Cofree with Option as the functor (making it a potentially-terminated stream).
  • Use extend to implement a Huffman encoding annotator: label each node with its subtree character frequency.
  • Implement unfold: S -> (A, Vec<S>) -> Cofree<A> as the Cofree anamorphism (corecursive unfold).
  • Show that duplicate (the Cofree version of extend id) produces a tree where each node's annotation is the subtree rooted there.
  • Implement a Cofree-based attribute grammar evaluator: given a grammar tree and inherited/synthesized attribute rules, use extend to compute the attributes in one traversal.
  • Open Source Repos