feat(library/init/meta): support nesting for coinductive predicates
This commit is contained in:
parent
3e6c7efd48
commit
652cbee425
2 changed files with 143 additions and 31 deletions
|
|
@ -4,7 +4,7 @@ Released under Apache 2.0 license as described in the file LICENSE.
|
|||
Author: Johannes Hölzl (CMU)
|
||||
-/
|
||||
prelude
|
||||
import init.meta.expr init.meta.tactic init.meta.constructor_tactic
|
||||
import init.meta.expr init.meta.tactic init.meta.constructor_tactic init.meta.attribute
|
||||
|
||||
namespace name
|
||||
|
||||
|
|
@ -98,6 +98,87 @@ meta def elim_gen_sum (n : nat) (e : expr) : tactic (list expr) := do
|
|||
set_goals ((gs.taken n).reverse ++ gs.dropn n),
|
||||
return $ hs.reverse ++ [h']
|
||||
|
||||
end tactic
|
||||
|
||||
section
|
||||
universe u
|
||||
|
||||
def monotonicity := { user_attribute . name := `monotonicity, descr := "Monotonicity rules for predicates" }
|
||||
run_cmd register_attribute `monotonicity
|
||||
|
||||
lemma monotonicity.pi {α : Sort u} {p q : α → Prop} (h : ∀a, implies (p a) (q a)) :
|
||||
implies (Πa, p a) (Πa, q a) :=
|
||||
take h' a, h a (h' a)
|
||||
|
||||
lemma monotonicity.imp {p p' q q' : Prop} (h₁ : implies p' q') (h₂ : implies q p) :
|
||||
implies (p → p') (q → q') :=
|
||||
take h, h₁ ∘ h ∘ h₂
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.const (p : Prop) : implies p p := id
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.true (p : Prop) : implies p true := take _, trivial
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.false (p : Prop) : implies false p := false.elim
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.exists {α : Sort u} {p q : α → Prop} (h : ∀a, implies (p a) (q a)) :
|
||||
implies (∃a, p a) (∃a, q a) :=
|
||||
exists_imp_exists h
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.and {p p' q q' : Prop} (hp : implies p p') (hq : implies q q') :
|
||||
implies (p ∧ q) (p' ∧ q') :=
|
||||
and.imp hp hq
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.or {p p' q q' : Prop} (hp : implies p p') (hq : implies q q') :
|
||||
implies (p ∨ q) (p' ∨ q') :=
|
||||
or.imp hp hq
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.not {p q : Prop} (h : implies p q) :
|
||||
implies (¬ q) (¬ p) :=
|
||||
mt h
|
||||
|
||||
end
|
||||
|
||||
namespace tactic
|
||||
open expr tactic
|
||||
|
||||
/- TODO: use backchaining -/
|
||||
private meta def mono_aux (ns : list name) (hs : list expr) : tactic unit := do
|
||||
intros,
|
||||
(do
|
||||
`(implies %%p %%q) ← target,
|
||||
(do is_def_eq p q, applyc `monotone.const) <|>
|
||||
(do
|
||||
(expr.pi pn pbi pd pb) ← return p,
|
||||
(expr.pi qn qbi qd qb) ← return q,
|
||||
sort u ← infer_type pd,
|
||||
(do is_def_eq pd qd,
|
||||
let p' := expr.lam pn pbi pd pb,
|
||||
let q' := expr.lam qn qbi qd qb,
|
||||
apply $ (const `monotonicity.pi [u] : expr) pd p' q') <|>
|
||||
(do guard $ u = level.zero ∧ is_arrow p ∧ is_arrow q,
|
||||
let p' := pb.lower_vars 0 1,
|
||||
let q' := qb.lower_vars 0 1,
|
||||
apply $ (const `monotonicity.imp []: expr) pd p' qd q'))) <|>
|
||||
first (hs.map $ λh, apply_core h { md := transparency.none } >> skip) <|>
|
||||
first (ns.map $ λn, do c ← mk_const n, apply_core c { md := transparency.none }, skip),
|
||||
all_goals mono_aux
|
||||
|
||||
meta def mono (e : expr) (hs : list expr) : tactic unit := do
|
||||
t ← target,
|
||||
t' ← infer_type e,
|
||||
ns ← attribute.get_instances `monotonicity,
|
||||
((), p) ← solve_aux `(implies %%t' %%t) (mono_aux ns hs),
|
||||
exact (p e)
|
||||
|
||||
end tactic
|
||||
|
||||
/-
|
||||
The coinductive predicate `pred`:
|
||||
|
||||
|
|
@ -139,6 +220,7 @@ where
|
|||
lemma {u} pred.r (A) (b) : pred_i A p
|
||||
-/
|
||||
|
||||
namespace tactic
|
||||
open level expr tactic
|
||||
|
||||
namespace add_coinductive_predicate
|
||||
|
|
@ -202,7 +284,11 @@ meta def mono (pd : coind_pred) : expr :=
|
|||
(const (pd.func.const_name ++ "mono") pd.u_params).app_of_list pd.params
|
||||
|
||||
meta def rec' (pd : coind_pred) : tactic expr :=
|
||||
mk_const (pd.func.const_name ++ "rec")
|
||||
do let c := pd.func.const_name ++ "rec",
|
||||
env ← get_env,
|
||||
decl ← env.get c,
|
||||
let num := decl.univ_params.length,
|
||||
return (const c $ if num = pd.u_params.length then pd.u_params else level.zero :: pd.u_params)
|
||||
-- ^^ `rec`'s universes are not always `u_params`, e.g. eq, wf, false
|
||||
|
||||
meta def construct (pd : coind_pred) : expr :=
|
||||
|
|
@ -243,7 +329,7 @@ meta def add_coinductive_predicate
|
|||
intros ← is.mmap (λi, do
|
||||
(args, t') ← mk_local_pis i.local_type,
|
||||
(name.mk_string sub p) ← return i.local_uniq_name,
|
||||
let loc_args := args.map $ λe, (fs₁.zip preds).foldl (λ(e:expr) ⟨f, c, _⟩,
|
||||
let loc_args := args.map $ λe, (fs₁.zip preds).foldl (λ(e:expr) ⟨f, c, _⟩,
|
||||
e.replace_with (pred_g c) f) e,
|
||||
let t' := t'.replace_with (pred_g c) f₂,
|
||||
return { tactic.add_coinductive_predicate.coind_rule .
|
||||
|
|
@ -260,7 +346,7 @@ meta def add_coinductive_predicate
|
|||
intros := intros, locals := ls, params := params, u_names := u_names }),
|
||||
|
||||
/- Introduce all functionals -/
|
||||
pds.mmap (λpd:coind_pred, do
|
||||
pds.mmap' (λpd:coind_pred, do
|
||||
let func_f₁ := pd.func_g.app_of_list $ fs₁,
|
||||
let func_f₂ := pd.func_g.app_of_list $ fs₂,
|
||||
|
||||
|
|
@ -272,24 +358,24 @@ meta def add_coinductive_predicate
|
|||
(params.length + preds.length) (pd.type.pis $ params ++ fs₁) func_intros,
|
||||
|
||||
/- Prove monotonicity rule -/
|
||||
let mono_type :=
|
||||
pds.reverse.foldl (λc (pd:coind_pred), ((pd.le pd.f₁ pd.f₂).imp c).pis [pd.f₁, pd.f₂]) $
|
||||
pd.le func_f₁ func_f₂,
|
||||
pd.add_theorem (pd.func.const_name ++ "mono") mono_type [] (do
|
||||
hf ← pds.mmap (λpd, do
|
||||
[f₁, f₂, hf] ← intro_lst [pd.f₁.local_pp_name, pd.f₂.local_pp_name, `hf],
|
||||
return (f₂, hf)),
|
||||
let fs₂ := hf.map prod.fst,
|
||||
let hfs := hf.map prod.snd,
|
||||
monos ← pds.mmap (λpd, do
|
||||
h ← mk_local_def `h $ pd.le pd.f₁ pd.f₂,
|
||||
-- the type of h' reduces to h
|
||||
let h' := local_const h.local_uniq_name h.local_pp_name h.local_binder_info $
|
||||
((const `implies [] : expr) (pd.f₁.app_of_list pd.locals) (pd.f₂.app_of_list pd.locals)).pis pd.locals,
|
||||
return ([pd.f₁, pd.f₂, h], h')),
|
||||
let mono_params := monos.map prod.fst,
|
||||
let mono_rules := monos.map prod.snd,
|
||||
pd.add_theorem (pd.func.const_name ++ "mono") (pd.le func_f₁ func_f₂) mono_params.join (do
|
||||
m ← pd.rec',
|
||||
apply $ m.app_of_list params, -- somehow `induction` / `cases` doesn't work?
|
||||
focus $ func_intros.map (λ⟨n, t⟩, do
|
||||
func_intros.mmap' (λ⟨n, t⟩, solve1 $ do
|
||||
bs ← intros,
|
||||
ms ← apply_core ((const n u_params).app_of_list $ params ++ fs₂) { all := tt },
|
||||
params ← (ms.zip bs).mfilter (λ⟨m, d⟩, is_assigned m),
|
||||
focus $ params.map (λ⟨m, d⟩, apply d <|> first (hfs.map $ λ hf, apply hf >> apply d))))),
|
||||
params.mmap' (λ⟨m, d⟩, mono d mono_rules)))),
|
||||
|
||||
pds.mmap (λpd:coind_pred, do
|
||||
pds.mmap' (λpd:coind_pred, do
|
||||
let func_f := λpd:coind_pred, pd.func_g.app_of_list $ pds.map coind_pred.f₁,
|
||||
|
||||
/- define final predicate -/
|
||||
|
|
@ -307,14 +393,14 @@ meta def add_coinductive_predicate
|
|||
ls ← intro_lst $ pd.locals.map local_pp_name,
|
||||
h ← intro `h,
|
||||
whnf_target,
|
||||
fs₁.mmap existsi,
|
||||
hs.mmap (λf, constructor >> exact f),
|
||||
fs₁.mmap' existsi,
|
||||
hs.mmap' (λf, constructor >> exact f),
|
||||
exact h)),
|
||||
|
||||
let func_f := λpd : coind_pred, pd.func_g.app_of_list $ pds.map coind_pred.pred_g,
|
||||
|
||||
/- prove `destruct` rules -/
|
||||
pds.enum.mmap (λ⟨n, pd⟩, do
|
||||
pds.enum.mmap' (λ⟨n, pd⟩, do
|
||||
let destruct := pd.le pd.pred_g (func_f pd),
|
||||
pd.add_theorem (pd.pred.const_name ++ "destruct") destruct [] (do
|
||||
ls ← intro_lst $ pd.locals.map local_pp_name,
|
||||
|
|
@ -322,7 +408,7 @@ meta def add_coinductive_predicate
|
|||
(fs, h) ← elim_gen_prod pds.length h [],
|
||||
(hs, h) ← elim_gen_prod pds.length h [],
|
||||
apply $ pd.mono,
|
||||
pds.mmap (λpd:coind_pred, focus1 $ do
|
||||
pds.mmap' (λpd:coind_pred, focus1 $ do
|
||||
apply $ pd.corec_functional,
|
||||
focus $ hs.map exact),
|
||||
some h' ← return $ hs.nth n,
|
||||
|
|
@ -330,17 +416,17 @@ meta def add_coinductive_predicate
|
|||
exact h)),
|
||||
|
||||
/- prove `construct` rules -/
|
||||
pds.mmap (λpd:coind_pred,
|
||||
pds.mmap' (λpd:coind_pred,
|
||||
pd.add_theorem (pd.pred.const_name ++ "construct") (pd.le (func_f pd) pd.pred_g) [] (do
|
||||
let func_pred_g := λpd:coind_pred,
|
||||
pd.func.app_of_list $ params ++ pds.map (λpd:coind_pred, pd.pred.app_of_list params),
|
||||
apply $ pd.corec_functional.app_of_list $ pds.map func_pred_g,
|
||||
focus $ pds.map (λpd:coind_pred, do
|
||||
pds.mmap' (λpd:coind_pred, solve1 $ do
|
||||
apply pd.mono,
|
||||
focus $ pds.map (λpd, apply pd.destruct)))),
|
||||
pds.mmap' (λpd, solve1 $ apply pd.destruct)))),
|
||||
|
||||
/- prove `cases_on` rules -/
|
||||
pds.mmap (λpd:coind_pred, do
|
||||
pds.mmap' (λpd:coind_pred, do
|
||||
let C := pd.f₁.to_implicit_binder,
|
||||
h ← mk_local_def `h $ pd.pred_g.app_of_list pd.locals,
|
||||
rules ← pd.intros.mmap (λr:coind_rule, do
|
||||
|
|
@ -355,12 +441,12 @@ meta def add_coinductive_predicate
|
|||
set_basic_attribute `elab_as_eliminator cases_on.const_name),
|
||||
|
||||
/- prove `corec_on` rules -/
|
||||
pds.mmap (λpd:coind_pred, do
|
||||
pds.mmap' (λpd:coind_pred, do
|
||||
rules ← pds.mmap (λpd, do
|
||||
intros ← pd.intros.mmap (λr:coind_rule, do
|
||||
eqs ← (list.zip pd.locals r.insts).mmap (λ⟨l, i⟩, do
|
||||
sort u ← infer_type l.local_type,
|
||||
return $ (const `eq [u] : expr) l.local_type l i),
|
||||
return $ (const `eq [u] : expr) l.local_type i l),
|
||||
mk_exists_lst r.loc_args $ mk_and_lst eqs),
|
||||
mk_local_def (mk_simple_name $ "h_" ++ pd.pd_name.last_string) $
|
||||
((pd.f₁.app_of_list pd.locals).imp (mk_or_lst intros)).pis pd.locals),
|
||||
|
|
@ -386,15 +472,15 @@ meta def add_coinductive_predicate
|
|||
skip
|
||||
end),
|
||||
exact h)),
|
||||
|
||||
|
||||
/- prove constructors -/
|
||||
pds.mmap (λpd:coind_pred, pd.intros.mmap (λr,
|
||||
pds.mmap' (λpd:coind_pred, pd.intros.mmap' (λr,
|
||||
pd.add_theorem r.orig_nm r.type [] $ do
|
||||
bs ← intros,
|
||||
apply $ pd.construct,
|
||||
exact $ (const r.func_nm u_params).app_of_list $ params ++ pds.map coind_pred.pred_g ++ bs)),
|
||||
|
||||
pds.mmap (λpd:coind_pred, set_basic_attribute `irreducible pd.pd_name),
|
||||
pds.mmap' (λpd:coind_pred, set_basic_attribute `irreducible pd.pd_name),
|
||||
|
||||
try triv -- we setup a trivial goal for the tactic framework
|
||||
|
||||
|
|
|
|||
|
|
@ -3,8 +3,6 @@ import data.stream
|
|||
|
||||
universe u
|
||||
|
||||
open level expr tactic
|
||||
|
||||
coinductive all_stream {α : Type u} (s : set α) : stream α → Prop
|
||||
| step {} : ∀{a : α} {ω : stream α}, a ∈ s → all_stream ω → all_stream (a :: ω)
|
||||
|
||||
|
|
@ -17,6 +15,8 @@ coinductive all_stream {α : Type u} (s : set α) : stream α → Prop
|
|||
∀ {α : Type u} (s : set α) (C : stream α → Prop),
|
||||
(∀ (a : stream α), C a → all_stream.functional s C a) → ∀ (a : stream α), C a → all_stream s a)
|
||||
|
||||
coinductive all_stream' {α : Type u} (s : set α) : stream α → Prop
|
||||
| step {} : ∀{ω : stream α}, stream.head ω ∈ s → all_stream' (stream.tail ω) → all_stream' ω
|
||||
|
||||
|
||||
coinductive alt_stream : stream bool → Prop
|
||||
|
|
@ -55,3 +55,29 @@ with ff_stream : stream bool → Prop
|
|||
(∀ (a : stream bool), C_tt_stream a → tt_stream.functional C_tt_stream C_ff_stream a) →
|
||||
(∀ (a : stream bool), C_ff_stream a → ff_stream.functional C_tt_stream C_ff_stream a) →
|
||||
∀ (a : stream bool), C_ff_stream a → ff_stream a)
|
||||
|
||||
|
||||
mutual coinductive tt_ff_stream, ff_tt_stream
|
||||
with tt_ff_stream : stream bool → Prop
|
||||
| step {} : ∀{ω : stream bool}, tt_ff_stream ω ∨ ff_tt_stream ω → tt_ff_stream (stream.cons tt ω)
|
||||
with ff_tt_stream : stream bool → Prop
|
||||
| step {} : ∀{ω : stream bool}, ff_tt_stream ω ∨ tt_ff_stream ω → ff_tt_stream (stream.cons ff ω)
|
||||
|
||||
#print prefix tt_ff_stream
|
||||
|
||||
inductive all_list {α : Type} (p : α → Prop) : list α → Prop
|
||||
| nil : all_list []
|
||||
| cons : ∀a xs, p a → all_list xs → all_list (a :: xs)
|
||||
|
||||
@[monotonicity]
|
||||
lemma monotonicity.all_list {α : Type} {p q : α → Prop} (h : ∀a, implies (p a) (q a)) :
|
||||
∀xs, implies (all_list p xs) (all_list q xs)
|
||||
| ._ (all_list.nil ._) := all_list.nil _
|
||||
| ._ (all_list.cons a xs ha hxs) := all_list.cons _ _ (h a ha) (monotonicity.all_list _ hxs)
|
||||
|
||||
mutual coinductive walk_a, walk_b {α β : Type} (f : α → list β) (g : β → α) (p : α → Prop) (t : α → Prop)
|
||||
with walk_a : α → Prop
|
||||
| step : ∀a, all_list walk_b (f a) → p a → walk_a a
|
||||
| term : ∀a, t a → walk_a a
|
||||
with walk_b : β → Prop
|
||||
| step : ∀b, walk_a (g b) → walk_b b
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue