The coercion integration (Param witness → Coe) #
This module derives a Lean Coe/CoeTC from a Param witness by extracting
its forward map, so a representation change occurs during elaboration:
writing (a : B) for a : A inserts the transfer's forward map automatically.
This is the Lean analogue of Coq-Trocq's coercion fallback, realized through
Lean's per-pair Coe/CoeTC instance mechanism rather than
a general "expected-X-got-Y → run-a-tactic" hook (see the Scope note below).
The building blocks:
Param.fwdMapprojects the forward functionA → Bout of anyParam .map1 n A B(andParam.fwdMapOfMap3does the same from a.map3forward witness, via theParamHierarchy.leanforget chain). Every level≥ map1carries themapfield, so this is total on the relevant fragment.coeOfParamis the same forward function, named for its coercion role.coeTCOfHasParamis a scoped blanketCoeTC A Bderived from a synthesizedHasParam .map1 .map1 A Bwitness.
Scope #
Lean 4 has no general coercion fallback hook — there is no analogue of
Coq-Elpi's CoercionFallbackTactic, i.e. no "when the expected type is X but
the term has type Y, run this tactic to bridge them" entry point. Coercion in
Lean is driven entirely by registered Coe/CoeTC/CoeHead/CoeTail
instances keyed on the specific (A, B) pair, resolved by the instance cache.
So the realization here is necessarily per-(A, B) instances derived from
Param witnesses, not a single catch-all tactic the elaborator calls.
Two consequences worth stating plainly:
- Only the forward map survives. A coercion is just a function
A → B; inserting it discards theParamrelation's relatedness proof (map_in_R/R_in_map). The coercion says "here is aB", not "here is aBprovably related to the originalA". To also carry the relatedness — e.g. to rewrite a goal mentioninga : Ainto the related goal overBand keep the equivalence — you must use the transfer tactic (transfer!/synth_paramand theParam-translation machinery in this directory), not a plain coercion. - The blanket instance is scoped, not global. A global blanket
[HasParam .map1 .map1 A B] : CoeTC A Bis dangerous: withidHasParamgivingHasParam .map1 .map1 A Afor everyA, andarrowHasParamrecursing through→, an always-on instance invites the coercion resolver to attemptHasParamsynthesis on every type-mismatch site (over-applying, and re-entrant search through the arrow rule). It isscopedso it is only active where a module explicitlyopens this namespace and opts in; the trade-off is opt-in convenience vs. global safety. A specificCoe(coeNatWrapbelow) is the safest form when a single pair is wanted.
The forward-map extractor #
Every Map_k.Has R with k ≥ map1 carries a map : A → B field. MapHas .map1 R
reduces (by the defining match) to Map1Has R, so p.fwd.map typechecks
directly for a .map1 forward witness.
The forward map of a Param .map1 n A B — the function the relation's
forward direction encodes.
Instances For
The forward map of a .map3 forward witness, obtained by forgetting down to
map1 (Map3Has.toMap2a.toMap1). Lets an embedding's Param .map3 .map0
(e.g. paramOfEmbedding) feed the coercion machinery directly.
Equations
Instances For
A coercion function from a Param witness #
The forward map of a Param .map1 n A B, named for its role as the function
underlying a derived coercion A → B. Definitionally Param.fwdMap.
Equations
Instances For
Deriving Coe/CoeTC from a Param witness #
Scoped blanket (opt-in) #
A CoeTC A B for any pair carrying a synthesized HasParam .map1 .map1 A B.
scoped so it only fires where a module opens this namespace — see the Scope
note for why a global version over-fires.
Scoped blanket coercion: derive CoeTC A B from a HasParam .map1 .map1 A B
witness by inserting its forward map. Active only under
open scoped Transfer.Param.
Equations
- Transfer.Param.coeTCOfHasParam = { coe := fun (a : A) => Transfer.Param.coeOfParam Transfer.Param.HasParam.param a }
Instances For
Demo: a concrete pair + elaboration-time transfer #
A safe, self-contained demonstration that does not rely on the scoped blanket:
a hand-built Param .map1 .map0 whose forward map is the wrapper constructor,
turned into a specific Coe.
A specific coercion Nat → Wrap derived from natWrapParam (the safe,
non-blanket form).
Equations
Demo of the scoped blanket on a cross-type witness #
To show the blanket itself applies (not the specific coeNatWrap), this demo
uses a fresh pair Boxed/Nat that has only a HasParam .map1 .map1 witness and
no hand-written Coe. With the namespace open, (b : Nat) for b : Boxed
resolves through coeTCOfHasParam.
A diagonal-fragment witness HasParam .map1 .map1 Boxed Nat: forward map
Boxed.contents, backward map Boxed.mk.
Equations
- One or more equations did not get rendered due to their size.