This PR introduces an explicit `defeq` attribute to mark theorems that can be used by `dsimp`. The benefit of an explicit attribute over the prior logic of looking at the proof body is that we can reliably omit theorem bodies across module boundaries. It also helps with intra-file parallelism. If a theorem is syntactically defined by `:= rfl`, then the attribute is assumed and need not given explicitly. This is a purely syntactic check and can be fooled, e.g. if in the current namespace, `rfl` is not actually “the” `rfl` of `Eq`. In that case, some other syntax has be used, such as `:= (rfl)`. This is also the way to go if a theorem can be proved by `defeq`, but one does not actually want `dsimp` to use this fact. The `defeq` attribute will look at the *type* of the declaration, not the body, to check if it really holds definitionally. Because of different reduction settings, this can sometimes go wrong. Then one should also write `:= (rfl)`, if one does not want this to be a defeq theorem. (If one does then this is currently not possible, but it’s probably a bad idea anyways). The `set_option debug.tactic.simp.checkDefEqAttr true`, `dsimp` will warn if could not apply a lemma due to a missing `defeq` attribute. With `set_option backward.dsimp.useDefEqAttr.get false` one can revert to the old behavior of inferring rfl-ness based on the theorem body. Both options will go away eventually (too bad we can’t mark them as deprecated right away, see #7969) Meta programs that generate theorems (e.g. equational theorems) can use `inferDefEqAttr` to set the attribute based on the theorem body of the just created declaration. This builds on #8501 to update Init to `@[expose]` a fair amount of definitions that, if not exposed, would prevent some existing `:= rfl` theorems from being `defeq` theorems. In the interest of starting backwards compatible, I exposed these function. Hopefully many can be un-exposed later again. A mathlib adaption branch exists that includes both the meta programming fixes and changes to the theorems (e.g. changing `:= by rfl` to `:= rfl`). With the module system there is now no special handling for `defeq` theorem bodies, because we don’t look at the body anymore. The previous hack is removed. The `defeq`-ness of the theorem needs to be checked in the context of the theorem’s *type*; the error message contains a hint if the defeq check fails because of the exported context.
104 lines
3.4 KiB
Text
104 lines
3.4 KiB
Text
inductive Tree where | node : List Tree → Tree
|
||
|
||
mutual
|
||
def Tree.size : Tree → Nat
|
||
| node ts => list_size ts
|
||
|
||
def Tree.list_size : List Tree → Nat
|
||
| [] => 0
|
||
| t::ts => t.size + list_size ts
|
||
end
|
||
|
||
example : Tree.list_size (t :: ts) = t.size + Tree.list_size ts := rfl
|
||
|
||
-- If we only look at the nested type at a finite depth we don't need an auxiliary definition:
|
||
|
||
def Tree.isList : Tree → Bool
|
||
| .node [] => true
|
||
| .node [t] => t.isList
|
||
| .node _ => false
|
||
|
||
|
||
-- A nested inductive type
|
||
-- the `Bool → RTree α` prevents well-founded recursion and
|
||
-- tests support for reflexive types
|
||
|
||
inductive RTree (α : Type u) : Type u
|
||
| node : α → (Bool → RTree α) → List (RTree α) → RTree α
|
||
|
||
-- only recurse on the non-nested component
|
||
def RTree.simple_size : RTree α → Nat
|
||
| .node _x t _ts => 1 + (t true).simple_size + (t false).simple_size
|
||
|
||
/--
|
||
info: @[defeq] theorem RTree.simple_size.eq_1.{u_1} : ∀ {α : Type u_1} (_x : α) (t : Bool → RTree α) (_ts : List (RTree α)),
|
||
(RTree.node _x t _ts).simple_size = 1 + (t true).simple_size + (t false).simple_size :=
|
||
fun {α} _x t _ts => Eq.refl (RTree.node _x t _ts).simple_size
|
||
-/
|
||
#guard_msgs in
|
||
#print RTree.simple_size.eq_1
|
||
|
||
-- set_option trace.Elab.definition.structural true
|
||
|
||
-- also recurse on the nested components
|
||
#guard_msgs in
|
||
mutual
|
||
def RTree.size : RTree α → Nat
|
||
| .node _ t ts => 1 + (t true).size + (t false).size + aux_size ts
|
||
def RTree.aux_size : List (RTree α) → Nat
|
||
| [] => 0
|
||
| t::ts => t.size + aux_size ts
|
||
end
|
||
|
||
/--
|
||
info: @[defeq] theorem RTree.aux_size.eq_2.{u_1} : ∀ {α : Type u_1} (t : RTree α) (ts : List (RTree α)),
|
||
RTree.aux_size (t :: ts) = t.size + RTree.aux_size ts :=
|
||
fun {α} t ts => Eq.refl (RTree.aux_size (t :: ts))
|
||
-/
|
||
#guard_msgs in
|
||
#print RTree.aux_size.eq_2
|
||
|
||
mutual
|
||
def RTree.map (f : α → β) : RTree α → RTree β
|
||
| .node x t ts => .node (f x) (fun b => (t b).map f) (map_aux f ts)
|
||
def RTree.map_aux (f : α → β) : List (RTree α) → List (RTree β)
|
||
| [] => []
|
||
| t::ts => t.map f :: map_aux f ts
|
||
end
|
||
|
||
/--
|
||
info: @[defeq] theorem RTree.map_aux.eq_2.{u_1, u_2} : ∀ {α : Type u_1} {β : Type u_2} (f : α → β) (t : RTree α)
|
||
(ts : List (RTree α)), RTree.map_aux f (t :: ts) = RTree.map f t :: RTree.map_aux f ts :=
|
||
fun {α} {β} f t ts => Eq.refl (RTree.map_aux f (t :: ts))
|
||
-/
|
||
#guard_msgs in
|
||
#print RTree.map_aux.eq_2
|
||
|
||
|
||
inductive Vec (α : Type u) : Nat → Bool → Type u where
|
||
| empty : Vec α 0 false
|
||
| succ : α → Vec α n b → Vec α (n + 1) true
|
||
|
||
-- Now an example with indices all over the place
|
||
|
||
inductive VTree (α : Type u) : Bool → Nat → Type u
|
||
| node (b : Bool) (n : Nat) : α → (List Bool → List Nat → Vec (VTree α true 5) n b) → VTree α (!b) (n+1)
|
||
|
||
mutual
|
||
def VTree.size : VTree α b n → Nat
|
||
| .node _ _ _ f => 1 + vec_size (f [] [])
|
||
-- We have to write `VTree α true 5` here, and cannot write `VTree α b' n'` here.
|
||
-- This seems to be reasonable, cf. the type of the motives of `VTree.rec`
|
||
def VTree.vec_size : Vec (VTree α true 5) n b → Nat
|
||
| .empty => 0
|
||
| .succ t ts => t.size + vec_size ts
|
||
end
|
||
|
||
/--
|
||
info: @[defeq] theorem VTree.size.eq_1.{u_1} : ∀ {α : Type u_1} (b_2 : Bool) (n_2 : Nat) (a : α)
|
||
(f : List Bool → List Nat → Vec (VTree α true 5) n_2 b_2),
|
||
(VTree.node b_2 n_2 a f).size = 1 + VTree.vec_size (f [] []) :=
|
||
fun {α} b_2 n_2 a f => Eq.refl (VTree.node b_2 n_2 a f).size
|
||
-/
|
||
#guard_msgs in
|
||
#print VTree.size.eq_1
|