package lambdapi
Proof assistant for the λΠ-calculus modulo rewriting
Install
Dune Dependency
Authors
Maintainers
Sources
lambdapi-2.6.0.tbz
sha256=d01e5f13db2eaba6e4fe330667149e0059d4886c651ff9d6b672db2dfc9765ed
sha512=33b68c972aca37985ed73c527076198e7d4961c7e27c89cdabfe4d1cff97cd41ccfb85ae9499eb98ad9a0aefd920bc55555df6393fc441ac2429e4d99cddafa8
doc/src/lambdapi.handle/inductive.ml.html
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 -> (* create parameters *) let vs = Array.init arity (new_tvar_ind a_str) in let env = match ind_list with | [] -> assert false (* there must be at least one type definition *) | (ind_sym, _) :: _ -> fst (Env.of_prod_using [] vs !(ind_sym.sym_type)) in (* create the ind_pred_map *) let create_sym_pred_data i (ind_sym,_) = (* predicate variable *) let ind_var = new_tvar_ind p_str i in (* predicate type *) 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 (* predicate conclusion *) 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 (* [case_of ind_sym cons_sym] creates the clause for the constructor [cons_sym] in the induction principle of [ind_sym]. *) let case_of : sym -> sym -> tbox = fun ind_sym cons_sym -> (* 'var = tvar, 'a = unit, 'aux = unit, 'c = tbox *) (* the accumulator is not used *) let inj_var _ x = x in let init = () in (* aux computes the induction hypothesis *) 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 (* Generates an induction principle for each type. *) 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 -> (* Rules are declared after recursor declarations. *) let rules_pos = shift (List.length ind_list + 1) (end_pos pos) in let n = Array.length vs in (* variable name used for a recursor case argument *) let case_arg_name cons_sym = cons_sym.sym_name in (* [arec sym_ind ts t] generates the application of the recursor of [sym_ind] to the type parameters [ts] and the constructor argument [t]. *) let arec : sym -> term list -> p_term -> p_term = fun sym_ind ts t -> (* Note: there cannot be name clashes between pattern variable names and function symbol names since pattern variables are prefixed by $. *) let apatt t n = P.appl t (P.patt0 n) in let head = P.iden (rec_name sym_ind) in (* add a wildcard for each parameter *) let head = P.appl_wild head n in (* add a predicate variable for each inductive type *) let head = let apred (_,d) t = apatt t (Bindlib.name_of d.ind_var) in List.fold_right apred ind_pred_map head in (* add a case variable for each constructor *) 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 (* [gen_rule_cons ind_sym rec_sym cons_sym] generates the p_rule of the recursor [rec_sym] of the inductive type [ind_sym] for the constructor [cons_sym]. *) let gen_rule_cons : sym -> sym -> p_rule = fun ind_sym cons_sym -> (* 'var = p_term, 'aux = p_term, 'a = p_term, 'c = p_rule *) (* the accumulator is used to compute the RHS *) let inj_var n _ = P.patt0 ("x" ^ string_of_int n) in let init = P.patt0 (case_arg_name cons_sym) in (* aux computes the recursive call *) 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 (* Iterate [f] over every rule. *) 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
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>