-
Notifications
You must be signed in to change notification settings - Fork 57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Created rewrite tactic #746
Changes from 6 commits
e00ff5b
a04f24a
041fcf4
f6ff17b
9ef2af9
0873629
ea72a39
c3df3b1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
From elpi.apps Require Import eltac.rewrite. | ||
|
||
Goal (forall x : nat, 1 + x = x + 1) -> | ||
forall y, 2 * ((y+y) + 1) = ((y + y)+1) * 2. | ||
Proof. | ||
intro H. | ||
intro x. | ||
eltac.rewrite H. | ||
eltac.rewrite PeanoNat.Nat.mul_comm. | ||
exact eq_refl. | ||
Defined. | ||
|
||
Section Example_rewrite. | ||
Variable A : Type. | ||
Variable B : A -> Type. | ||
Variable C : forall (a : A) (b : B a), Type. | ||
Variable add : forall {a : A} {b : B a}, C a b -> C a b -> C a b. | ||
Variable sym : forall {a : A} {b : B a} (c c' : C a b), add c c' = add c' c. | ||
|
||
Goal forall (a : A) (b : B a) (x y : C a b), | ||
add x y = add y x /\ add x y = add y x. | ||
Proof. | ||
intros a b x y. | ||
eltac.rewrite @sym. (* @sym is a gref *) | ||
(** [add y x = add y x /\ add y x = add y x] *) | ||
easy. | ||
Defined. | ||
|
||
Goal forall (a : A) (b : B a) (x y : C a b), | ||
add x y = add y x /\ add x y = add y x. | ||
Proof. | ||
intros a b x y. | ||
eltac.rewrite sym. (* because of implicit arguments, this is sym _ _, which is a term *) | ||
easy. | ||
Defined. | ||
|
||
End Example_rewrite. |
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,50 @@ | ||||||||||
From elpi Require Export elpi. | ||||||||||
|
||||||||||
Elpi Tactic rewrite. | ||||||||||
Elpi Accumulate lp:{{ | ||||||||||
% Second argument is a type of the form forall x1 x2 x3... P = Q. | ||||||||||
% First argument is a term of that type. | ||||||||||
% This tactic finds a subterm of the goal that Q unifies with | ||||||||||
% and rewrites all instances of that subterm from right to left. | ||||||||||
pred nested_forall i:term, i:term, o:goal, o:list sealed-goal. | ||||||||||
|
||||||||||
% The copy predicate used below is discussed in the tutorial here: | ||||||||||
% https://lpcic.github.io/coq-elpi/tutorial_coq_elpi_tactic.html#let-s-code-set-in-elpi | ||||||||||
|
||||||||||
nested_forall Eqpf {{@eq lp:S lp:P lp:Q }} (goal _ _ GoalType _ _ as G) GL :- | ||||||||||
% First, introduce a rule that causes "copy" to act as a function | ||||||||||
% sending a type T to the same type, but with all | ||||||||||
% subterms of T unifiable with Q to be replaced with a fresh constant x. | ||||||||||
pi x\ (pi J\ copy J x :- coq.unify-leq Q J ok) => | ||||||||||
% Apply this copy function to the goal type. | ||||||||||
(copy GoalType (A x), | ||||||||||
% If the subterm Q did indeed appear in the goal, | ||||||||||
% then pattern match on the given equality assumption P = Q, | ||||||||||
% causing Q to be replaced with P everywhere. | ||||||||||
if (occurs x (A x)) | ||||||||||
(refine (match | ||||||||||
Eqpf | ||||||||||
(fun _ S (a\ | ||||||||||
fun _ {{ @eq lp:S lp:P lp:Q }} (_\ A a ) | ||||||||||
)) | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Also shouldn't Q be a ? |
||||||||||
% We only want to create one hole, | ||||||||||
% the one corresponding to the | ||||||||||
% contents of the (single) branch of the match. | ||||||||||
[Hole_]) | ||||||||||
G GL | ||||||||||
) | ||||||||||
(coq.ltac.fail _ "Couldn't unify.")). | ||||||||||
|
||||||||||
solve (goal Ctx _ _ _ [trm Eq] as G) GL :- ( | ||||||||||
% Eq is a direct Gallina term or a gref and we will infer its type | ||||||||||
% from context | ||||||||||
coq.typecheck Eq Ty ok; | ||||||||||
% Eq is a reference to a declared variable in the context | ||||||||||
std.mem Ctx (decl Eq _ Ty)), | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The LHS of |
||||||||||
coq.saturate Ty Eq Eq', | ||||||||||
coq.typecheck Eq' Ty' ok, | ||||||||||
nested_forall Eq' Ty' G GL. | ||||||||||
}}. | ||||||||||
|
||||||||||
Tactic Notation "eltac.rewrite" ident(T) := elpi rewrite ltac_term:(T). | ||||||||||
Tactic Notation "eltac.rewrite" uconstr(T) := elpi rewrite ltac_term:(T). |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
From elpi.apps.eltac Require Export | ||
intro | ||
rewrite | ||
constructor | ||
assumption | ||
discriminate | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks like a good introduction to the power of copy etc, thanks for providing it.
But this implementation of rewrite is known to be a "bad rewrite" since it is too smart, eg lets you rewrite with commutativity of addition on a goal like
2 * x = ..
that has no addition in it. I recommend at least writing a comment about this, or write a more complex line that compares the key verbatim, see forexample
coq-elpi/elpi/elpi_elaborator.elpi
Lines 173 to 177 in c40e913