I haven't touched Elpi in a year or so, I am trying to re-learn it. I wrote a basic rewrite tactic. I was wondering if anyone could offer constructive criticism about the code, or suggestions, more idiomatic ways of doing things, etc.
Elpi Tactic rewrite.
(** Argument either the name of a hypothesis in the context,
or the name of a global definition (variables count as global references
as far as Elpi is concerned, although you can get a list of
the section-local variables by calling coq.env.section.
The declared type of the argument should be type is of the form
[forall (x1 : A1) (x2 : A2) ... P = Q]
Unifies Q with a subterm of the goal and rewrites right to left.
*)
Elpi Accumulate lp:{{
pred nested_forall i:term, i:term, o:goal, o:list sealed-goal.
nested_forall Eqpf {{@eq lp:S lp:P lp:Q }} (goal _ _ GoalType _ _ as G) GL :-
pi x\ (copy J x :- coq.unify-leq Q J ok) =>
copy GoalType (A x),
if (occurs x (A x))
(refine (match
Eqpf
(fun _ S (a\
fun _ {{ @eq lp:S lp:P lp:Q }} (_\ A a )
))
% We only want to create one hole,
% the one corresponding to the
% contents of the (single) branch of the match.
[{{_}}])
G GL
)
(coq.ltac.fail _ "Couldn't unify.").
nested_forall Eqpf (prod _ A B) G GL :-
Hole = {{ _ }},
coq.typecheck Hole A ok,
% This might not make progress if
% Eqpf is opaque or an assumption.
whd (app [Eqpf, Hole]) [] HD ARGS,
unwind HD ARGS Eqpf',
nested_forall Eqpf' (B Hole) G GL.
solve (goal Ctx _ _ _ [trm Eq] as G) GL :- (
% Eq is a direct reference to a global definition or axiom
Eq = global Gref, coq.env.typeof Gref Ty;
% Eq is a direct Gallina term 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)),
nested_forall Eq Ty G GL.
}}.
Elpi Typecheck.
I didn't see a rewrite tactic in the coq-elpi/examples folder on Github, so it might be nice to polish this up a bit and submit it to the examples folder.
Hello, yes this code code should definitely go in the apps/eltac subdirectory.
A few comments here, but I can be more precise if you open a PR:
coq.typecheck
works for any term, no need to check if Eq is a proof variable or a gref_
istead of {{ _ }}
, holes in Elpi are holes in CoqThe copy clause needs to be copied on J? You mean I should write pi x j\ ...
? I don't think I understand that, I thought quantified variables behave like constants. How would GoalType be unified with the constant j
You should do something like: pi x\ (pi J\ copy J x :- coq.unif-leq Q J ok) =>
this way the J
variable is fresh each time the new copy rule is used.
I also suggest to put a bang after the call to unify-leq, otherwise backtracking may cause perf issues if a subsequent premise fails
Is there a semantic difference between pi j\
and pi J\
they have same semantic
Okay. I think I get it now. Higher order clauses are just a little novel
Last updated: Oct 13 2024 at 01:02 UTC