Stream: Elpi users & devs

Topic: Code review for homemade rewrite


view this post on Zulip Patrick Nicodemus (Aug 26 2024 at 01:48):

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.

view this post on Zulip Patrick Nicodemus (Aug 26 2024 at 01:49):

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.

view this post on Zulip Patrick Nicodemus (Aug 26 2024 at 01:52):

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.

view this post on Zulip Enrico Tassi (Aug 26 2024 at 13:36):

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:

view this post on Zulip Patrick Nicodemus (Aug 26 2024 at 14:04):

The 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

view this post on Zulip Davide F (Aug 26 2024 at 15:53):

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

view this post on Zulip Patrick Nicodemus (Aug 26 2024 at 16:15):

Is there a semantic difference between pi j\ and pi J\

view this post on Zulip Davide F (Aug 26 2024 at 16:49):

they have same semantic

view this post on Zulip Patrick Nicodemus (Aug 26 2024 at 17:02):

Okay. I think I get it now. Higher order clauses are just a little novel


Last updated: Oct 13 2024 at 01:02 UTC