Skip to content

Commit c97df3f

Browse files
authored
Merge pull request #538 from FissoreD/class-declare-TC-command
Class declare tc command
2 parents e15e6b9 + 34fd543 commit c97df3f

File tree

10 files changed

+234
-98
lines changed

10 files changed

+234
-98
lines changed

apps/tc/README.md

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ to make instance search on coq goals.
88

99
- [The compiler](#the-compiler)
1010
- [Class compilation](#class-compilation)
11-
- [Deterministic search](#deterministic-search)
11+
- [Deterministic search](#deterministic-search)
12+
- [Hint modes](#hint-modes)
1213
- [Instance compilation](#instance-compilation)
13-
- [Instance priorities](#instance-priorities)
14+
- [Instance priorities](#instance-priorities)
1415
- [Technical details](#technical-details)
15-
- [Instance locality](#instance-locality)
16+
- [Instance locality](#instance-locality)
1617
- [Goal resolution](#goal-resolution)
1718
- [Commands](#commands)
1819
- [Flags](#flags)
@@ -71,7 +72,7 @@ $N + 1$ terms where:
7172
The set of rules allowing to add new type-class predicates in elpi are grouped
7273
in [create_tc_predicate.elpi](elpi/create_tc_predicate.elpi).
7374

74-
### Deterministic search
75+
#### Deterministic search
7576

7677
Sometimes, it could be interesting to disable the backtracking search for some
7778
type classes, for performances issues or design choices. In coq the flag
@@ -84,11 +85,8 @@ In the example below, we want the `NoBacktrack` type class not to backtrack if
8485
a solution is found.
8586

8687
```coq
87-
Class NoBacktrack (n: nat).
88-
Elpi TC.Set_deterministic NoBacktrack.
89-
(* Ideally
90-
#[backtrack(off)] Class NoBacktrack (n : nat).
91-
*)
88+
#[deterministic] TC.declare Class NoBacktrack (n: nat).
89+
9290
Class A (n: nat).
9391
9492
Instance a0 : A 0. Qed.
@@ -124,6 +122,34 @@ do-once P :- P, !.
124122
as implementation. The cut (`!`) operator is in charge to avoid backtracking on
125123
the query `tc-NoBacktrack A B`
126124

125+
#### Hint modes
126+
127+
Instance search is done looking to the arguments passed to the class. If there
128+
is an instance $I$ unifying to it, the premises of $I$ are tried to be solved to
129+
commit $I$ as the solution of the current goal (modulo backtracking). Concerning
130+
the parameters of a type class, coq type class solver allows to constrain the
131+
argument to be ground, in input or output modes (see
132+
[here](https://coq.inria.fr/refman/proofs/automatic-tactics/auto.html#coq:cmd.Hint-Mode)).
133+
We provide a similar behavior in elpi: classes represent elpi predicates where
134+
the parameters can be in input `i` or output `o` mode (see
135+
[here](https://github.com/LPCIC/elpi/blob/master/ELPI.md#modes)). We translate
136+
coq modes in the following way: `+` and `!` become `i` in elpi and `-` becomes
137+
`o` (see
138+
[here](https://github.com/FissoreD/coq-elpi/blob/c3cce183c3b2727ef82178454f0c583196ee2c21/apps/tc/elpi/create_tc_predicate.elpi#L12)).
139+
140+
In elpi we allow type classes to have at most one mode, if that mode is not
141+
defined, all parameters are considered in `o` mode. The command to be used
142+
to let elpi compile classes with modes is done via the command `TC.Declare`.
143+
144+
```coq
145+
#[mode(i, o, i)] TC.Declare Class (T1: Type) (T2: Type) (N : nat).
146+
```
147+
148+
The pragma `mode` is taken into account to make `T1` and `N` in input mode and
149+
`T2` in output mode. The command `TC.Declare` both create the class in elpi and
150+
in coq. Note that the accepted list arguments for the attribute `mode` are
151+
`i, o, +, -` and `!` with their respective meaning.
152+
127153
### Instance compilation
128154

129155
Instances are compiled in elpi from their type. In particular, since the
@@ -189,7 +215,7 @@ variables.
189215
The set of rules allowing to compile instances in elpi are grouped in
190216
[compiler.elpi](elpi/compiler.elpi).
191217
****
192-
### Instance priorities
218+
#### Instance priorities
193219

194220
To reproduce coq behavior, instances need to respect a notion of priority:
195221
sometime multiple instances can be applied on a goal, but, for sake of
@@ -265,7 +291,7 @@ get its priority (see
265291

266292
by default, once registered, the elpi program `PROG` is activated
267293

268-
### Instance locality
294+
#### Instance locality
269295

270296
The instances in the elpi database respect the locality given by the user. This
271297
is possible thanks to the attributes from
@@ -438,7 +464,17 @@ A small recap of the available elpi commands:
438464
<code>TC.AddHook G OldName NewName</code> (click to expand)
439465
</summary>
440466

441-
See [](#technical-details)
467+
See [here](#technical-details)
468+
469+
</details>
470+
471+
<details>
472+
<summary>
473+
<code>TC.Declare ClassDef</code> (click to expand)
474+
</summary>
475+
476+
See [here](#deterministic-search) and [here](#hint-modes) for respectively
477+
deterministic type class and mode declaration
442478

443479
</details>
444480

@@ -515,10 +551,7 @@ Here the list of the flags available (all of them are `off` by default):
515551
## WIP
516552

517553
1. Mode management:
518-
- Classes with a single user defined should be taken into account to use the
519-
elpi modes
520-
- Classes with multiple modes ??
554+
- Classes with multiple modes
521555
2. Clarify pattern fragment unification
522556
3. Topological sort of premises in modes are activated
523-
4. Option to disable auto compiler (maybe)
524557

apps/tc/_CoqProject.test

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
-R tests elpi.apps.tc.tests
1010
-I src
1111

12+
tests/classes_declare.v
1213

1314
# Register (de-)activation
1415
tests/register/f1.v

apps/tc/elpi/compiler.elpi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ compile-aux (prod N T F) I RevPremises ListVar [] IsPositive IsHead Clause ff :-
9898
compile-aux (F p) I L [p | ListVar] [] IsPositive IsHead (C p) _.
9999
compile-aux Ty I RevPremises ListVar [] _ IsHead Clause tt :-
100100
not (is-option-active {oTC-use-pattern-fragment-compiler}), !,
101-
std.rev RevPremises RevRHS,
101+
std.rev RevPremises Premises,
102102
coq.mk-app I {std.rev ListVar} AppInst,
103-
make-tc IsHead Ty AppInst RevRHS Clause.
103+
make-tc IsHead Ty AppInst Premises Clause.
104104
compile-aux Ty I RevPremises ListVar [] IsPositive IsHead Clause tt :- !,
105105
std.rev RevPremises Premises,
106106
coq.mk-app I {std.rev ListVar} AppInst,
Lines changed: 71 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,80 @@
11
/* license: GNU Lesser General Public License Version 2.1 or later */
22
/* ------------------------------------------------------------------------- */
3+
pred string->coq-mode i:string, o:hint-mode.
4+
string->coq-mode "bang" mode-ground :- !.
5+
string->coq-mode "plus" mode-input :- !.
6+
string->coq-mode "minus" mode-output :- !.
7+
string->coq-mode "i" mode-input :- !.
8+
string->coq-mode "o" mode-output :- !.
9+
string->coq-mode A _ :- coq.error A "is not a valid mode".
310

4-
pred bool->mode-term i:bool, o:pair argument_mode string.
5-
% TODO: here every mode is declared to O;term.
6-
% If you want to make it work as intended,
7-
% replace the output of tt with "i:term"
8-
:name "bool->mode-term"
9-
bool->mode-term tt (pr in "term").
10-
bool->mode-term ff (pr out "term").
11-
12-
pred modes->string i:list bool, o:list (pair argument_mode string).
13-
modes->string L S :-
14-
std.map L bool->mode-term S.
15-
16-
pred make-tc-modes i:int, o:list (pair argument_mode string).
17-
make-tc-modes NB_args ModesStr :-
18-
list-init NB_args (x\r\ r = ff) ModesBool,
19-
modes->string ModesBool ModesStr.
20-
21-
pred build-modes i:gref, o:list (pair argument_mode string).
22-
build-modes ClassGr Modes :-
23-
is-option-active {oTC-addModes},
24-
coq.hints.modes ClassGr "typeclass_instances" ModesProv,
25-
not (ModesProv = []),
26-
coq.hints.modes ClassGr "typeclass_instances" ModesProv,
27-
std.assert! (ModesProv = [HintModes]) "At the moment we only allow TC with one Hint Mode",
28-
std.map {std.append HintModes [mode-output]} (x\r\ if (x = mode-output) (r = ff) (r = tt)) ModesBool,
29-
modes->string ModesBool Modes.
30-
build-modes ClassGr Modes :-
11+
pred coq-mode->elpi i:hint-mode, o:pair argument_mode string.
12+
:name "coq-mode->elpi"
13+
coq-mode->elpi mode-ground (pr in "term"). % approximation
14+
coq-mode->elpi mode-input (pr in "term").
15+
coq-mode->elpi mode-output (pr out "term").
16+
17+
pred modes-of-class i:gref, o:list (pair argument_mode string).
18+
modes-of-class ClassGr Modes :-
19+
coq.hints.modes ClassGr "typeclass_instances" CoqModesList,
20+
not (CoqModesList = []),
21+
std.assert! (CoqModesList = [HintModesFst]) "At the moment we only allow TC with one Hint Mode",
22+
std.append {std.map HintModesFst coq-mode->elpi} [pr out "term"] Modes.
23+
modes-of-class ClassGr Modes :-
3124
coq.env.typeof ClassGr ClassTy,
32-
coq.count-prods ClassTy N',
33-
N is N' + 1, % Plus one for the solution
34-
make-tc-modes N Modes.
25+
N is {coq.count-prods ClassTy} + 1, % + 1 for the solution
26+
list-init N (x\r\ r = (pr out "term")) Modes.
3527

3628
pred add-class-gr i:search-mode, i:gref.
37-
add-class-gr SearchMode ClassGr :-
38-
std.assert! (coq.TC.class? ClassGr) "Only gref of type classes can be added as new predicates",
39-
if (class ClassGr _ _) true
40-
(build-modes ClassGr Modes,
41-
gref->pred-name ClassGr PredName,
29+
add-class-gr SearchMode ClassGR :-
30+
std.assert! (coq.TC.class? ClassGR) "Only gref of type classes can be added as new predicates",
31+
if (class ClassGR _ _) true
32+
(modes-of-class ClassGR Modes,
33+
gref->pred-name ClassGR PredName,
4234
coq.elpi.add-predicate "tc.db" _ PredName Modes,
43-
add-tc-db _ _ (tc-mode ClassGr Modes),
44-
@global! => coq.elpi.accumulate _ "tc.db" (clause _ _ (class ClassGr PredName SearchMode :- !))).
35+
add-tc-db _ _ (tc-mode ClassGR Modes),
36+
@global! => add-tc-db _ _ (class ClassGR PredName SearchMode)).
4537

4638
pred add-class-str i:search-mode, i:string.
47-
add-class-str SearchMode TC_Name :-
48-
coq.locate TC_Name TC_Gr,
49-
add-class-gr SearchMode TC_Gr.
39+
add-class-str SearchMode ClassStr :-
40+
coq.locate ClassStr ClassGR,
41+
add-class-gr SearchMode ClassGR.
42+
43+
% Following are predicates for TC.declare
44+
45+
pred attr->deterministic o:search-mode.
46+
attr->deterministic deterministic :- get-option "deterministic" tt, !.
47+
attr->deterministic classic.
48+
49+
pred attr->modes o:list hint-mode.
50+
attr->modes CoqModes :-
51+
get-option "mode" L,
52+
std.map L get-key-from-option RawModes,
53+
std.map RawModes string->coq-mode CoqModes, !.
54+
attr->modes [].
55+
56+
pred get-key-from-option i:prop, o:string.
57+
get-key-from-option (get-option A tt) A :- !.
58+
get-key-from-option (get-option "i" ff) "o" :- !.
59+
get-key-from-option (get-option "o" ff) "i" :- !.
60+
get-key-from-option A _ :- coq.error A "should be an option".
61+
62+
pred declare-class-in-coq i:gref.
63+
declare-class-in-coq ClassGR :-
64+
attr->modes CoqModes,
65+
if (CoqModes = []) true
66+
(@global! => coq.hints.add-mode ClassGR "typeclass_instances" CoqModes),
67+
% CAVEAT: this triggers the observer
68+
coq.TC.declare-class ClassGR,
69+
attr->deterministic SearchMode,
70+
gref->pred-name ClassGR PredName,
71+
% HACK: we override the clauses added by the observer, since it does not know
72+
% the SearchMode.
73+
@global! => add-tc-db _ (after "0") (class ClassGR PredName SearchMode :- !).
74+
75+
pred declare-class i:indt-decl.
76+
declare-class D :- !,
77+
coq.env.add-indt D I,
78+
coq.parse-attributes {attributes}
79+
[ att "mode" attlist, att "deterministic" bool ] Opts,
80+
Opts => declare-class-in-coq (indt I).

apps/tc/tests/classes_declare.v

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
From elpi.apps Require Import tc.
2+
3+
Elpi Override TC TC.Solver All.
4+
5+
(* Base test *)
6+
Section S1.
7+
8+
TC.Declare Class class1 (n : nat).
9+
10+
(* TODO: here coq can solve the goal without applying Build_class1 *)
11+
Instance inst1 : class1 3. Proof. apply Build_class1. Qed.
12+
13+
Goal exists x, class1 x. Proof. eexists. apply _. Qed.
14+
15+
End S1.
16+
17+
(* Deterministic class test *)
18+
Section S2.
19+
20+
#[deterministic] TC.Declare Class class2 (n : nat).
21+
22+
Instance inst2 : class2 1 | 0. Proof. apply Build_class2. Qed.
23+
Instance inst2' : class2 2 | 1. Proof. apply Build_class2. Qed.
24+
25+
Class aux (i: nat).
26+
27+
Instance inst_aux : forall n, class2 n -> aux n -> aux 3. Qed.
28+
Section S2'.
29+
Local Instance inst_aux' : aux 1. Qed.
30+
Goal aux 3. apply _. Qed.
31+
End S2'.
32+
33+
Section S2'.
34+
Local Instance inst_aux'' : aux 2. Qed.
35+
Goal aux 3.
36+
Proof.
37+
Succeed apply (inst_aux 2 inst2' inst_aux'').
38+
(* Note: since class2 is deterministic we cannot backtrack.
39+
The first hypothesis of inst_aux is unified to inst2,
40+
this causes `aux 2` to fail. The instance inst2' is not tried
41+
due to the deterministic class *)
42+
Fail apply _.
43+
Abort.
44+
End S2'.
45+
46+
End S2.
47+
48+
(* Mode test *)
49+
Section S3.
50+
#[mode(i)] TC.Declare Class class3 (n : nat).
51+
Instance inst3 : class3 0. Proof. apply Build_class3. Qed.
52+
53+
Goal exists x, class3 x.
54+
Proof.
55+
eexists.
56+
Succeed apply inst3.
57+
Fail apply _.
58+
Abort.
59+
60+
End S3.
61+
62+
Section S31.
63+
#[mode(o=ff)] TC.Declare Class class31 (n : nat).
64+
Instance inst31 : class31 0. Proof. apply Build_class31. Qed.
65+
66+
Goal exists x, class31 x.
67+
Proof.
68+
eexists.
69+
Succeed apply inst31.
70+
Fail apply _.
71+
Abort.
72+
73+
End S31.
74+
75+

apps/tc/theories/add_commands.v

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,21 @@ Elpi Accumulate lp:{{
9595
}}.
9696
Elpi Typecheck.
9797

98+
Elpi Command TC.Declare.
99+
Elpi Accumulate Db tc.db.
100+
Elpi Accumulate Db tc_options.db.
101+
Elpi Accumulate File base.
102+
Elpi Accumulate File tc_aux.
103+
Elpi Accumulate File create_tc_predicate.
104+
Elpi Accumulate lp:{{
105+
main [indt-decl D] :- declare-class D.
106+
main _ :- coq.error "Argument should be an inductive type".
107+
}}.
108+
Elpi Typecheck.
109+
98110
Elpi Export TC.AddAllClasses.
99111
Elpi Export TC.AddAllInstances.
100112
Elpi Export TC.AddClasses.
101113
Elpi Export TC.AddInstances.
102-
Elpi Export TC.AddHook.
114+
Elpi Export TC.AddHook.
115+
Elpi Export TC.Declare.

apps/tc/theories/db.v

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ Elpi Db tc_options.db lp:{{
2222

2323
pred oTC-debug o:list string.
2424
oTC-debug ["TC", "Debug"].
25-
26-
pred oTC-addModes o:list string.
27-
oTC-addModes ["TC", "AddModes"].
2825

2926
pred oTC-use-pattern-fragment-compiler o:list string.
3027
oTC-use-pattern-fragment-compiler ["TC", "CompilerWithPatternFragment"].

0 commit comments

Comments
 (0)