Source file inductive.ml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
(** Generation of induction principles.
We only consider first-order dependent types (no functional
arguments). Polymorphic types can be encoded by using codes. In case of a
mutually defined types, we generate an induction for each type. A single
induction principle can be defined from those individual induction
principles by using a conjunction operator.
In the OCaml code, the prefix "ind" is used for inductive types. The prefix
"rec" is used for recursors, aka induction principles. *)
open Lplib
open Timed
open Common open Pos open Error
open Core open Term open Print
open Parsing open Syntax
(** Logging function for generating of inductive principle. *)
let log_ind = Logger.make 'g' "indu" "induction principles generation"
let log_ind = log_ind.pp
(** Type for inductive type definitions. *)
type inductive = (sym * sym list) list
(** Builtin configuration for induction. *)
type config =
{ symb_Prop : sym (** : TYPE. Type of propositions. *)
; symb_prf : sym (** : Prop → TYPE.
Interpretation of propositions as types. *) }
(** [get_config ss pos] build the configuration using [ss]. *)
let get_config : Sig_state.t -> Pos.popt -> config = fun ss pos ->
let builtin = Builtin.get ss pos in
{ symb_Prop = builtin "Prop"
; symb_prf = builtin "P" }
(** [prf_of p c ts t] returns the term [c.symb_prf (p t1 ... tn t)] where ts =
[ts1;...;tsn]. *)
let prf_of : config -> tvar -> tbox list -> tbox -> tbox = fun c p ts t ->
_Appl_Symb c.symb_prf [_Appl (_Appl_list (_Vari p) ts) t]
(** compute safe prefixes for predicate and constructor argument variables. *)
let gen_safe_prefixes : inductive -> string * string * string =
let letter c = match c with 'a' | 'p' | 'x' -> true | _ -> false in
fun ind_list ->
let clashing_names =
let add_name_from_sym set sym =
let s = sym.sym_name in
if s <> "" && letter s.[0] then Extra.StrSet.add s set else set
in
let add_names_from_ind set (ind_sym, cons_sym_list) =
let set = add_name_from_sym set ind_sym in
List.fold_left add_name_from_sym set cons_sym_list
in
List.fold_left add_names_from_ind Extra.StrSet.empty ind_list
in
let a_str = Extra.get_safe_prefix "a" clashing_names in
let p_str = Extra.get_safe_prefix "p" clashing_names in
let x_str = Extra.get_safe_prefix "x" clashing_names in
a_str, p_str, x_str
(** Type of maps associating to every inductive type some data useful for
generating the induction principles. *)
type data = { ind_var : tvar (** predicate variable *)
; ind_type : tbox (** predicate variable type *)
; ind_conclu : tbox (** induction principle conclusion *) }
type ind_pred_map = (sym * data) list
(** [ind_typ_with_codom pos ind_sym env codom x_str a] assumes that [a] is of
the form [Π(i1:a1),...,Π(in:an), TYPE]. It then generates a [tbox] similar
to this type except that [TYPE] is replaced by [codom [i1;...;in]]. The
string [x_str] is used as prefix for the variables [ik]. *)
let ind_typ_with_codom :
popt -> sym -> Env.t -> (tbox list -> tbox) -> string -> term -> tbox =
fun pos ind_sym env codom x_str a ->
let rec aux : tvar list -> int -> term -> tbox = fun xs k a ->
match get_args a with
| (Type, _) -> codom (List.rev_map _Vari xs)
| (Prod(a,b), _) ->
let (x,b) = LibTerm.unbind_name (x_str ^ string_of_int k) b in
_Prod (lift a) (Bindlib.bind_var x (aux (x::xs) (k+1) b))
| _ -> fatal pos "The type of %a is not supported" sym ind_sym
in
aux (List.map (fun (_,(v,_,_)) -> v) env) 0 a
(** [create_ind_pred_map pos c arity ind_list a_str p_str x_str] builds an
[ind_pred_map] from [ind_list]. The resulting list is in reverse order wrt
[ind_list]. [a_str] is used as prefix for parameter names, [p_str] is used
as prefix for predicate variable names, and [x_str] is used as prefix for
the names of the variable arguments of predicate variables. *)
let create_ind_pred_map :
popt -> config -> int -> inductive -> string -> string -> string
-> tvar array * Env.t * ind_pred_map =
fun pos c arity ind_list a_str p_str x_str ->
let vs = Array.init arity (new_tvar_ind a_str) in
let env =
match ind_list with
| [] -> assert false
| (ind_sym, _) :: _ -> fst (Env.of_prod_using [] vs !(ind_sym.sym_type))
in
let create_sym_pred_data i (ind_sym,_) =
let ind_var = new_tvar_ind p_str i in
let codom ts = _Impl (_Appl_Symb ind_sym ts) (_Symb c.symb_Prop) in
let a = snd (Env.of_prod_using [] vs !(ind_sym.sym_type)) in
let ind_type = ind_typ_with_codom pos ind_sym env codom x_str a in
let codom ts =
let x = new_tvar x_str in
let t = Bindlib.bind_var x
(prf_of c ind_var (List.remove_heads arity ts) (_Vari x)) in
_Prod (_Appl_Symb ind_sym ts) t
in
let ind_conclu = ind_typ_with_codom pos ind_sym env codom x_str a in
(ind_sym, {ind_var; ind_type; ind_conclu})
in
vs, env, List.rev_mapi create_sym_pred_data ind_list
(** [fold_cons_type] generates some value of type ['c] by traversing the
structure of [cons_sym.sym_type] and accumulating some data of type ['a].
[pos] is the position of the inductive command.
[ind_pred_map] is defined above.
[x_str] is a string used as prefix for generating variable names when
deconstructing a product with [LibTerm.unbind_name].
[ind_sym] is an inductive type symbol of [ind_pred_map].
[cons_sym] is a constructor symbol of [ind_sym].
[inj_var] injects traversed bound variables into the type ['var].
[init] is the initial value of type ['a].
The traversal is made by the function [fold : (xs : 'var list) -> (acc :
'a) -> term -> 'c] below. It keeps track of the variables [xs] we went
through (the last variable comes first) and some accumulator [acc] set to
[init] at the beginning.
During the traversal, there are several possible cases:
1) If the argument is a product of the form [Π x:t, u] with [t] of the form
[Π y₁:a₁, …, Π yₙ:aₙ, s ts] and [s] mapped to [p] in [ind_pred_map], then
the result is [rec_dom t x' v next] where [x' = inj_var (length xs) x], [v
= aux env s p ts x'], [env = y₁:a₁; …; yₙ:aₙ] and [next = fold (x'::xs)
acc' u] is the result of the traversal of [u] with the list of variables
extended with [x] and the accumulator [acc' = acc_rec_dom acc x' v].
2) If the type argument is a product [Πx:t, u] not of the previous form,
then the result is [nonrec_dom t x' next] where [next = fold (x'::xs) acc'
u] and [acc' = acc_nonrec_dom acc x'].
3) If the type argument is of the form [ind_sym ts], then the result is
[codom ts xs acc].
4) Any other case is an error. *)
let fold_cons_type
(pos : popt)
(ind_pred_map : ind_pred_map)
(x_str : string)
(ind_sym : sym)
(vs : tvar array)
(cons_sym : sym)
(inj_var : int -> tvar -> 'var)
(init : 'a)
(aux : Env.t -> sym -> tvar -> term list -> 'var -> 'aux)
(acc_rec_dom : 'a -> 'var -> 'aux -> 'a)
(rec_dom : term -> 'var -> 'aux -> 'c -> 'c)
(acc_nonrec_dom : 'a -> 'var -> 'a)
(nonrec_dom : term -> 'var -> 'c -> 'c)
(codom : 'var list -> 'a -> tvar -> term list -> 'c)
: 'c =
let rec fold : 'var list -> int -> 'a -> term -> 'c = fun xs n acc t ->
match get_args t with
| (Symb(s), ts) ->
if s == ind_sym then
let d = List.assq ind_sym ind_pred_map in
codom xs acc d.ind_var ts
else fatal pos "%a is not a constructor of %a"
sym cons_sym sym ind_sym
| (Prod(t,u), _) ->
let x, u = LibTerm.unbind_name (x_str ^ string_of_int n) u in
let x = inj_var (Array.length vs + n) x in
begin
let env, b = Env.of_prod [] "y" t in
match get_args b with
| (Symb(s), ts) ->
begin
match List.assq_opt s ind_pred_map with
| Some d ->
let v = aux env s d.ind_var ts x in
let next = fold (x::xs) (n+1) (acc_rec_dom acc x v) u in
rec_dom t x v next
| None ->
let next = fold (x::xs) (n+1) (acc_nonrec_dom acc x) u in
nonrec_dom t x next
end
| _ -> fatal pos "The type of %a is not supported" sym cons_sym
end
| _ -> fatal pos "The type of %a is not supported" sym cons_sym
in
let _, t = Env.of_prod_using [] vs !(cons_sym.sym_type) in
fold (List.rev_mapi inj_var (Array.to_list vs)) 0 init t
(** [gen_rec_type c pos ind_list vs env ind_pred_map x_str] generates the
induction principles for each type in the inductive definition [ind_list]
defined at the position [pos] whose parameters are [vs]. [x_str] is
a string used for prefixing variable names that are generated. Remark: in
the generated induction principles, each recursive argument is followed by
its induction hypothesis. For instance, with [inductive T:TYPE := c:
T->T->T], we get [ind_T: Π p:T -> Prop, (Π x₀:T, π(p x₀)-> Π x₁:T, π(p
x₁)-> π(p (c x₀ x₁)) -> Π x:T, π(p x)]. *)
let gen_rec_types :
config -> popt -> inductive
-> tvar array -> Env.t -> ind_pred_map -> string -> term list =
fun c pos ind_list vs env ind_pred_map x_str ->
let n = Array.length vs in
let case_of : sym -> sym -> tbox = fun ind_sym cons_sym ->
let inj_var _ x = x in
let init = () in
let aux env _ p ts x =
let v = Env.appl (_Vari x) env in
let v = prf_of c p (List.map lift (List.remove_heads n ts)) v in
Env.to_prod_box env v
in
let acc_rec_dom _ _ _ = () in
let rec_dom t x v next =
_Prod (lift t) (Bindlib.bind_var x (_Impl v next))
in
let acc_nonrec_dom _ _ = () in
let nonrec_dom t x next = _Prod (lift t) (Bindlib.bind_var x next) in
let codom xs _ p ts =
prf_of c p (List.map lift (List.remove_heads n ts))
(_Appl_Symb cons_sym (List.rev_map _Vari xs))
in
fold_cons_type pos ind_pred_map x_str ind_sym vs cons_sym inj_var
init aux acc_rec_dom rec_dom acc_nonrec_dom nonrec_dom codom
in
let gen_rec_type (_, d) =
let add_clause_cons ind_sym cons_sym t =
_Impl (case_of ind_sym cons_sym) t
in
let add_clauses_ind (ind_sym, cons_sym_list) t =
List.fold_right (add_clause_cons ind_sym) cons_sym_list t
in
let rec_typ = List.fold_right add_clauses_ind ind_list d.ind_conclu in
let add_quantifier t (_,d) =
_Prod d.ind_type (Bindlib.bind_var d.ind_var t) in
let rec_typ = List.fold_left add_quantifier rec_typ ind_pred_map in
Bindlib.unbox (Env.to_prod_box env rec_typ)
in
List.map gen_rec_type ind_pred_map
(** [rec_name ind_sym] returns the name of the induction principle associated
to the inductive type symbol [ind_sym]. *)
let rec_name ind_sym = Escape.add_prefix "ind_" ind_sym.sym_name
(** [iter_rec_rules pos ind_list vs rec_sym_list ind_pred_map] generates the
recursor rules for the inductive type definition [ind_list] as position
[pos] with parameters [vs], recursors are [rec_sym_list] and
[ind_pred_map].
For instance, [inductive T : Π(i1:A1),..,Π(im:Am), TYPE := c1:T1 | .. |
cn:Tn] generates a rule for each constructor. If [Ti = Π x1:B1,..,Π
xk:Bk,T] then the rule for ci is [ind_T p pc1 .. pcn _ .. _ (ci x1 .. xk)
--> pci x1 t1? ... xk tk?] with m underscores, [tj? = ind_T p pc1 .. pcn _
.. _ xj] if [Bj = T v1 ... vm], and nothing otherwise. *)
let iter_rec_rules :
popt -> inductive -> tvar array -> ind_pred_map
-> (p_rule -> unit) -> unit =
fun pos ind_list vs ind_pred_map f ->
let rules_pos = shift (List.length ind_list + 1) (end_pos pos) in
let n = Array.length vs in
let case_arg_name cons_sym = cons_sym.sym_name in
let arec : sym -> term list -> p_term -> p_term = fun sym_ind ts t ->
let apatt t n = P.appl t (P.patt0 n) in
let head = P.iden (rec_name sym_ind) in
let head = P.appl_wild head n in
let head =
let apred (_,d) t = apatt t (Bindlib.name_of d.ind_var) in
List.fold_right apred ind_pred_map head
in
let acase t cons_sym = apatt t (case_arg_name cons_sym) in
let acases t (_, cons_sym_list) =
List.fold_left acase t cons_sym_list
in
let head = List.fold_left acases head ind_list in
P.appl (P.appl_wild head (List.length ts - n)) t
in
let gen_rule_cons : sym -> sym -> p_rule = fun ind_sym cons_sym ->
let inj_var n _ = P.patt0 ("x" ^ string_of_int n) in
let init = P.patt0 (case_arg_name cons_sym) in
let aux env s _ ts x =
let env_appl t env =
List.fold_right (fun (_,(x,_,_)) t -> P.appl t (P.var x)) env t in
let add_abst t (_,(x,_,_)) =
P.abst (Some (Pos.none (Bindlib.name_of x))) t in
List.fold_left add_abst (arec s ts (env_appl x env)) env
in
let acc_rec_dom acc x aux = P.appl (P.appl acc x) aux in
let rec_dom _ _ _ next = next in
let acc_nonrec_dom a x = P.appl a x in
let nonrec_dom _ _ next = next in
let codom xs rhs _ ts =
let cons_arg = P.appl_list (P.iden cons_sym.sym_name) (List.rev xs) in
Pos.make rules_pos (arec ind_sym ts cons_arg, rhs)
in
fold_cons_type pos ind_pred_map "" ind_sym vs cons_sym inj_var
init aux acc_rec_dom rec_dom acc_nonrec_dom nonrec_dom codom
in
let g (ind_sym, cons_sym_list) =
List.iter (fun cons_sym -> f (gen_rule_cons ind_sym cons_sym))
cons_sym_list
in
List.iter g ind_list