research_rewrite tutorial
This tutorial runs the rewriting prototype of the research_rewrite package: rewrite one subterm with a local equation, check the recorded replay obligation, and simplify a connective application. The package is research-only and not part of the shipped prover, so use it to experiment, not to build on.
Quick start
Import the packages in moon.pkg:
import {
"Luna-Flow/QED/kernel",
"Luna-Flow/QED/logic",
"Luna-Flow/QED/research_rewrite",
}
Rewrite f p to f q using the local hypothesis h : p = q:
test "quick start" {
let st = @logic.install_prop_prelude(@kernel.empty_kernel_state()).unwrap()
let pre = @logic.default_prop_prelude()
let bool = @kernel.bool_ty()
let p = @kernel.mk_var("p", bool)
let q = @kernel.mk_var("q", bool)
let f = @kernel.mk_var("f", @kernel.fun_ty(bool, bool))
let locals = [("h", @kernel.mk_eq(p, q).unwrap())]
let req = @research_rewrite.research_rewrite_request(
@research_rewrite.research_local_equality("h"),
@research_rewrite.research_left_to_right(),
[@research_rewrite.research_site_comb_arg()], // the argument of f p
)
match @research_rewrite.research_rewrite_term_concl(st, pre, locals, @kernel.mk_comb(f, p), req) {
Rewritten(after, _) => inspect(@kernel.term_to_string(after), content="Comb(Var(f : fun(bool, bool)), Var(q : bool))")
_ => fail("expected a rewrite")
}
}
A request names the equation (a local by name), the direction, and the path to the subterm to replace.
Everyday tasks
Check the replay obligation
Each rewrite comes with a record of the kernel steps behind it. Validation rebuilds those steps; it fails when the context they relied on is missing:
test "obligation" {
let st = @logic.install_prop_prelude(@kernel.empty_kernel_state()).unwrap()
let pre = @logic.default_prop_prelude()
let bool = @kernel.bool_ty()
let p = @kernel.mk_var("p", bool)
let q = @kernel.mk_var("q", bool)
let f = @kernel.mk_var("f", @kernel.fun_ty(bool, bool))
let locals = [("h", @kernel.mk_eq(p, q).unwrap())]
let req = @research_rewrite.research_rewrite_request(
@research_rewrite.research_local_equality("h"),
@research_rewrite.research_left_to_right(),
[@research_rewrite.research_site_comb_arg()],
)
guard @research_rewrite.research_rewrite_term_concl(st, pre, locals, @kernel.mk_comb(f, p), req)
is Rewritten(_, obligation) else {
fail("expected a rewrite")
}
inspect(@research_rewrite.research_validate_replay_obligation(st, pre, locals, obligation) is Ok(_), content="true")
inspect(@research_rewrite.research_validate_replay_obligation(st, pre, [], obligation) is Err(_), content="true")
}
Simplify a connective
research_simplify_term_concl unfolds connective constants and β-normalises until nothing changes:
test "simplify" {
let st = @logic.install_prop_prelude(@kernel.empty_kernel_state()).unwrap()
let pre = @logic.default_prop_prelude()
let p = @kernel.mk_var("p", @kernel.bool_ty())
let not_p = @kernel.mk_comb(@kernel.ks_mk_const(st, "not").unwrap(), p) // the constant `not` applied to p
let cfg = @research_rewrite.research_simplify_config(10, true, true, [])
match @research_rewrite.research_simplify_term_concl(st, pre, [], not_p, cfg) {
Rewritten(after, obligation) => {
inspect(@kernel.term_alpha_eq(after, @logic.prop_mk_not(st, pre, p).unwrap()), content="true")
inspect(obligation.segments.length(), content="1")
}
_ => fail("expected a simplification")
}
// a variable has nothing to simplify
inspect(@research_rewrite.research_simplify_term_concl(st, pre, [], p, cfg) is NoChange, content="true")
}
See the honest failures
Unsupported requests say why they fail:
test "failures" {
let st = @logic.install_prop_prelude(@kernel.empty_kernel_state()).unwrap()
let pre = @logic.default_prop_prelude()
let p = @kernel.mk_var("p", @kernel.bool_ty())
let not_p = @kernel.mk_comb(@kernel.ks_mk_const(st, "not").unwrap(), p)
// a step limit of 0 allows no segment
let tight = @research_rewrite.research_simplify_config(0, true, true, [])
match @research_rewrite.research_simplify_term_concl(st, pre, [], not_p, tight) {
HonestFailure(msg) => inspect(msg, content="step limit exceeded")
_ => fail("expected a failure")
}
// the path asks for an abstraction where there is an application
let req = @research_rewrite.research_rewrite_request(
@research_rewrite.research_beta_normalization(),
@research_rewrite.research_left_to_right(),
[@research_rewrite.research_site_abs_body()],
)
match @research_rewrite.research_rewrite_term_concl(st, pre, [], not_p, req) {
HonestFailure(msg) => inspect(msg, content="rewrite site expected a lambda abstraction")
_ => fail("expected a failure")
}
}
Going further
Rewrite with a theorem. research_explicit_theorem(th) uses any equation theorem as the witness, for example one from @logic.logic_eq_sym. Combine it with research_right_to_left() to rewrite in the other direction.
Chain requests. Put several requests into SimplifyConfig.rewrite_requests; each round applies those that match, and the obligation records one segment per applied step.
Read the research notes. The design and the promotion gates are in research/rewrite-simplify/; the research_rewrite design summarises what is missing for promotion.
Common pitfalls
- Expecting a theorem. A rewrite returns a term and an obligation. Nothing proves the original goal.
- Rewriting under λ.
AbsBodysites are rejected. - Folding definitions.
CanonicalUnfoldandBetaNormalizationonly work left to right. - Depending on the package. It is research-only and may change or be removed.
Next steps
- The research_rewrite API lists every type and function.
- The research_rewrite design explains conversions and replay obligations.
- The tactics tutorial shows the shipped way to prove goals.