The Fintype
derive handler #
This file defines a derive handler to automatically generate Fintype
instances
for structures and inductive types.
The following is a prototypical example of what this can handle:
inductive MyOption (α : Type*)
| none
| some (x : α)
deriving Fintype
This deriving handler does not attempt to process inductive types that are either recursive or that have indices.
To get debugging information, do set_option trace.Elab.Deriving.fintype true
and set_option Elab.ProxyType true
.
There is a term elaborator derive_fintype%
implementing the derivation of Fintype
instances.
This can be useful in cases when there are necessary additional assumptions (like DecidableEq
).
This is implemented using Fintype.ofEquiv
and proxy_equiv%
, which is a term elaborator
that creates an equivalence from a "proxy type" composed of basic type constructors. If Lean
can synthesize a Fintype
instance for the proxy type, then derive_fintype%
succeeds.
Implementation notes #
There are two kinds of Fintype
instances that we generate, depending on the inductive type.
If it is an enum (an inductive type with only 0-ary constructors), then we generate the
complete List
of all constructors; see Mathlib.Deriving.Fintype.mkFintypeEnum
for more
details. The proof has $O(n)$ complexity in the number of constructors.
Otherwise, the strategy we take is to generate a "proxy type", define an equivalence between
our type and the proxy type (see proxy_equiv%
), and then use Fintype.ofEquiv
to pull a
Fintype
instance on the proxy type (if one exists) to our inductive type. For example, with
the MyOption α
type above, we generate Unit ⊕ α
. While the proxy type is not a finite type
in general, we add Fintype
instances for every type parameter of our inductive type (and
Decidable
instances for every Prop
parameter). Hence, in our example we get
Fintype (MyOption α)
assuming Fintype α
.
There is a source of quadratic complexity in this Fintype
instance from the fact that an
inductive type with n
constructors has a proxy type of the form C₁ ⊕ (C₂ ⊕ (⋯ ⊕ Cₙ))
,
so mapping to and from Cᵢ
requires looking through i
levels of Sum
constructors.
Ignoring time spent looking through these constructors, the construction of Finset.univ
contributes just linear time with respect to the cardinality of the type since the instances
involved compute the underlying List
for the Finset
as l₁ ++ (l₂ ++ (⋯ ++ lₙ))
with
right associativity.
Note that an alternative design could be that instead of using Sum
we could create a
function C : Fin n → Type*
with C i = ULift Cᵢ
and then use (i : Fin n) × C i
for
the proxy type, which would save us from the nested Sum
constructors.
This implementation takes some inspiration from the one by Mario Carneiro for Mathlib 3.
A difference is that the Mathlib 3 version does not explicitly construct the total proxy type,
and instead it opts to construct the underlying Finset
as a disjoint union of the Finset.univ
for each individual constructor's proxy type.
The term elaborator derive_fintype% α
tries to synthesize a Fintype α
instance
using all the assumptions in the local context; this can be useful, for example, if one
needs an extra DecidableEq
instance. It works only if α
is an inductive
type that proxy_equiv% α
can handle. The elaborator makes use of the
expected type, so (derive_fintype% _ : Fintype α)
works.
This uses proxy_equiv% α
, so as a side effect it defines proxyType
and proxyTypeEquiv
in
the namespace associated to the inductive type α
.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.
Instances For
Derive a Fintype
instance for enum types. These come with a toCtorIdx
function.
We generate a more optimized instance than the one produced by mkFintype
.
The strategy is to (1) create a list enumList
of all the constructors, (2) prove that this
is in toCtorIdx
order, (3) show that toCtorIdx
maps enumList
to List.range numCtors
to show
the list has no duplicates, and (4) give the Fintype
instance, using 2 for completeness.
The proofs are all linear complexity, and the main computation is that
enumList.map toCtorIdx = List.range numCtors
, which is true by refl
.
Equations
- One or more equations did not get rendered due to their size.
Instances For
Equations
- One or more equations did not get rendered due to their size.