/- Copyright (c) 2025 Henrique Borges. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Authors: Davi Barreira, Henrique Borges, Alexandre Rademaker -/ import Vizagrams.Mark /-! # Free Monad for Compositional Graphics This module implements the free monad structure `𝕋` (Tree) that provides compositional semantics for graphical marks. It enables building complex diagrams through algebraic composition and transformation operators. ## Core Concepts The free monad `𝕋 Ξ±` represents a tree of operations that can be: - **Pure values**: `𝕋.pure : Ξ± β†’ 𝕋 Ξ±` β€” lift a value into the tree - **Composition**: `𝕋.comp : 𝕋 Ξ± β†’ 𝕋 Ξ± β†’ 𝕋 Ξ±` β€” combine two trees (like `+`) - **Transformation**: `𝕋.act : ℍ β†’ 𝕋 Ξ± β†’ 𝕋 Ξ±` β€” apply a transformation (like `*`) ## The ℍ Structure `ℍ` (graphical transformation) bundles: - `s : Style` β€” styling attributes (color, stroke, etc.) - `g : Mat2Vec2` β€” geometric transformations (rotation, scaling, translation) ## Algebra and Evaluation The free monad is "free" because it builds an abstract syntax tree of operations without immediately evaluating them. The algebra `algΞΈ` interprets this tree into concrete primitives: ``` 𝕋 Mark β†’ Array Prim ``` This deferred evaluation allows for optimization and reasoning about diagram structure. ## Usage Examples ```lean -- Compose marks let diagram := circle + square -- 𝕋.comp -- Apply transformations let rotated := rotate (Ο€/4) * square -- 𝕋.act -- Combine both let complex := (rotate (Ο€/4) * square) + circle ``` ## Theoretical Foundation This follows the free monad construction from category theory: - `𝕋` is the free monad over the functor `F` - `Ξ·` (eta) is the unit of the monad (pure) - `ΞΌ` (mu) is the multiplication (join/flatten) - The monad laws hold by construction -/ open GraphicalPrimitive open GraphicalMark open Sty open LinearAlgebra namespace FreeMonad /-- **ℍ (Graphical Transformation)**: Combines styling and geometric transformations. This structure represents both visual styling and spatial transformations that can be applied to graphical marks. The name ℍ is chosen to represent a "homomorphism" or transformation in the categorical sense. **Components:** - `s : Style` β€” Styling attributes (fill color, stroke color, stroke width) - `g : Mat2Vec2` β€” Affine transformation (rotation, scaling, translation) **Usage:** ```lean let redRotated : ℍ := ℍ.mk {fillColor := Color.mk 1 0 0} (rotate (Ο€/4)) let transformed := redRotated * myMark ``` -/ structure ℍ where s : Style g : Mat2Vec2 /-- Coerce a `Style` into `ℍ` by using the identity transformation (scale 1). -/ instance : Coe Style ℍ where coe st := ℍ.mk st (scale 1) /-- Coerce a geometric transformation `Mat2Vec2` into `ℍ` with empty styling. -/ instance : Coe Mat2Vec2 ℍ where coe Ab := ℍ.mk {} Ab /-- Multiplication of transformations composes styles (via `++`) while keeping the first transformation. This allows chaining transformations like `h1 * h2`. -/ instance : Mul ℍ where mul x y := ℍ.mk ( x.s ++ y.s) ( x.g ) universe u /-- **𝕋 (Free Monad Tree)**: The central data structure for compositional graphics. The free monad `𝕋 Ξ±` builds an abstract syntax tree of graphical operations: - `pure x`: A leaf node containing a value of type `Ξ±` - `comp t1 t2`: Composition node combining two trees (visualized as `t1 + t2`) - `act h t`: Transformation node applying `ℍ` to a tree (visualized as `h * t`) **Key Properties:** - Deferred evaluation: operations build a tree without immediate rendering - Compositional: combine simple marks into complex diagrams - Algebraic: supports monadic operations (pure, bind) - Universe polymorphic: works across different type universes **Evaluation:** The tree is interpreted into concrete primitives via the algebra `algΞΈ : 𝕋 Mark β†’ Array Prim`. -/ inductive 𝕋 (Ξ± : Type u) where | pure : Ξ± β†’ 𝕋 Ξ± | comp : 𝕋 Ξ± β†’ 𝕋 Ξ± β†’ 𝕋 Ξ± | act : ℍ β†’ 𝕋 Ξ± β†’ 𝕋 Ξ± /-- Map a function over the values in a tree, preserving the tree structure. This makes `𝕋` a functor. -/ def 𝕋.map (f : Ξ± β†’ Ξ²) (a : 𝕋 Ξ±) : 𝕋 Ξ² := match a with | 𝕋.pure x => 𝕋.pure (f x) | 𝕋.comp x y => 𝕋.comp (𝕋.map f x) (𝕋.map f y) | 𝕋.act h x => 𝕋.act h (𝕋.map f x) /-- `𝕋` is a functor. -/ instance : Functor 𝕋 where map := 𝕋.map /-- **Ξ· (eta, unit)**: Lifts a value into the free monad. This is the monadic `pure` operation. -/ def Ξ· : Ξ± β†’ 𝕋 Ξ± := fun a => 𝕋.pure a /-- **ΞΌ (mu, multiplication)**: Flattens a nested tree `𝕋 (𝕋 Ξ±)` into `𝕋 Ξ±`. This is the monadic join operation. It handles transformation composition: when two transformations are nested (`act h (act h' t)`), they are composed into a single transformation (`act (h * h') t`). -/ def ΞΌ : 𝕋 (𝕋 Ξ±) β†’ 𝕋 Ξ± | 𝕋.pure x => x | 𝕋.comp x y => 𝕋.comp (ΞΌ x) (ΞΌ y) | 𝕋.act h x => match x with | 𝕋.act h' y => 𝕋.act (h * h') (ΞΌ y) | a => ΞΌ a /-- Monadic bind for the free monad: composes `ma : 𝕋 Ξ±` with `f : Ξ± β†’ 𝕋 Ξ²`. Defined as `ΞΌ ∘ map f`. -/ def freebind : (𝕋 Ξ±) β†’ (Ξ± β†’ 𝕋 Ξ²) β†’ (𝕋 Ξ²) := fun ma f => (ΞΌ ∘ (𝕋.map f)) ma /-- `𝕋` is a monad with `Ξ·` as unit and `freebind` as bind. -/ instance : Monad 𝕋 where pure := Ξ· bind := freebind /-- Apply a graphical transformation `ℍ` to an array of primitives. First applies the geometric transformation `h.g`, then the style `h.s`. -/ def applyH (h : ℍ) (prims : Array Prim) : Array Prim := prims.map (fun p => h.s * (h.g * p)) /-- **algΞΈ (Algebra Theta)**: The evaluation algebra for the free monad. Interprets a tree of primitive arrays into a single array of primitives: - `pure x`: Return the array as-is - `comp x y`: Concatenate the arrays from both branches (using `βŠ•`) - `act h x`: Apply transformation `h` to the evaluated array from `x` This is a catamorphism (fold) over the tree structure. -/ def algΞΈ : 𝕋 (Array Prim) β†’ Array Prim | 𝕋.pure x => x | 𝕋.comp x y => (algΞΈ x) βŠ• (algΞΈ y) | 𝕋.act h x => applyH h (algΞΈ x) /-- **flat**: Evaluates a tree of marks into an array of primitives. This is the main evaluation function that: 1. Maps `Mark.ΞΈ` over the tree to get `𝕋 (Array Prim)` 2. Evaluates the tree using the algebra `algΞΈ` to get `Array Prim` **Type signature:** `𝕋 Mark β†’ Array Prim` This flattens the compositional structure into drawable primitives. -/ def flat (t : 𝕋 Mark) : Array Prim := algΞΈ ((𝕋.map Mark.ΞΈ) t) /-! ### Operator Instances These instances provide intuitive syntax for building diagrams: - `h * mark`: Apply transformation `h` to a mark (left multiplication) - `mark * h`: Apply transformation `h` to a mark (right multiplication) - `mark1 + mark2`: Compose two marks side-by-side - Automatic coercion from `Mark` to `𝕋 Mark` -/ /-- Left multiplication: `h * t` applies transformation `h` to tree `t`. -/ instance : HMul ℍ (𝕋 Mark) (𝕋 Mark) := ⟨fun h t => 𝕋.act h t⟩ /-- Right multiplication: `t * h` applies transformation `h` to tree `t`. -/ instance : HMul (𝕋 Mark) ℍ (𝕋 Mark) := ⟨fun t h => 𝕋.act h t⟩ /-- Automatically lift a `Mark` into the free monad `𝕋 Mark`. -/ instance : Coe Mark (𝕋 Mark) where coe m := 𝕋.pure m /-- Composition: `t1 + t2` combines two trees. -/ instance : HAdd (𝕋 Mark) (𝕋 Mark) (𝕋 Mark) where hAdd m1 m2 := 𝕋.comp m1 m2 /-- Composition: `mark + tree` lifts mark and composes. -/ instance : HAdd Mark (𝕋 Mark) (𝕋 Mark) where hAdd m t := 𝕋.comp (𝕋.pure m) t /-- Composition: `tree + mark` lifts mark and composes. -/ instance : HAdd (𝕋 Mark) Mark (𝕋 Mark) where hAdd t m := 𝕋.comp t (𝕋.pure m) /-- Universe-polymorphic left multiplication. -/ instance : HMul ℍ (𝕋 Mark.{u}) (𝕋 Mark.{u}) := ⟨fun h t => 𝕋.act h t⟩ /-- Universe-polymorphic right multiplication. -/ instance : HMul (𝕋 Mark.{u}) ℍ (𝕋 Mark.{u}) := ⟨fun t h => 𝕋.act h t⟩ /-- Universe-polymorphic coercion from `Mark` to `𝕋 Mark`. -/ instance : Coe Mark.{u} (𝕋 Mark.{u}) where coe m := 𝕋.pure m /-! ### Universe Polymorphism To support composition across different type universes, we provide `ulift` and universe-polymorphic instances. This allows combining marks from different universe levels seamlessly. -/ /-- **ulift**: Lift a tree from universe `u+1` to universe `max u v + 1`. This enables composition of marks across different universe levels. -/ def 𝕋.ulift (a : 𝕋.{u+1} Mark ) : 𝕋.{(max u v) + 1} (Mark) := match a with | .pure x => .pure x.ulift | .comp s t => .comp s.ulift t.ulift | .act h t => .act h (𝕋.ulift t) /-- Universe-polymorphic addition: `𝕋 Mark.{u} + 𝕋 Mark.{v}`. -/ instance : HAdd (𝕋 Mark.{u}) (𝕋 Mark.{v}) (𝕋 Mark.{max u v}) where hAdd m n := 𝕋.comp m.ulift n.ulift /-- Universe-polymorphic addition: `Mark.{u} + 𝕋 Mark.{v}`. -/ instance : HAdd Mark.{u} (𝕋 Mark.{v}) (𝕋 Mark.{max u v}) where hAdd m n := 𝕋.comp (𝕋.pure m.ulift) n.ulift /-- Universe-polymorphic addition: `𝕋 Mark.{u} + Mark.{v}`. -/ instance : HAdd (𝕋 Mark.{u}) Mark.{v} (𝕋 Mark.{max u v}) where hAdd m n := 𝕋.comp (m.ulift) (𝕋.pure n.ulift) /-- Universe-polymorphic addition: `Mark.{u} + Mark.{v}`. -/ instance : HAdd Mark.{u} Mark.{v} (𝕋 Mark.{max u v}) where hAdd m n := 𝕋.comp (𝕋.pure (Mark.ulift m)) (𝕋.pure (Mark.ulift n)) end FreeMonad