Class Graph #
Builds the class graph from the environment.
References
- Alex J. Best. 2023. Automatically Generalizing Theorems Using Typeclasses. In Fifth Workshop on Formal Mathematics for Mathematicians. CEUR Workshop Proceedings. Retrieved from https://ceur-ws.org/Vol-3377/fmm12.pdf.
Edges #
Extraction #
Returns true iff e is "statable" from sArgs.
We define statability inductively as follows: e is statable from sArgs if
whnfR eis syntactically equal towhnfR sArgs[i]for somei,whnfR eis an fvar occurring inwhnfR sArgs[i]for somei,whnfR eis "closed" according toisClosed(intuitively, this means it contains no fvars), orwhnfR eis an application of a "non-synonym" type former whose key arguments (seekeyArgs) are all statable fromsArgs, orwhnfR eis a projection whose projected term is statable fromsArgs.
By synonym type former we mean any type former which unfolds to one of its own arguments. By
"non-synonym" type formers we mean any other type former. For example, OrderDual is a synonym type
former, while Monoid is a "non-synonym" type former. See also isSynonymFormer.
Implementation notes
One could argue that condition 2 should be weakened to whnfR e being an fvar that occurs in
sArgs[i] instead of whnfR sArgs[i]. For example, if sArgs[i] is FirstOf α β, where FirstOf α β := α, and whnfR e is β, the weakened version would claim that β is statable from FirstOf α β, while the original condition 2 claims that it is not. However, condition 2, as stated, is
better-suited for our purposes. This is because canonArg also uses whnfR, meaning that an
application like Monoid (FirstOf α β) would be canonicalized into Monoid α. Hence, an instance
like instance [Monoid (FirstOf α β)] : Mul β (let's pretend that it makes sense) would be
extracted into an (unsound) edge from Monoid α to Mul β under the weakened condition 2, and
rejected under the original condition 2.
Examples
According to our definition of "statable", we have the following (where α and β are fvars):
Definition case 1:
αis statable from#[α].Monoid αis statable from#[Monoid α].OrderDual αis statable from#[OrderDual α].
Definition case 2:
αis statable from#[Monoid α].αis statable from#[OrderDual α].
Definition case 3:
ℕis statable from#[].Monoid ℕis statable from#[].
Definition case 4:
Monoid αis statable from#[α].
Definition case 5: Suppose x : Nat × Nat. Then:
x.1is statable from#[x].x.1is statable from#[x.1].xis statable from#[x.1].
Definition cases 2 and 4:
Monoid αis statable from#[Group α].
Non-examples:
αis not statable from#[]or#[β].Monoid αis not statable from#[]or#[β].OrderDual αis not statable from#[α].- If
x y : Nat × Natthenx.1is not statable from#[y]or#[y.1].
Note: The reason we claim that OrderDual α is not statable from #[α], while at the same time
claiming that α is statable from #[OrderDual α], is that we don't want to introduce synonyms,
but are okay with removing them.
Equations
- GeneralizationLinter.statableFrom sArgs isClosed e = do let __do_lift ← Array.mapM Lean.Meta.whnfR sArgs GeneralizationLinter.statableFrom.go✝ isClosed __do_lift e
Instances For
Helper for extractEdge? which, given some information about the instance declaration that
extractEdge? is processing, indicates whether the edge is a weakening edge, which is the case iff
all of the following conditions are satisfied:
Every key argument of the target is statable from the source's arguments (see also
statableFrom).Why? Our graph's edges are ordered pairs of vertices, each vertex representing a specific kind of class application. For the graph to be sound, we need each edge to guarantee that, given the source vertex, we can reach the target vertex. If the target is not statable from the source's arguments, then this cannot be the case.
Every class-typed argument of the declaration other than the source is contained in the source's type as a direct argument.
Why? The rationale is essentially the same as for condition 1, this is just the analog for class-typed args. Together, non-instance-implicit and class-typed arguments comprise all of an instance (unless there's a non-class-typed instance-implicit parameter, which is almost never the case; Mathlib has only one such declaration,
CategoryTheory.Bundled.of, and even there the intended usage is that the instance-implicit parameter should be a typeclass). They may also overlap; for example, there's plenty of class-typed implicit arguments in Mathlib (see e.g.IsTopologicalGroup.toContinuousInvin the examples section below).
For a brief discussion on this topic, see the thread general > When are instance mappings projections on the Lean Zulip.
Examples
(Note: In the examples below, "source" refers to the last argument of the instance, which is looser
than the sense in which the term is used for sourceArg?.)
| Instance | Source's args | Target's key args | C1 |
|---|---|---|---|
IsTopologicalGroup.toContinuousInv | G, inst₁, inst₂ | G | ✓ |
Semiring.toNatAlgebra | R | ℕ, R | ✓ |
ULift.addLeftCancelMonoid | α | ULift α | ✓ |
Lex.instIsRightCancelAdd | α, inst₁ | Lex α | ✗ |
Matrix.isScalarTower | R, S, α, inst₁, inst₃, inst₂ | R, S, Matrix m n α | ✗ |
IsNoetherianRing.wfDvdMonoid | R, @CommSemiring.toSemiring R inst₁ | R | ✓ |
| Instance | Class-typed args…¹ | Source's type | C2 |
|---|---|---|---|
IsTopologicalGroup.toContinuousInv | inst₁, inst₂ | @IsTopologicalGroup G inst₁ inst₂ | ✓ |
Semiring.toNatAlgebra | (none) | @Semiring R | ✓ |
ULift.addLeftCancelMonoid | (none) | @AddLeftCancelMonoid α | ✓ |
Lex.instIsRightCancelAdd | inst₁ | @IsRightCancelAdd α inst₁ | ✓ |
Matrix.isScalarTower | inst₁, inst₂, inst₃ | @IsScalarTower R S α inst₁ inst₃ inst₂ | ✓ |
IsNoetherianRing.wfDvdMonoid | inst₁, inst₂ | @IsNoetherianRing R (CommSemiring.toSemiring R inst₁) | ✗ |
¹Class-typed args of the declaration other than the source.
instance IsTopologicalGroup.toContinuousInv {G : Type*} {inst₁ : TopologicalSpace G}
{inst₂ : Group G} [self : @IsTopologicalGroup G inst₁ inst₂] : ContinuousInv G
instance Semiring.toNatAlgebra {R : Type*} [inst : @Semiring R] : Algebra ℕ R
instance ULift.addLeftCancelMonoid {α : Type*}
[inst : @AddLeftCancelMonoid α] : AddLeftCancelMonoid (ULift α)
instance Lex.instIsRightCancelAdd {α : Type*} [inst₁ : Add α]
[inst₂ : @IsRightCancelAdd α inst₁] : IsRightCancelAdd (Lex α)
instance Matrix.isScalarTower {m n R S α : Type*}
[inst₁ : SMul R S] [inst₂ : SMul R α] [inst₃ : SMul S α]
[inst₄ : @IsScalarTower R S α inst₁ inst₃ inst₂] : IsScalarTower R S (Matrix m n α)
instance IsNoetherianRing.wfDvdMonoid {R : Type u_1} [inst₁ : CommSemiring R]
[inst₂ : @IsDomain R (@CommSemiring.toSemiring R inst₁)]
[inst₃ : @IsNoetherianRing R (@CommSemiring.toSemiring R inst₁)] : WfDvdMonoid R
Equations
- One or more equations did not get rendered due to their size.
Instances For
Processes a declaration into an edge for the class graph, if appropriate.
Examples (each edge below abbreviates its src/tgt Vertex to that vertex's class name)
Single-premise instance declaration. One of the forgetful instances automatically generated by
class Monoid (M : Type u) extends Semigroup M, MulOneClass M, NPow M:-- instance Monoid.toSemigroup {α} [Monoid α] : Semigroup α extractEdge? `Monoid.toSemigroup = some { src := `Monoid, tgt := `Semigroup }Multi-premise instance declaration yielding an edge. Suppose we call
extractEdge?on the following instance declaration:instance Algebra.toModule {R A} {_ : CommSemiring R} {_ : Semiring A} [Algebra R A] : Module R ADespite having multiple premises, this would lead to an edge being extracted:
extractEdge? `Algebra.toModule = some { src := `Algebra, tgt := `Module }The reason for this is that the class
Algebra R Arequires that instances[CommSemiring R]and[Semiring A]be provided. In fact,Algebra R Ais hiding these instance implicit arguments; the fully explicit version would be@Algebra R A instCommSemiringR instSemiringA. This is established in the class's declaration:class Algebra (R : Type u) (A : Type v) [CommSemiring R] [Semiring A] extends SMul R A whereAccordingly, whenever we encounter a class hypothesis
[Algebra R A]in a theorem, we're actually seeing@Algebra R A instCommSemiringR instSemiringA, meaning that the{_ : CommSemiring R}and{_ : Semiring A}premises ofAlgebra.toModuleare satisfied.Instance declaration not yielding any edge because the carrier changes.
-- instance Prod.instMonoid [Monoid M] [Monoid N] : Monoid (M × N) extractEdge? `Prod.instMonoid = none -- conclusion is over a new carrier
Equations
- One or more equations did not get rendered due to their size.
Instances For
Build #
The class graph.
Array of edges. This is what defines the class graph.
trueonly if all applications of the vertex's class are subsingletons; in particular,truefor everyProp-valued class in the graph.ClassGraph.assembledecides this per class, and only attempts synthesis for classes insubHeads, sofalseis not a negative verdict.- condensation : Digraph.Condensation Vertex
Condensation of the class graph.
Instances For
Scans every name in names (which is expected to be a list of instance declarations), collecting
weakening edges and taking note of classes that may be subsingletons as it goes.
Implementation notes
This is quite expensive, taking several seconds, and so we try to run it as seldomly as possible.
Examples (edges abbreviated as in extractEdge?)
-- instance Monoid.toSemigroup {α} [Monoid α] : Semigroup α
-- instance Fintype.subsingleton (α : Type*) : Subsingleton (Fintype α)
-- instance Prod.instMonoid [Monoid M] [Monoid N] : Monoid (M × N)
ClassGraph.scanInstances #[`Monoid.toSemigroup, `Fintype.subsingleton, `Prod.instMonoid] =
(#[{ src := `Monoid, tgt := `Semigroup }], {`Fintype})
ClassGraph.scanInstances #[] = (#[], {})
Equations
- One or more equations did not get rendered due to their size.
Instances For
Assembles weakening edges and heads of subsingleton classes into a ClassGraph.
Implementation notes
This function is relatively fast compared to ClassGraph.scanInstances, so we re-run it on local
rebuilds.
Equations
- One or more equations did not get rendered due to their size.