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>
179 lines
7.6 KiB
Text
179 lines
7.6 KiB
Text
/-
|
||
Infoductor.Foundation.Edit — Edit monad + Context comonad
|
||
============================================================
|
||
Phase B of `docs/ALGEBRA_PLAN.md`. Defines the abstract `Edit`
|
||
monad (a thread of source mutations) and the abstract `Context`
|
||
comonad (ambient information at a code position), plus the
|
||
distributive law that lets `Context`-aware functions produce
|
||
`Edit`-valued results.
|
||
|
||
This module is *pure data*: it does not depend on the Lean LSP,
|
||
Lean.Elab APIs, or any I/O backend. LSP-specific bindings
|
||
(a real `MakeEditLinkProps.ofReplaceRange` integration; a Code
|
||
Action provider; a `Lean.Server.CodeActionContext` consumer) live
|
||
in a separate `Algebra/EditLSP.lean` module and import this one.
|
||
|
||
The headless interpreter `runHeadless` applies an `Edit` to an
|
||
in-memory source buffer and emits the post-edit text. This is
|
||
what `lake exe algebra-restructure` uses (per ALGEBRA_PLAN §5.3
|
||
No-LSP fallback).
|
||
-/
|
||
|
||
import Infoductor.Foundation.Meta
|
||
|
||
namespace Infoductor
|
||
|
||
-- ── EditOp — the leaf of every edit ──────────────────────────────────────────
|
||
|
||
/-- A single source-mutation request. The `position` field names the
|
||
spot in source; `newContent` is what to write there; `oldContent`
|
||
(if known) is recorded for audit / undo.
|
||
|
||
`EditOp` is the meta-CTerm leaf — emitted by `restructure` as part
|
||
of an `Edit α` computation. -/
|
||
structure EditOp where
|
||
position : MetaPosition
|
||
newContent : MetaArtifact
|
||
oldContent : Option MetaArtifact := none
|
||
description : String := ""
|
||
deriving Inhabited
|
||
|
||
instance : Repr EditOp where
|
||
reprPrec e _ := s!"EditOp(at={repr e.position}, new={e.newContent.toString}, desc={e.description})"
|
||
|
||
-- ── The Edit monad ──────────────────────────────────────────────────────────
|
||
-- A computation that may emit zero or more `EditOp`s. Pure value α
|
||
-- is the result of the computation; the list is the side-effect.
|
||
|
||
/-- The Edit monad: a value-and-edit-list pair. Composition appends
|
||
edit lists; `pure` emits nothing.
|
||
|
||
Implementation note: we use the bare-bones `α × List EditOp`
|
||
representation rather than the IO-threaded `run :
|
||
CodeActionContext → IO (...)` form proposed in ALGEBRA_PLAN §3.1.
|
||
The IO and LSP threading are *implementation choices* that don't
|
||
affect the algebraic structure; lifting from this pure form into
|
||
the LSP form is a `MonadLift Edit (ReaderT … IO)` instance in
|
||
`EditLSP.lean`.
|
||
|
||
See `docs/ALGEBRA_PLAN.md` §3.1 for the design discussion. -/
|
||
structure Edit (α : Type) where
|
||
result : α
|
||
ops : List EditOp
|
||
deriving Inhabited
|
||
|
||
namespace Edit
|
||
|
||
/-- Lift a value into the trivial edit (zero ops). -/
|
||
def pure {α : Type} (a : α) : Edit α := { result := a, ops := [] }
|
||
|
||
/-- Sequence: concatenate edit lists. -/
|
||
def bind {α β : Type} (m : Edit α) (f : α → Edit β) : Edit β :=
|
||
let n := f m.result
|
||
{ result := n.result, ops := m.ops ++ n.ops }
|
||
|
||
/-- Emit a single edit op with no result. -/
|
||
def emit (op : EditOp) : Edit Unit := { result := (), ops := [op] }
|
||
|
||
/-- Emit several edit ops. -/
|
||
def emitMany (ops : List EditOp) : Edit Unit := { result := (), ops := ops }
|
||
|
||
instance : Monad Edit where
|
||
pure := Edit.pure
|
||
bind := Edit.bind
|
||
|
||
/-- Functor instance. Maps the result; leaves ops untouched. -/
|
||
instance : Functor Edit where
|
||
map f m := { result := f m.result, ops := m.ops }
|
||
|
||
end Edit
|
||
|
||
-- ── The Context comonad ─────────────────────────────────────────────────────
|
||
-- "What's around here?" At each meta-position, expose ambient info:
|
||
-- the current artifact, the position, the namespace, and whatever
|
||
-- additional metadata the elaborator pass populates.
|
||
|
||
/-- Ambient information at a meta-position.
|
||
|
||
`here` is the focal artifact; `pos` is its location; `siblings`
|
||
is a list of nearby artifacts that the restructuring might wish
|
||
to consult (e.g., to rename a definition consistently with its
|
||
consumers).
|
||
|
||
Per ALGEBRA_PLAN §3.1 the full Context carries `Lean.Environment`
|
||
and `QuestionGraph` fields too; those are populated only when
|
||
running inside the elaborator (post-import). The pure-data form
|
||
here works headless. -/
|
||
structure Context (α : Type) where
|
||
here : α
|
||
pos : MetaPosition
|
||
siblings : List MetaArtifact := []
|
||
deriving Inhabited
|
||
|
||
namespace Context
|
||
|
||
/-- Comonad `extract`: the focal artifact at the current position. -/
|
||
def extract {α : Type} (c : Context α) : α := c.here
|
||
|
||
/-- Comonad `extend`: relocate an `α` value to a context-dependent
|
||
`β` value, i.e. apply a context-aware function pointwise. -/
|
||
def extend {α β : Type} (f : Context α → β) (c : Context α) : Context β :=
|
||
{ here := f c, pos := c.pos, siblings := c.siblings }
|
||
|
||
instance : Functor Context where
|
||
map f c := { here := f c.here, pos := c.pos, siblings := c.siblings }
|
||
|
||
end Context
|
||
|
||
-- ── The distributive law: Context → Edit ────────────────────────────────────
|
||
-- The standard "comonad-to-monad" distributive setup. A context-
|
||
-- aware decision produces an edit; lifting it into a Context-keyed
|
||
-- Edit yields the contextualized edit.
|
||
|
||
/-- Lift a context-aware decision into an `Edit`. Per ALGEBRA_PLAN
|
||
§3.2 this is the *distributive law* between the Context comonad
|
||
and the Edit monad. -/
|
||
def Context.contextualEdit {α β : Type}
|
||
(decide : Context α → Edit β) (c : Context α) : Edit β :=
|
||
decide c
|
||
|
||
-- ── Classifier-at-position evaluation ───────────────────────────────────────
|
||
-- Decide whether a `MetaClassifier` matches a `MetaPosition`. Used
|
||
-- by `restructure` to pick `witness` vs `fallback`.
|
||
|
||
/-- Decide whether `φ` matches `pos`, syntactically. For
|
||
`.underAttribute` and `.dependencyOf` we conservatively answer
|
||
`false` here — those require Lean.Environment and live in the
|
||
LSP integration module.
|
||
|
||
The lattice operations `.meet` and `.join` are the obvious
|
||
Boolean combinations; `.always` is `true`, `.never` is `false`. -/
|
||
def MetaClassifier.atPosition : MetaClassifier → MetaPosition → Bool
|
||
| .always, _ => true
|
||
| .never, _ => false
|
||
| .atDecl n, p => decide (n = p.declName)
|
||
| .inFile s, p => decide (s = p.filePath)
|
||
| .underAttribute _, _ => false -- needs environment; resolved in LSP module
|
||
| .dependencyOf _, _ => false -- needs dep graph; resolved in LSP module
|
||
| .inNamespace n, p =>
|
||
-- syntactic prefix check: is `p.declName` lexically under `n`?
|
||
n.isPrefixOf p.declName
|
||
| .meet a b, p => a.atPosition p && b.atPosition p
|
||
| .join a b, p => a.atPosition p || b.atPosition p
|
||
|
||
/-- Lattice laws on `atPosition` — the meta-mirror of the cubical
|
||
face-formula laws (`FaceFormula.eval`). `meet` is conjunction,
|
||
`join` is disjunction, `always`/`never` are top/bottom. -/
|
||
theorem MetaClassifier.atPosition_always (p : MetaPosition) :
|
||
MetaClassifier.always.atPosition p = true := rfl
|
||
|
||
theorem MetaClassifier.atPosition_never (p : MetaPosition) :
|
||
MetaClassifier.never.atPosition p = false := rfl
|
||
|
||
theorem MetaClassifier.atPosition_meet (a b : MetaClassifier) (p : MetaPosition) :
|
||
(a.meet b).atPosition p = (a.atPosition p && b.atPosition p) := rfl
|
||
|
||
theorem MetaClassifier.atPosition_join (a b : MetaClassifier) (p : MetaPosition) :
|
||
(a.join b).atPosition p = (a.atPosition p || b.atPosition p) := rfl
|
||
|
||
end Infoductor
|