Initial commit. Six modules extracted from
cubical-transport-hott-lean4/CubicalTransport/Algebra/ and
re-namespaced as `Infoductor`:
- Foundation/Meta.lean — MetaCType, MetaClassifier,
MetaArtifact, MetaPosition
(with manual mutual decEq for
MetaClassifier's recursive
lattice arms)
- Foundation/Edit.lean — Edit monad + Context comonad +
distributive law +
MetaClassifier.atPosition
with lattice laws as theorems
- Foundation/Restructure.lean — universal `restructure` (5-field
comp-shaped operation), 6 frozen
aliases, headless apply +
brokenRefs / selfConsistent /
Edit.guarded
- Foundation/MacroAlias.lean — @[macroAlias] attribute +
EnvExtension registry
- Foundation/MetaPath.lean — @[metaPath src target]
attribute + registry +
findPathsFromSource/ToTarget
- Foundation/Methodology.lean — @[methodology classifier]
attribute + registry +
deriveByTransport (walks
metaPath registry) +
tryEntryAsClosed primitive +
cubical_search reference
demonstrator
Builds clean on lean4 v4.30.0-rc2, ten oleans. No deps beyond
Lean stdlib. Sub-libs Tactics / Pantograph / Runner are planned
under the same lakefile.toml but not yet implemented.
Pairs with Pantograph as the conductor sits atop the pantograph
on an electric train. "Info-ductor" — conducts information
(structure / classifications / methodology) through a Lean
codebase.
Forgejo: http://maxgit.wg:3000/max/infoductor
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
285 lines
13 KiB
Text
285 lines
13 KiB
Text
/-
|
|
Infoductor.Foundation.Methodology — `@[methodology]` + `cubical_search`
|
|
==========================================================================
|
|
Phase D' of `docs/ALGEBRA_PLAN.md`. Provides the autodiscovery
|
|
layer that crowns the metacoding stack:
|
|
|
|
- `@[methodology]` attribute: registers a Lean theorem as a "proof
|
|
fragment" indexed by a classifier shape. When `cubical_search`
|
|
encounters a question matching that shape, it tries the
|
|
registered theorem.
|
|
|
|
- `cubical_search` tactic: walks the methodology library by
|
|
classifier dispatch and applies matches; on miss, attempts
|
|
methodology-transport along declared structural Paths
|
|
(`transp`-at-the-meta-level).
|
|
|
|
Per ALGEBRA_PLAN §4.3:
|
|
> Once a small library of base methodologies exists, every new
|
|
> structural Path declared in the codebase **automatically generates
|
|
> new methodology candidates** by transporting existing methodologies
|
|
> across the path.
|
|
|
|
This module ships the registry + the basic tactic; the
|
|
methodology-transport clause is scaffolded as `deriveByTransport`
|
|
(a stub returning `[]` until the structural-Path infrastructure
|
|
arrives in REL2.6+).
|
|
-/
|
|
|
|
import Lean
|
|
import Infoductor.Foundation.MacroAlias
|
|
import Infoductor.Foundation.MetaPath
|
|
|
|
namespace Infoductor
|
|
|
|
open Lean Lean.Elab Lean.Elab.Tactic
|
|
|
|
-- ── Methodology entries ─────────────────────────────────────────────────────
|
|
|
|
/-- A registered methodology. Indexes a Lean theorem by the
|
|
classifier shape it solves.
|
|
|
|
`classifierName` is a `Name` referring to a `MetaClassifier`-
|
|
valued `def` (or one of the syntactic classifier predicates from
|
|
`Question.lean`). `theoremName` is the `Name` of the theorem
|
|
that `cubical_search` will try when the question matches.
|
|
|
|
Priority is used for ordering: higher priority methodologies are
|
|
tried first. Default 100; specialised methodologies bump higher,
|
|
catch-alls run lower. -/
|
|
structure MethodologyEntry where
|
|
classifierName : Name
|
|
theoremName : Name
|
|
priority : Nat := 100
|
|
description : String := ""
|
|
deriving Repr, Inhabited
|
|
|
|
-- ── The methodology registry ────────────────────────────────────────────────
|
|
|
|
/-- Global registry of methodologies, indexed by classifier name.
|
|
Built up via `@[methodology]`-tagged declarations.
|
|
|
|
Implementation: `EnvExtension` so adds are visible across
|
|
modules. Lookup is linear in the registry size (acceptable for
|
|
REL2.5; a discrtree-indexed version is REL2.6 work per
|
|
ALGEBRA_PLAN §10.4). -/
|
|
initialize methodologyRegistryExt :
|
|
SimplePersistentEnvExtension MethodologyEntry (Array MethodologyEntry) ←
|
|
registerSimplePersistentEnvExtension {
|
|
name := `Infoductor.methodologyRegistry
|
|
addEntryFn := fun arr e => arr.push e
|
|
addImportedFn := fun arrs => arrs.foldl (init := #[]) Array.append
|
|
asyncMode := .sync
|
|
}
|
|
|
|
def getMethodologies : CoreM (Array MethodologyEntry) := do
|
|
let env ← getEnv
|
|
return methodologyRegistryExt.getState env
|
|
|
|
def registerMethodology (entry : MethodologyEntry) : CoreM Unit := do
|
|
modifyEnv (methodologyRegistryExt.addEntry · entry)
|
|
|
|
/-- Find every methodology indexed against a given classifier name. -/
|
|
def findMethodologies (classifier : Name) : CoreM (Array MethodologyEntry) := do
|
|
let all ← getMethodologies
|
|
return all.filter (·.classifierName == classifier)
|
|
|
|
-- ── The `@[methodology]` attribute ──────────────────────────────────────────
|
|
-- Attached to a theorem. Attribute parser parses the classifier
|
|
-- name and optional priority. Registration adds an entry to the
|
|
-- methodology registry pointing back to the tagged theorem.
|
|
|
|
/-- Parser-level data for the `@[methodology]` attribute. We accept
|
|
a plain identifier naming the classifier. Priority configuration
|
|
via `priority := n` is Phase D'.1 work; the v0 attribute uses a
|
|
fixed default of 100.
|
|
|
|
The syntax `name :=` matches the attribute name registered below;
|
|
Lean's attribute system looks up attributes by syntax kind, not
|
|
by leading keyword. -/
|
|
syntax (name := methodology) "methodology" ident : attr
|
|
|
|
/-- The `@[methodology]` attribute itself. Records the tagged
|
|
theorem under the named classifier (priority defaults to 100). -/
|
|
initialize methodologyAttr : Unit ←
|
|
registerBuiltinAttribute {
|
|
name := `methodology
|
|
descr := "Register this theorem as a proof methodology indexed \
|
|
by a classifier (Phase D' of ALGEBRA_PLAN.md). Usage: \
|
|
`@[methodology IsFullFace]`."
|
|
add := fun declName stx _kind => do
|
|
-- Parse classifier name from the attribute syntax.
|
|
let classifierName? : Option Name :=
|
|
match stx with
|
|
| `(attr| methodology $cls:ident) => some cls.getId
|
|
| _ => none
|
|
let some classifierName := classifierName?
|
|
| throwError "@[methodology] requires a classifier name"
|
|
let env ← getEnv
|
|
let docstring? := (← findDocString? env declName).getD ""
|
|
registerMethodology
|
|
{ classifierName := classifierName
|
|
, theoremName := declName
|
|
, priority := 100
|
|
, description := docstring? }
|
|
}
|
|
|
|
-- ── The methodology-transport clause (scaffold) ─────────────────────────────
|
|
-- ALGEBRA_PLAN §4.3: when a structural Path connects classifierA to
|
|
-- classifierB and the methodology library has an entry for
|
|
-- classifierA, transport derives a candidate for classifierB.
|
|
--
|
|
-- Full implementation requires (a) declared structural Paths in the
|
|
-- codebase, (b) reification of methodology bodies as CTerms, (c)
|
|
-- application of `transp` at the methodology level. REL2.6+ work.
|
|
--
|
|
-- The scaffold below returns `[]` so the surrounding tactic remains
|
|
-- well-typed; uses are guarded by feature flags.
|
|
|
|
/-- Derive new methodology candidates by transporting existing ones
|
|
along declared structural Paths.
|
|
|
|
Per ALGEBRA_PLAN §4.3:
|
|
> Twenty starting methodologies + a hundred declared paths →
|
|
> potentially thousands of derived methodologies, each formally
|
|
> certified-by-construction.
|
|
|
|
Implementation: for every methodology M registered against a
|
|
classifier C₁, and every MetaPath C₁ ↦ C₂ in the registry,
|
|
derive a candidate methodology against C₂ whose witness is M's
|
|
body composed with the path witness.
|
|
|
|
For the v0 dispatch tactic, the "composed witness" is just M's
|
|
theorem name — `cubical_search` will try both M and the path
|
|
witness directly via apply/exact. A future Phase D'.6 will use
|
|
actual Lean term composition to produce a single derived
|
|
theorem. See `Algebra/MetaPath.lean` for the path attribute. -/
|
|
def deriveByTransport (targetClassifier : Name) : CoreM (Array MethodologyEntry) := do
|
|
let paths ← findPathsToTarget targetClassifier
|
|
let mut out := #[]
|
|
for path in paths do
|
|
-- For every methodology M against the source classifier of this
|
|
-- path, produce a derived candidate against the target classifier.
|
|
let sourceMethodologies ← findMethodologies path.sourceClassifier
|
|
for M in sourceMethodologies do
|
|
out := out.push
|
|
{ classifierName := targetClassifier
|
|
, theoremName := M.theoremName -- v0: try the source method directly
|
|
, priority := M.priority - 10 -- derived; lower priority
|
|
, description := s!"derived via metaPath {path.sourceClassifier} ↦ {path.targetClassifier} from {M.theoremName}" }
|
|
-- Also add the path witness itself as a candidate; cubical_search
|
|
-- can apply it to convert the goal to the source classifier form.
|
|
out := out.push
|
|
{ classifierName := targetClassifier
|
|
, theoremName := path.witnessName
|
|
, priority := M.priority - 20
|
|
, description := s!"path witness {path.witnessName} for transport" }
|
|
return out
|
|
|
|
-- ── Tactic primitive: try one entry as a goal closer ───────────────────────
|
|
-- Foundation primitive for any methodology-dispatching tactic.
|
|
-- Anyone can build different orderings, retry strategies, or
|
|
-- failure-reporting on top of this.
|
|
|
|
/-- Try to close the current goal by applying the registered
|
|
methodology entry's theorem.
|
|
|
|
**Contract.** Attempts `exact entry.theoremName`, falling back
|
|
to `apply entry.theoremName`. Returns `true` if the goal closes
|
|
completely (no subgoals remaining); `false` otherwise.
|
|
|
|
Tactic state is restored on `false` — neither a partial-apply nor
|
|
a thrown elaboration error leaks past the call. Never throws.
|
|
Order of attempts (`exact` before `apply`) is fixed by this
|
|
primitive; alternative orderings can be implemented by composing
|
|
different primitives.
|
|
|
|
Use this as the building block for goal-closing dispatch loops;
|
|
no opinion baked in beyond the fixed exact/apply order. -/
|
|
def tryEntryAsClosed (entry : MethodologyEntry) : TacticM Bool := do
|
|
let stx := mkIdent entry.theoremName
|
|
let initialState ← saveState
|
|
try
|
|
evalTactic (← `(tactic| (first | exact $stx | apply $stx)))
|
|
if (← getUnsolvedGoals).isEmpty then
|
|
return true
|
|
else
|
|
restoreState initialState
|
|
return false
|
|
catch _ =>
|
|
restoreState initialState
|
|
return false
|
|
|
|
-- ── Reference dispatcher: `cubical_search` ─────────────────────────────────
|
|
-- A reference composition of `findMethodologies` + `deriveByTransport`
|
|
-- + `tryEntryAsClosed` in one obvious order. This is a *demonstrator*,
|
|
-- not a normative tactic — anyone wanting different ordering, priority
|
|
-- handling, or failure reporting writes their own using the
|
|
-- primitives above.
|
|
|
|
/-- Reference-composition tactic: walk every registered methodology;
|
|
on miss, walk the `@[metaPath]`-derived transport candidates;
|
|
emit a structured failure report on full miss.
|
|
|
|
Stages (fixed in this demonstrator; anyone can write a different
|
|
composition):
|
|
1. Direct registry: `getMethodologies`, try each via
|
|
`tryEntryAsClosed` in registry order.
|
|
2. Transport: walk `getMetaPaths` and try each path's source
|
|
methodologies and the path witness itself.
|
|
3. Failure: structured report with per-candidate "didn't fire"
|
|
reasons.
|
|
|
|
This is a tactic *demonstrator*, exposing the primitive composition
|
|
pattern. Tag your goal-closing methodologies with `@[methodology
|
|
Classifier]` and they enter this dispatcher's stage 1; declare
|
|
`@[metaPath A B]` and stage 2 lifts methodologies from A to B
|
|
automatically. No opinion about ordering, retry strategy, or
|
|
priority interpretation beyond what's stated here. -/
|
|
syntax (name := cubicalSearch) "cubical_search" : tactic
|
|
|
|
elab_rules : tactic
|
|
| `(tactic| cubical_search) => do
|
|
let goal ← getMainGoal
|
|
let goalType ← goal.getType
|
|
let methodologies ← getMethodologies
|
|
let mut tried : Array Name := #[]
|
|
-- Stage 1: direct methodology dispatch.
|
|
for entry in methodologies do
|
|
tried := tried.push entry.theoremName
|
|
if (← tryEntryAsClosed entry) then return
|
|
-- Stage 2: methodology-transport candidates from every
|
|
-- declared `@[metaPath]`.
|
|
let allPaths ← getMetaPaths
|
|
for path in allPaths do
|
|
let sourceMethods ← findMethodologies path.sourceClassifier
|
|
for M in sourceMethods do
|
|
tried := tried.push M.theoremName
|
|
if (← tryEntryAsClosed M) then return
|
|
let witnessEntry : MethodologyEntry :=
|
|
{ classifierName := path.targetClassifier
|
|
, theoremName := path.witnessName
|
|
, priority := 0, description := "" }
|
|
tried := tried.push path.witnessName
|
|
if (← tryEntryAsClosed witnessEntry) then return
|
|
-- Structured failure (demonstrator format; no normative
|
|
-- commitment — alternative composers may emit any shape).
|
|
let lines := tried.map (fun n => s!" ✗ {n}")
|
|
let report := String.intercalate "\n" lines.toList
|
|
let pathCount := allPaths.size
|
|
let methCount := methodologies.size
|
|
throwError s!"cubical_search: no registered methodology closed the goal\n {← Meta.ppExpr goalType}\n\ncandidates considered ({methCount} direct + {pathCount} paths):\n{report}\n\nregister a new @[methodology] (or build a custom dispatcher from `tryEntryAsClosed`)."
|
|
|
|
-- ── Diagnostics ─────────────────────────────────────────────────────────────
|
|
|
|
/-- Print the methodology registry — what's currently available to
|
|
`cubical_search`. -/
|
|
def printMethodologies : CoreM Unit := do
|
|
let entries ← getMethodologies
|
|
IO.println s!"── Methodology registry ({entries.size}) ──"
|
|
for entry in entries do
|
|
let p := if entry.priority == 100 then "" else s!" (prio {entry.priority})"
|
|
let d := if entry.description.isEmpty then "" else s!" — {entry.description}"
|
|
IO.println s!" {entry.classifierName} → {entry.theoremName}{p}{d}"
|
|
|
|
end Infoductor
|