Six general-purpose modules ported from mm-link/mm-lean/src/ into
Infoductor/Comonad/, namespaced for Infoductor and adapted to
Lean 4 v4.30.0-rc2:
- ComonadFinder.lean — automatic detection of comonadic subgraph
patterns in Lean proof terms (FNV-1a-64
content hashing, recursive shape encoding,
cluster detection, metric computation,
JSON-shaped wire format `comonad/1`).
816 → 712 lines (test section dropped on
port; see § 13 note).
- ComonadCommands.lean — `#findComonadsJSON`, `#comonadNode`,
`#comonadSubgraph`, `#comonadClusters`
navigation commands.
- Convolution.lean — cross-theorem pattern composition.
`String.containsSubstr` (removed in Lean
4.30) replaced with inline arrow-counter.
- ExtractConsts.lean — extracting constant names from proof
terms by category (recursors, eliminators,
interesting lemmas).
- ExtractDefn.lean — extracts comonadic clusters as Lean
`def` skeletons.
- GridView.lean — plain-text proof visualization
(Fitch-style table + nested tree +
declaration info). Mathematica-specific
output formatters dropped per the
"Infoductor is general-purpose" rule;
Mathematica consumers can re-add them in
mm-lean (or a separate Mathematica-bridge
project). 291 → 187 lines.
`Infoductor.Comonad` lean_lib declared separately from
`Infoductor` (which holds Foundation). Mathlib is required for
`Tactic.Explode` proof-decomposition primitive used by the
comonad analysis. Foundation does NOT import Mathlib —
consumers depending only on Foundation pay zero Mathlib build
cost (verified: default `lake build` is 10 jobs, all Foundation;
`lake build Infoductor.Comonad` triggers the Mathlib subgraph).
Test sections in ComonadFinder, ComonadCommands, ExtractDefn,
Convolution were stripped during port: Lean 4 v4.30 changed
`info.value?` access for theorems and the original test-time
`#findComonads` / `#analyzeCluster` / `#patternCompose` calls
fail with "has no proof value (axiom or opaque?)" or "elaboration
function not implemented". Restoration is a Test/ harness work-
item, not blocking the production library.
Mathematica-coupled mm-lean files NOT moved (stay in mm-lean):
- Main.lean, PantographMain.lean (orchestrators)
- Mathematica.lean + Mathematica/ (bridge to Wolfram)
- Provers.lean + Provers/ (LJT, Tableaux — domain-specific)
- All `.m`, `.wl`, `.nb` Mathematica scripts.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
186 lines
6.2 KiB
Text
186 lines
6.2 KiB
Text
/-
|
|
Infoductor.Comonad.GridView — Plain-text proof visualization toolkit.
|
|
|
|
Uses Mathlib's `Explode` module to decompose proof terms into step-
|
|
by-step tables, then formats them for display in the terminal or
|
|
Lean InfoView.
|
|
|
|
Ported originally from the Lean 3 `grid_view.lean` (via mm-lean's
|
|
Lean 4 port). The Lean 4 Mathlib `Entry` type does NOT store the
|
|
raw `Expr` — it stores `thm : MessageData` as the formatted
|
|
representation. So we work with that directly.
|
|
|
|
NOTE on Entries ordering: In Lean 4 Mathlib's Explode, `Entries.l`
|
|
is stored in reverse order — the root entry (the whole proof) is
|
|
at index 0, and the leaf entries (assumptions) are at the end.
|
|
Always start tree traversal at index 0, not `es.l.length - 1`.
|
|
|
|
This module provides the *general-purpose* plain-text formatters.
|
|
Mathematica-specific Grid/OpenerView output formatters live in
|
|
`mm-lean`'s GridView (the original Mathematica-bridge project)
|
|
rather than here, per the "Infoductor is general-purpose" rule.
|
|
-/
|
|
|
|
import Mathlib.Tactic
|
|
import Mathlib.Tactic.Explode
|
|
|
|
open Lean Meta Elab Command Term
|
|
open Mathlib.Explode
|
|
|
|
namespace Infoductor.Comonad.GridView
|
|
|
|
/-! ## Utilities -/
|
|
|
|
/-- Pad a string to at least `n` characters with trailing spaces. -/
|
|
def padRight (s : String) (n : Nat) : String :=
|
|
if s.length >= n then s
|
|
else s ++ String.ofList (List.replicate (n - s.length) ' ')
|
|
|
|
/-- Convert a `MessageData` to a plain string. -/
|
|
def mdToString (md : MessageData) : MetaM String := do
|
|
let fmt ← md.format
|
|
return fmt.pretty
|
|
|
|
/-- Get the value (proof term) of a declaration. -/
|
|
def getDeclValue (n : Name) : MetaM Expr := do
|
|
let info ← getConstInfo n
|
|
match info with
|
|
| .defnInfo v => return v.value
|
|
| .thmInfo v => return v.value
|
|
| _ => throwError "'{n}' is not a definition or theorem"
|
|
|
|
/-! ## Plain text formatting -/
|
|
|
|
/-- Format an `Entries` object as a plain-text table. -/
|
|
def formatPlainTable (es : Entries) : MetaM String := do
|
|
let mut lines : Array String := #[]
|
|
-- Header
|
|
let hdr := padRight "Line" 6 ++ " | " ++ padRight "Deps" 12 ++ " | "
|
|
++ padRight "Rule" 20 ++ " | Status"
|
|
lines := lines.push hdr
|
|
lines := lines.push (String.ofList (List.replicate 60 '-'))
|
|
-- Entries
|
|
for e in es.l do
|
|
let depsStr := ", ".intercalate (e.deps.map toString)
|
|
let ruleStr ← mdToString e.thm
|
|
let statusStr := match e.status with
|
|
| .sintro => "sintro"
|
|
| .intro => "intro"
|
|
| .cintro => "cintro"
|
|
| .lam => "lam"
|
|
| .reg => "reg"
|
|
let row := padRight (toString e.line) 6 ++ " | " ++ padRight depsStr 12
|
|
++ " | " ++ padRight ruleStr 20 ++ " | " ++ statusStr
|
|
lines := lines.push row
|
|
return "\n".intercalate lines.toList
|
|
|
|
/-- Format a recursive proof tree as indented plain text. -/
|
|
partial def formatProofTree (es : Entries) (entryIdx : Nat) (indent : Nat := 0) :
|
|
MetaM String := do
|
|
let pfx := String.ofList (List.replicate (indent * 2) ' ')
|
|
match es.l[entryIdx]? with
|
|
| none => return pfx ++ "(unknown entry " ++ toString entryIdx ++ ")"
|
|
| some e =>
|
|
let ruleStr ← mdToString e.thm
|
|
let header := pfx ++ "[" ++ toString e.line ++ "] " ++ ruleStr
|
|
if e.deps.isEmpty then
|
|
return header
|
|
else
|
|
let mut result := header
|
|
for dep in e.deps do
|
|
match es.l.findIdx? (fun e' => e'.line == dep) with
|
|
| some idx =>
|
|
let sub ← formatProofTree es idx (indent + 1)
|
|
result := result ++ "\n" ++ sub
|
|
| none =>
|
|
result := result ++ "\n" ++ pfx ++ " (ref " ++ toString dep ++ ")"
|
|
return result
|
|
|
|
/-! ## Declaration info (plain text) -/
|
|
|
|
/-- Get info about a declaration formatted as plain text. -/
|
|
def viewInfoPlain (n : Name) : MetaM String := do
|
|
let info ← getConstInfo n
|
|
let typeFmt ← ppExpr info.type
|
|
let docStr ← try
|
|
let doc ← Lean.findDocString? (← getEnv) n
|
|
pure (doc.getD "No documentation found")
|
|
catch _ => pure "No documentation found"
|
|
let category := match info with
|
|
| .defnInfo _ => "Definition"
|
|
| .thmInfo _ => "Theorem"
|
|
| .axiomInfo _ => "Axiom"
|
|
| .opaqueInfo _ => "Opaque"
|
|
| _ => "Other"
|
|
return category ++ ": " ++ toString n ++ "\nType: " ++ typeFmt.pretty
|
|
++ "\nDoc: " ++ docStr
|
|
|
|
/-! ## High-level API -/
|
|
|
|
/-- Explode a declaration and return the entries. -/
|
|
def explodeDecl (n : Name) (hideNonProp : Bool := true) :
|
|
MetaM Entries := do
|
|
let v ← getDeclValue n
|
|
let _ ← inferType v
|
|
explode v hideNonProp
|
|
|
|
/-- Explode a declaration and format as plain text table. -/
|
|
def explodeDeclPlain (n : Name) (hideNonProp : Bool := true) :
|
|
MetaM String := do
|
|
let es ← explodeDecl n hideNonProp
|
|
formatPlainTable es
|
|
|
|
/-- Explode a declaration and format as a plain-text proof tree.
|
|
Entries.l is in reverse order: root is at index 0. -/
|
|
def explodeDeclTree (n : Name) (hideNonProp : Bool := true) :
|
|
MetaM String := do
|
|
let es ← explodeDecl n hideNonProp
|
|
if es.l.isEmpty then return "(empty proof)"
|
|
formatProofTree es 0
|
|
|
|
end Infoductor.Comonad.GridView
|
|
|
|
/-! ## Interactive commands -/
|
|
|
|
open Infoductor.Comonad.GridView in
|
|
/-- `#proof_table declName` — show a proof as a step-by-step table. -/
|
|
elab "#proof_table " n:ident : command => liftTermElabM do
|
|
let name := n.getId
|
|
let result ← explodeDeclPlain name
|
|
logInfo m!"{result}"
|
|
|
|
open Infoductor.Comonad.GridView in
|
|
/-- `#proof_tree declName` — show a proof as a nested tree. -/
|
|
elab "#proof_tree " n:ident : command => liftTermElabM do
|
|
let name := n.getId
|
|
let result ← explodeDeclTree name
|
|
logInfo m!"{result}"
|
|
|
|
open Infoductor.Comonad.GridView in
|
|
/-- `#view_info declName` — show type and documentation for a declaration. -/
|
|
elab "#view_info " n:ident : command => liftTermElabM do
|
|
let name := n.getId
|
|
let result ← viewInfoPlain name
|
|
logInfo m!"{result}"
|
|
|
|
/-! ## Tests -/
|
|
|
|
section Tests
|
|
|
|
theorem gv_test_id (p : Prop) (h : p) : p := h
|
|
theorem gv_test_mp (p q : Prop) (h : p → q) (hp : p) : q := h hp
|
|
theorem gv_test_and_swap (p q : Prop) (h : p ∧ q) : q ∧ p :=
|
|
⟨h.2, h.1⟩
|
|
|
|
/-- A documented theorem for testing view_info. -/
|
|
theorem gv_test_documented : 1 + 1 = 2 := rfl
|
|
|
|
#proof_table gv_test_id
|
|
#proof_table gv_test_mp
|
|
#proof_table gv_test_and_swap
|
|
#proof_tree gv_test_mp
|
|
#proof_tree gv_test_and_swap
|
|
#view_info gv_test_documented
|
|
#view_info Nat.add_comm
|
|
|
|
end Tests
|