This page records the repo's expectation that new Lean declarations carry the weakest typeclass assumptions that actually match the code.
- For every new
def,instance,lemma, ortheorem, spell out the minimal typeclass assumptions needed by that declaration. - Avoid blanket section-level declarations such as
variable {R : Type*} [Ring R] [BEq R] [LawfulBEq R]unless every declaration in scope genuinely needs all of them. - Keep definitions as weak as possible even if some later proofs need stronger assumptions.
- If one theorem in a group needs stronger assumptions than nearby definitions, strengthen only that theorem instead of the whole section.
Avoid:
variable {R : Type*} [Ring R] [BEq R] [LawfulBEq R]
def coeff (p : Foo R) : R := ...
def X : Foo R := ...
theorem support_spec (p : Foo R) : ... := ...Prefer:
def coeff {R : Type*} [Zero R] (p : Foo R) : R := ...
def X {R : Type*} [Zero R] [One R] : Foo R := ...
theorem support_spec {R : Type*} [Zero R] [BEq R] (p : Foo R) : ... := ...- Use
Zerofor coefficient lookup, support, degree, trimming, and other zero-sensitive queries. - Use
ZeroandOnefor constants and variables. - Use
AddorSemiringonly when addition or multiplication is actually used. - Use
NegZeroClassfor coefficientwise negation facts when the only extra fact needed is-0 = 0. - Use
Ringonly when the declaration genuinely uses subtraction or additive inverses in an essential way. - Add
DecidableEq,BEq, orLawfulBEqonly when the declaration actually branches on equality, trims throughBEq, or uses lemmas that require those structures.
These examples come directly from the recent typeclass-tightening refactor.
CompPoly/Univariate/Quotient.leannow defines the quotient carrier atZerolevel because coefficientwise equivalence only needs zero-padding:
def QuotientCPolynomial (R : Type*) [Zero R] :=
Quotient (@Raw.instSetoidCPolynomial R _)CompPoly/Bivariate/ToPoly.leannow states bridge lemmas likeevalEval_toPolyatSemiringlevel because the proof uses only additive and multiplicative structure:
theorem evalEval_toPoly {R : Type*} [BEq R] [LawfulBEq R] [Nontrivial R] [Semiring R]
(x y : R) (f : CBivariate R) :
@CBivariate.evalEval R _ _ _ _ x y f = (toPoly f).evalEval x y := by
...CompPoly/Univariate/Raw/Proofs.leannow keepsneg_coeffatNegZeroClassrather thanRingbecause coefficientwise negation only needs-0 = 0:
lemma neg_coeff {R : Type*} [NegZeroClass R] (p : CPolynomial.Raw R) (i : ℕ) :
p.neg.coeff i = - p.coeff i := by
...CompPoly/Univariate/Raw/Division.leanno longer carries a blanketRingassumption at section scope because every definition already has its own[Field R]requirement:
section Division
variable [BEq R]
def divModByMonicAux [Field R] ...Before finishing a change, check:
- Could this declaration drop from
RingtoSemiring? - Could it drop from
SemiringtoZero,One,Add, orMul? - Is
NegZeroClassenough instead ofRing? - Is a section-level
variable [...]forcing stronger assumptions onto nearby declarations that do not need them? - If a stronger theorem is unavoidable, can the stronger assumptions be made local to that theorem?