| author | haftmann | 
| Wed, 11 Mar 2009 08:45:47 +0100 | |
| changeset 30430 | 42ea5d85edcc | 
| parent 30364 | 577edc39b501 | 
| child 31723 | f5cafe803b55 | 
| permissions | -rw-r--r-- | 
| 23150 | 1 | (* Title: HOL/Tools/TFL/tfl.ML | 
| 2 | Author: Konrad Slind, Cambridge University Computer Laboratory | |
| 3 | ||
| 4 | First part of main module. | |
| 5 | *) | |
| 6 | ||
| 7 | signature PRIM = | |
| 8 | sig | |
| 9 | val trace: bool ref | |
| 10 | val trace_thms: string -> thm list -> unit | |
| 11 | val trace_cterms: string -> cterm list -> unit | |
| 12 | type pattern | |
| 13 |   val mk_functional: theory -> term list -> {functional: term, pats: pattern list}
 | |
| 14 | val wfrec_definition0: theory -> string -> term -> term -> theory * thm | |
| 15 | val post_definition: thm list -> theory * (thm * pattern list) -> | |
| 16 |    {rules: thm,
 | |
| 17 | rows: int list, | |
| 18 | TCs: term list list, | |
| 19 | full_pats_TCs: (term * term list) list} | |
| 20 | val wfrec_eqns: theory -> xstring -> thm list -> term list -> | |
| 21 |    {WFR: term,
 | |
| 22 | SV: term list, | |
| 23 | proto_def: term, | |
| 24 | extracta: (thm * term list) list, | |
| 25 | pats: pattern list} | |
| 26 | val lazyR_def: theory -> xstring -> thm list -> term list -> | |
| 27 |    {theory: theory,
 | |
| 28 | rules: thm, | |
| 29 | R: term, | |
| 30 | SV: term list, | |
| 31 | full_pats_TCs: (term * term list) list, | |
| 32 | patterns : pattern list} | |
| 33 | val mk_induction: theory -> | |
| 34 |     {fconst: term, R: term, SV: term list, pat_TCs_list: (term * term list) list} -> thm
 | |
| 35 |   val postprocess: bool -> {wf_tac: tactic, terminator: tactic, simplifier: cterm -> thm}
 | |
| 36 |     -> theory -> {rules: thm, induction: thm, TCs: term list list}
 | |
| 37 |     -> {rules: thm, induction: thm, nested_tcs: thm list}
 | |
| 38 | end; | |
| 39 | ||
| 40 | structure Prim: PRIM = | |
| 41 | struct | |
| 42 | ||
| 43 | val trace = ref false; | |
| 44 | ||
| 45 | structure R = Rules; | |
| 46 | structure S = USyntax; | |
| 47 | structure U = Utils; | |
| 48 | ||
| 49 | ||
| 50 | fun TFL_ERR func mesg = U.ERR {module = "Tfl", func = func, mesg = mesg};
 | |
| 51 | ||
| 52 | val concl = #2 o R.dest_thm; | |
| 53 | val hyp = #1 o R.dest_thm; | |
| 54 | ||
| 55 | val list_mk_type = U.end_itlist (curry (op -->)); | |
| 56 | ||
| 57 | fun enumerate xs = ListPair.zip(xs, 0 upto (length xs - 1)); | |
| 58 | ||
| 59 | fun front_last [] = raise TFL_ERR "front_last" "empty list" | |
| 60 | | front_last [x] = ([],x) | |
| 61 | | front_last (h::t) = | |
| 62 | let val (pref,x) = front_last t | |
| 63 | in | |
| 64 | (h::pref,x) | |
| 65 | end; | |
| 66 | ||
| 67 | ||
| 68 | (*--------------------------------------------------------------------------- | |
| 69 | * The next function is common to pattern-match translation and | |
| 70 | * proof of completeness of cases for the induction theorem. | |
| 71 | * | |
| 72 | * The curried function "gvvariant" returns a function to generate distinct | |
| 73 | * variables that are guaranteed not to be in names. The names of | |
| 74 | * the variables go u, v, ..., z, aa, ..., az, ... The returned | |
| 75 | * function contains embedded refs! | |
| 76 | *---------------------------------------------------------------------------*) | |
| 77 | fun gvvariant names = | |
| 78 | let val slist = ref names | |
| 79 | val vname = ref "u" | |
| 80 | fun new() = | |
| 81 | if !vname mem_string (!slist) | |
| 82 | then (vname := Symbol.bump_string (!vname); new()) | |
| 83 | else (slist := !vname :: !slist; !vname) | |
| 84 | in | |
| 85 | fn ty => Free(new(), ty) | |
| 86 | end; | |
| 87 | ||
| 88 | ||
| 89 | (*--------------------------------------------------------------------------- | |
| 90 | * Used in induction theorem production. This is the simple case of | |
| 91 | * partitioning up pattern rows by the leading constructor. | |
| 92 | *---------------------------------------------------------------------------*) | |
| 93 | fun ipartition gv (constructors,rows) = | |
| 94 | let fun pfail s = raise TFL_ERR "partition.part" s | |
| 95 |       fun part {constrs = [],   rows = [],   A} = rev A
 | |
| 96 |         | part {constrs = [],   rows = _::_, A} = pfail"extra cases in defn"
 | |
| 97 |         | part {constrs = _::_, rows = [],   A} = pfail"cases missing in defn"
 | |
| 98 |         | part {constrs = c::crst, rows,     A} =
 | |
| 99 | let val (c, T) = dest_Const c | |
| 100 | val L = binder_types T | |
| 101 | val (in_group, not_in_group) = | |
| 102 | fold_rev (fn (row as (p::rst, rhs)) => | |
| 103 | fn (in_group,not_in_group) => | |
| 104 | let val (pc,args) = S.strip_comb p | |
| 105 | in if (#1(dest_Const pc) = c) | |
| 106 | then ((args@rst, rhs)::in_group, not_in_group) | |
| 107 | else (in_group, row::not_in_group) | |
| 108 | end) rows ([],[]) | |
| 109 | val col_types = U.take type_of (length L, #1(hd in_group)) | |
| 110 | in | |
| 111 |           part{constrs = crst, rows = not_in_group,
 | |
| 112 |                A = {constructor = c,
 | |
| 113 | new_formals = map gv col_types, | |
| 114 | group = in_group}::A} | |
| 115 | end | |
| 116 |   in part{constrs = constructors, rows = rows, A = []}
 | |
| 117 | end; | |
| 118 | ||
| 119 | ||
| 120 | ||
| 121 | (*--------------------------------------------------------------------------- | |
| 122 | * Each pattern carries with it a tag (i,b) where | |
| 123 | * i is the clause it came from and | |
| 124 | * b=true indicates that clause was given by the user | |
| 125 | * (or is an instantiation of a user supplied pattern) | |
| 126 | * b=false --> i = ~1 | |
| 127 | *---------------------------------------------------------------------------*) | |
| 128 | ||
| 129 | type pattern = term * (int * bool) | |
| 130 | ||
| 131 | fun pattern_map f (tm,x) = (f tm, x); | |
| 132 | ||
| 133 | fun pattern_subst theta = pattern_map (subst_free theta); | |
| 134 | ||
| 135 | val pat_of = fst; | |
| 136 | fun row_of_pat x = fst (snd x); | |
| 137 | fun given x = snd (snd x); | |
| 138 | ||
| 139 | (*--------------------------------------------------------------------------- | |
| 140 | * Produce an instance of a constructor, plus genvars for its arguments. | |
| 141 | *---------------------------------------------------------------------------*) | |
| 142 | fun fresh_constr ty_match colty gv c = | |
| 143 | let val (_,Ty) = dest_Const c | |
| 144 | val L = binder_types Ty | |
| 145 | and ty = body_type Ty | |
| 146 | val ty_theta = ty_match ty colty | |
| 147 | val c' = S.inst ty_theta c | |
| 148 | val gvars = map (S.inst ty_theta o gv) L | |
| 149 | in (c', gvars) | |
| 150 | end; | |
| 151 | ||
| 152 | ||
| 153 | (*--------------------------------------------------------------------------- | |
| 154 | * Goes through a list of rows and picks out the ones beginning with a | |
| 155 | * pattern with constructor = name. | |
| 156 | *---------------------------------------------------------------------------*) | |
| 157 | fun mk_group name rows = | |
| 158 | fold_rev (fn (row as ((prfx, p::rst), rhs)) => | |
| 159 | fn (in_group,not_in_group) => | |
| 160 | let val (pc,args) = S.strip_comb p | |
| 161 | in if ((#1 (Term.dest_Const pc) = name) handle TERM _ => false) | |
| 162 | then (((prfx,args@rst), rhs)::in_group, not_in_group) | |
| 163 | else (in_group, row::not_in_group) end) | |
| 164 | rows ([],[]); | |
| 165 | ||
| 166 | (*--------------------------------------------------------------------------- | |
| 167 | * Partition the rows. Not efficient: we should use hashing. | |
| 168 | *---------------------------------------------------------------------------*) | |
| 169 | fun partition _ _ (_,_,_,[]) = raise TFL_ERR "partition" "no rows" | |
| 170 | | partition gv ty_match | |
| 171 | (constructors, colty, res_ty, rows as (((prfx,_),_)::_)) = | |
| 172 | let val fresh = fresh_constr ty_match colty gv | |
| 173 |      fun part {constrs = [],      rows, A} = rev A
 | |
| 174 |        | part {constrs = c::crst, rows, A} =
 | |
| 175 | let val (c',gvars) = fresh c | |
| 176 | val (in_group, not_in_group) = mk_group (#1 (dest_Const c')) rows | |
| 177 | val in_group' = | |
| 178 | if (null in_group) (* Constructor not given *) | |
| 179 | then [((prfx, #2(fresh c)), (S.ARB res_ty, (~1,false)))] | |
| 180 | else in_group | |
| 181 | in | |
| 182 |          part{constrs = crst,
 | |
| 183 | rows = not_in_group, | |
| 184 |               A = {constructor = c',
 | |
| 185 | new_formals = gvars, | |
| 186 | group = in_group'}::A} | |
| 187 | end | |
| 188 | in part{constrs=constructors, rows=rows, A=[]}
 | |
| 189 | end; | |
| 190 | ||
| 191 | (*--------------------------------------------------------------------------- | |
| 192 | * Misc. routines used in mk_case | |
| 193 | *---------------------------------------------------------------------------*) | |
| 194 | ||
| 195 | fun mk_pat (c,l) = | |
| 196 | let val L = length (binder_types (type_of c)) | |
| 197 | fun build (prfx,tag,plist) = | |
| 198 | let val args = Library.take (L,plist) | |
| 199 | and plist' = Library.drop(L,plist) | |
| 200 | in (prfx,tag,list_comb(c,args)::plist') end | |
| 201 | in map build l end; | |
| 202 | ||
| 203 | fun v_to_prfx (prfx, v::pats) = (v::prfx,pats) | |
| 204 | | v_to_prfx _ = raise TFL_ERR "mk_case" "v_to_prfx"; | |
| 205 | ||
| 206 | fun v_to_pats (v::prfx,tag, pats) = (prfx, tag, v::pats) | |
| 207 | | v_to_pats _ = raise TFL_ERR "mk_case" "v_to_pats"; | |
| 208 | ||
| 209 | ||
| 210 | (*---------------------------------------------------------------------------- | |
| 211 | * Translation of pattern terms into nested case expressions. | |
| 212 | * | |
| 213 | * This performs the translation and also builds the full set of patterns. | |
| 214 | * Thus it supports the construction of induction theorems even when an | |
| 215 | * incomplete set of patterns is given. | |
| 216 | *---------------------------------------------------------------------------*) | |
| 217 | ||
| 218 | fun mk_case ty_info ty_match usednames range_ty = | |
| 219 | let | |
| 220 | fun mk_case_fail s = raise TFL_ERR "mk_case" s | |
| 221 | val fresh_var = gvvariant usednames | |
| 222 | val divide = partition fresh_var ty_match | |
| 223 | fun expand constructors ty ((_,[]), _) = mk_case_fail"expand_var_row" | |
| 224 | | expand constructors ty (row as ((prfx, p::rst), rhs)) = | |
| 225 | if (is_Free p) | |
| 226 | then let val fresh = fresh_constr ty_match ty fresh_var | |
| 227 | fun expnd (c,gvs) = | |
| 228 | let val capp = list_comb(c,gvs) | |
| 229 | in ((prfx, capp::rst), pattern_subst[(p,capp)] rhs) | |
| 230 | end | |
| 231 | in map expnd (map fresh constructors) end | |
| 232 | else [row] | |
| 233 |  fun mk{rows=[],...} = mk_case_fail"no rows"
 | |
| 234 |    | mk{path=[], rows = ((prfx, []), (tm,tag))::_} =  (* Done *)
 | |
| 235 | ([(prfx,tag,[])], tm) | |
| 236 |    | mk{path=[], rows = _::_} = mk_case_fail"blunder"
 | |
| 237 |    | mk{path as u::rstp, rows as ((prfx, []), rhs)::rst} =
 | |
| 238 |         mk{path = path,
 | |
| 239 | rows = ((prfx, [fresh_var(type_of u)]), rhs)::rst} | |
| 240 |    | mk{path = u::rstp, rows as ((_, p::_), _)::_} =
 | |
| 241 | let val (pat_rectangle,rights) = ListPair.unzip rows | |
| 242 | val col0 = map(hd o #2) pat_rectangle | |
| 243 | in | |
| 244 | if (forall is_Free col0) | |
| 245 | then let val rights' = map (fn(v,e) => pattern_subst[(v,u)] e) | |
| 246 | (ListPair.zip (col0, rights)) | |
| 247 | val pat_rectangle' = map v_to_prfx pat_rectangle | |
| 248 |               val (pref_patl,tm) = mk{path = rstp,
 | |
| 249 | rows = ListPair.zip (pat_rectangle', | |
| 250 | rights')} | |
| 251 | in (map v_to_pats pref_patl, tm) | |
| 252 | end | |
| 253 | else | |
| 254 | let val pty as Type (ty_name,_) = type_of p | |
| 255 | in | |
| 256 | case (ty_info ty_name) | |
| 257 |      of NONE => mk_case_fail("Not a known datatype: "^ty_name)
 | |
| 258 |       | SOME{case_const,constructors} =>
 | |
| 259 | let | |
| 260 | val case_const_name = #1(dest_Const case_const) | |
| 261 | val nrows = List.concat (map (expand constructors pty) rows) | |
| 262 | val subproblems = divide(constructors, pty, range_ty, nrows) | |
| 263 | val groups = map #group subproblems | |
| 264 | and new_formals = map #new_formals subproblems | |
| 265 | and constructors' = map #constructor subproblems | |
| 266 |             val news = map (fn (nf,rows) => {path = nf@rstp, rows=rows})
 | |
| 267 | (ListPair.zip (new_formals, groups)) | |
| 268 | val rec_calls = map mk news | |
| 269 | val (pat_rect,dtrees) = ListPair.unzip rec_calls | |
| 270 | val case_functions = map S.list_mk_abs | |
| 271 | (ListPair.zip (new_formals, dtrees)) | |
| 272 | val types = map type_of (case_functions@[u]) @ [range_ty] | |
| 273 | val case_const' = Const(case_const_name, list_mk_type types) | |
| 274 | val tree = list_comb(case_const', case_functions@[u]) | |
| 275 | val pat_rect1 = List.concat | |
| 276 | (ListPair.map mk_pat (constructors', pat_rect)) | |
| 277 | in (pat_rect1,tree) | |
| 278 | end | |
| 279 | end end | |
| 280 | in mk | |
| 281 | end; | |
| 282 | ||
| 283 | ||
| 284 | (* Repeated variable occurrences in a pattern are not allowed. *) | |
| 285 | fun FV_multiset tm = | |
| 286 | case (S.dest_term tm) | |
| 287 |      of S.VAR{Name = c, Ty = T} => [Free(c, T)]
 | |
| 288 | | S.CONST _ => [] | |
| 289 |       | S.COMB{Rator, Rand} => FV_multiset Rator @ FV_multiset Rand
 | |
| 290 | | S.LAMB _ => raise TFL_ERR "FV_multiset" "lambda"; | |
| 291 | ||
| 292 | fun no_repeat_vars thy pat = | |
| 293 | let fun check [] = true | |
| 294 | | check (v::rst) = | |
| 295 | if member (op aconv) rst v then | |
| 296 | raise TFL_ERR "no_repeat_vars" | |
| 297 | (quote (#1 (dest_Free v)) ^ | |
| 298 | " occurs repeatedly in the pattern " ^ | |
| 26928 | 299 | quote (Display.string_of_cterm (Thry.typecheck thy pat))) | 
| 23150 | 300 | else check rst | 
| 301 | in check (FV_multiset pat) | |
| 302 | end; | |
| 303 | ||
| 304 | fun dest_atom (Free p) = p | |
| 305 | | dest_atom (Const p) = p | |
| 306 | | dest_atom _ = raise TFL_ERR "dest_atom" "function name not an identifier"; | |
| 307 | ||
| 308 | fun same_name (p,q) = #1(dest_atom p) = #1(dest_atom q); | |
| 309 | ||
| 310 | local fun mk_functional_err s = raise TFL_ERR "mk_functional" s | |
| 311 | fun single [_$_] = | |
| 312 | mk_functional_err "recdef does not allow currying" | |
| 313 | | single [f] = f | |
| 314 | | single fs = | |
| 315 | (*multiple function names?*) | |
| 316 | if length (distinct same_name fs) < length fs | |
| 317 | then mk_functional_err | |
| 318 | "The function being declared appears with multiple types" | |
| 319 | else mk_functional_err | |
| 320 | (Int.toString (length fs) ^ | |
| 321 | " distinct function names being declared") | |
| 322 | in | |
| 323 | fun mk_functional thy clauses = | |
| 324 | let val (L,R) = ListPair.unzip (map HOLogic.dest_eq clauses | |
| 325 | handle TERM _ => raise TFL_ERR "mk_functional" | |
| 326 | "recursion equations must use the = relation") | |
| 327 | val (funcs,pats) = ListPair.unzip (map (fn (t$u) =>(t,u)) L) | |
| 328 | val atom = single (distinct (op aconv) funcs) | |
| 329 | val (fname,ftype) = dest_atom atom | |
| 330 | val dummy = map (no_repeat_vars thy) pats | |
| 331 | val rows = ListPair.zip (map (fn x => ([]:term list,[x])) pats, | |
| 332 | map (fn (t,i) => (t,(i,true))) (enumerate R)) | |
| 30190 | 333 | val names = List.foldr OldTerm.add_term_names [] R | 
| 23150 | 334 | val atype = type_of(hd pats) | 
| 335 | and aname = Name.variant names "a" | |
| 336 | val a = Free(aname,atype) | |
| 337 | val ty_info = Thry.match_info thy | |
| 338 | val ty_match = Thry.match_type thy | |
| 339 | val range_ty = type_of (hd R) | |
| 340 | val (patts, case_tm) = mk_case ty_info ty_match (aname::names) range_ty | |
| 341 |                                     {path=[a], rows=rows}
 | |
| 342 | val patts1 = map (fn (_,tag,[pat]) => (pat,tag)) patts | |
| 343 | handle Match => mk_functional_err "error in pattern-match translation" | |
| 344 | val patts2 = Library.sort (Library.int_ord o Library.pairself row_of_pat) patts1 | |
| 345 | val finals = map row_of_pat patts2 | |
| 346 | val originals = map (row_of_pat o #2) rows | |
| 347 | val dummy = case (originals\\finals) | |
| 348 | of [] => () | |
| 349 | | L => mk_functional_err | |
| 350 |  ("The following clauses are redundant (covered by preceding clauses): " ^
 | |
| 351 | commas (map (fn i => Int.toString (i + 1)) L)) | |
| 30364 
577edc39b501
moved basic algebra of long names from structure NameSpace to Long_Name;
 wenzelm parents: 
30280diff
changeset | 352 |  in {functional = Abs(Long_Name.base_name fname, ftype,
 | 
| 23150 | 353 | abstract_over (atom, | 
| 354 | absfree(aname,atype, case_tm))), | |
| 355 | pats = patts2} | |
| 356 | end end; | |
| 357 | ||
| 358 | ||
| 359 | (*---------------------------------------------------------------------------- | |
| 360 | * | |
| 361 | * PRINCIPLES OF DEFINITION | |
| 362 | * | |
| 363 | *---------------------------------------------------------------------------*) | |
| 364 | ||
| 365 | ||
| 366 | (*For Isabelle, the lhs of a definition must be a constant.*) | |
| 367 | fun mk_const_def sign (c, Ty, rhs) = | |
| 24493 
d4380e9b287b
replaced ProofContext.infer_types by general Syntax.check_terms;
 wenzelm parents: 
23150diff
changeset | 368 | singleton (Syntax.check_terms (ProofContext.init sign)) | 
| 23150 | 369 |     (Sign.intern_term sign (Const("==",dummyT) $ Const(c,Ty) $ rhs));
 | 
| 370 | ||
| 371 | (*Make all TVars available for instantiation by adding a ? to the front*) | |
| 372 | fun poly_tvars (Type(a,Ts)) = Type(a, map (poly_tvars) Ts) | |
| 373 |   | poly_tvars (TFree (a,sort)) = TVar (("?" ^ a, 0), sort)
 | |
| 374 |   | poly_tvars (TVar ((a,i),sort)) = TVar (("?" ^ a, i+1), sort);
 | |
| 375 | ||
| 376 | local val f_eq_wfrec_R_M = | |
| 377 | #ant(S.dest_imp(#2(S.strip_forall (concl Thms.WFREC_COROLLARY)))) | |
| 378 |       val {lhs=f, rhs} = S.dest_eq f_eq_wfrec_R_M
 | |
| 379 | val (fname,_) = dest_Free f | |
| 380 | val (wfrec,_) = S.strip_comb rhs | |
| 381 | in | |
| 382 | fun wfrec_definition0 thy fid R (functional as Abs(x, Ty, _)) = | |
| 383 | let val def_name = if x<>fid then | |
| 384 | raise TFL_ERR "wfrec_definition0" | |
| 385 |                                       ("Expected a definition of " ^
 | |
| 386 | quote fid ^ " but found one of " ^ | |
| 387 | quote x) | |
| 388 | else x ^ "_def" | |
| 389 | val wfrec_R_M = map_types poly_tvars | |
| 390 | (wfrec $ map_types poly_tvars R) | |
| 391 | $ functional | |
| 392 | val def_term = mk_const_def thy (x, Ty, wfrec_R_M) | |
| 29579 | 393 | val ([def], thy') = PureThy.add_defs false [Thm.no_attributes (Binding.name def_name, def_term)] thy | 
| 23150 | 394 | in (thy', def) end; | 
| 395 | end; | |
| 396 | ||
| 397 | ||
| 398 | ||
| 399 | (*--------------------------------------------------------------------------- | |
| 400 | * This structure keeps track of congruence rules that aren't derived | |
| 401 | * from a datatype definition. | |
| 402 | *---------------------------------------------------------------------------*) | |
| 403 | fun extraction_thms thy = | |
| 404 |  let val {case_rewrites,case_congs} = Thry.extract_info thy
 | |
| 405 | in (case_rewrites, case_congs) | |
| 406 | end; | |
| 407 | ||
| 408 | ||
| 409 | (*--------------------------------------------------------------------------- | |
| 410 | * Pair patterns with termination conditions. The full list of patterns for | |
| 411 | * a definition is merged with the TCs arising from the user-given clauses. | |
| 412 | * There can be fewer clauses than the full list, if the user omitted some | |
| 413 | * cases. This routine is used to prepare input for mk_induction. | |
| 414 | *---------------------------------------------------------------------------*) | |
| 415 | fun merge full_pats TCs = | |
| 416 | let fun insert (p,TCs) = | |
| 417 | let fun insrt ((x as (h,[]))::rst) = | |
| 418 | if (p aconv h) then (p,TCs)::rst else x::insrt rst | |
| 419 | | insrt (x::rst) = x::insrt rst | |
| 420 | | insrt[] = raise TFL_ERR "merge.insert" "pattern not found" | |
| 421 | in insrt end | |
| 422 | fun pass ([],ptcl_final) = ptcl_final | |
| 423 | | pass (ptcs::tcl, ptcl) = pass(tcl, insert ptcs ptcl) | |
| 424 | in | |
| 425 | pass (TCs, map (fn p => (p,[])) full_pats) | |
| 426 | end; | |
| 427 | ||
| 428 | ||
| 429 | fun givens pats = map pat_of (List.filter given pats); | |
| 430 | ||
| 431 | fun post_definition meta_tflCongs (theory, (def, pats)) = | |
| 432 | let val tych = Thry.typecheck theory | |
| 433 | val f = #lhs(S.dest_eq(concl def)) | |
| 434 | val corollary = R.MATCH_MP Thms.WFREC_COROLLARY def | |
| 435 | val pats' = List.filter given pats | |
| 436 | val given_pats = map pat_of pats' | |
| 437 | val rows = map row_of_pat pats' | |
| 438 | val WFR = #ant(S.dest_imp(concl corollary)) | |
| 439 | val R = #Rand(S.dest_comb WFR) | |
| 440 | val corollary' = R.UNDISCH corollary (* put WF R on assums *) | |
| 441 | val corollaries = map (fn pat => R.SPEC (tych pat) corollary') | |
| 442 | given_pats | |
| 443 | val (case_rewrites,context_congs) = extraction_thms theory | |
| 444 | (*case_ss causes minimal simplification: bodies of case expressions are | |
| 445 | not simplified. Otherwise large examples (Red-Black trees) are too | |
| 446 | slow.*) | |
| 447 | val case_ss = Simplifier.theory_context theory | |
| 448 | (HOL_basic_ss addcongs | |
| 449 | (map (#weak_case_cong o snd) o Symtab.dest o DatatypePackage.get_datatypes) theory addsimps case_rewrites) | |
| 450 | val corollaries' = map (Simplifier.simplify case_ss) corollaries | |
| 451 | val extract = R.CONTEXT_REWRITE_RULE | |
| 26177 | 452 |                      (f, [R], @{thm cut_apply}, meta_tflCongs@context_congs)
 | 
| 23150 | 453 | val (rules, TCs) = ListPair.unzip (map extract corollaries') | 
| 454 | val rules0 = map (rewrite_rule [Thms.CUT_DEF]) rules | |
| 455 | val mk_cond_rule = R.FILTER_DISCH_ALL(not o curry (op aconv) WFR) | |
| 456 | val rules1 = R.LIST_CONJ(map mk_cond_rule rules0) | |
| 457 | in | |
| 458 |  {rules = rules1,
 | |
| 459 | rows = rows, | |
| 460 | full_pats_TCs = merge (map pat_of pats) (ListPair.zip (given_pats, TCs)), | |
| 461 | TCs = TCs} | |
| 462 | end; | |
| 463 | ||
| 464 | ||
| 465 | (*--------------------------------------------------------------------------- | |
| 466 | * Perform the extraction without making the definition. Definition and | |
| 467 | * extraction commute for the non-nested case. (Deferred recdefs) | |
| 468 | * | |
| 469 | * The purpose of wfrec_eqns is merely to instantiate the recursion theorem | |
| 470 | * and extract termination conditions: no definition is made. | |
| 471 | *---------------------------------------------------------------------------*) | |
| 472 | ||
| 473 | fun wfrec_eqns thy fid tflCongs eqns = | |
| 474 |  let val {lhs,rhs} = S.dest_eq (hd eqns)
 | |
| 475 | val (f,args) = S.strip_comb lhs | |
| 476 | val (fname,fty) = dest_atom f | |
| 477 | val (SV,a) = front_last args (* SV = schematic variables *) | |
| 478 | val g = list_comb(f,SV) | |
| 479 | val h = Free(fname,type_of g) | |
| 480 | val eqns1 = map (subst_free[(g,h)]) eqns | |
| 481 |      val {functional as Abs(x, Ty, _),  pats} = mk_functional thy eqns1
 | |
| 482 | val given_pats = givens pats | |
| 483 | (* val f = Free(x,Ty) *) | |
| 484 |      val Type("fun", [f_dty, f_rty]) = Ty
 | |
| 485 | val dummy = if x<>fid then | |
| 486 | raise TFL_ERR "wfrec_eqns" | |
| 487 |                                       ("Expected a definition of " ^
 | |
| 488 | quote fid ^ " but found one of " ^ | |
| 489 | quote x) | |
| 490 | else () | |
| 491 | val (case_rewrites,context_congs) = extraction_thms thy | |
| 492 | val tych = Thry.typecheck thy | |
| 493 | val WFREC_THM0 = R.ISPEC (tych functional) Thms.WFREC_COROLLARY | |
| 494 |      val Const("All",_) $ Abs(Rname,Rtype,_) = concl WFREC_THM0
 | |
| 30190 | 495 | val R = Free (Name.variant (List.foldr OldTerm.add_term_names [] eqns) Rname, | 
| 23150 | 496 | Rtype) | 
| 497 | val WFREC_THM = R.ISPECL [tych R, tych g] WFREC_THM0 | |
| 498 | val ([proto_def, WFR],_) = S.strip_imp(concl WFREC_THM) | |
| 499 | val dummy = | |
| 500 | if !trace then | |
| 501 |                writeln ("ORIGINAL PROTO_DEF: " ^
 | |
| 26939 
1035c89b4c02
moved global pretty/string_of functions from Sign to Syntax;
 wenzelm parents: 
26928diff
changeset | 502 | Syntax.string_of_term_global thy proto_def) | 
| 23150 | 503 | else () | 
| 504 | val R1 = S.rand WFR | |
| 505 | val corollary' = R.UNDISCH(R.UNDISCH WFREC_THM) | |
| 506 | val corollaries = map (fn pat => R.SPEC (tych pat) corollary') given_pats | |
| 507 | val corollaries' = map (rewrite_rule case_rewrites) corollaries | |
| 508 | fun extract X = R.CONTEXT_REWRITE_RULE | |
| 26177 | 509 |                        (f, R1::SV, @{thm cut_apply}, tflCongs@context_congs) X
 | 
| 23150 | 510 |  in {proto_def = proto_def,
 | 
| 511 | SV=SV, | |
| 512 | WFR=WFR, | |
| 513 | pats=pats, | |
| 514 | extracta = map extract corollaries'} | |
| 515 | end; | |
| 516 | ||
| 517 | ||
| 518 | (*--------------------------------------------------------------------------- | |
| 519 | * Define the constant after extracting the termination conditions. The | |
| 520 | * wellfounded relation used in the definition is computed by using the | |
| 521 | * choice operator on the extracted conditions (plus the condition that | |
| 522 | * such a relation must be wellfounded). | |
| 523 | *---------------------------------------------------------------------------*) | |
| 524 | ||
| 525 | fun lazyR_def thy fid tflCongs eqns = | |
| 526 |  let val {proto_def,WFR,pats,extracta,SV} =
 | |
| 527 | wfrec_eqns thy fid tflCongs eqns | |
| 528 | val R1 = S.rand WFR | |
| 529 | val f = #lhs(S.dest_eq proto_def) | |
| 530 | val (extractants,TCl) = ListPair.unzip extracta | |
| 531 | val dummy = if !trace | |
| 532 | then (writeln "Extractants = "; | |
| 26928 | 533 | Display.prths extractants; | 
| 23150 | 534 | ()) | 
| 535 | else () | |
| 30190 | 536 | val TCs = List.foldr (gen_union (op aconv)) [] TCl | 
| 23150 | 537 | val full_rqt = WFR::TCs | 
| 538 |      val R' = S.mk_select{Bvar=R1, Body=S.list_mk_conj full_rqt}
 | |
| 539 | val R'abs = S.rand R' | |
| 540 | val proto_def' = subst_free[(R1,R')] proto_def | |
| 541 |      val dummy = if !trace then writeln ("proto_def' = " ^
 | |
| 26939 
1035c89b4c02
moved global pretty/string_of functions from Sign to Syntax;
 wenzelm parents: 
26928diff
changeset | 542 | Syntax.string_of_term_global | 
| 23150 | 543 | thy proto_def') | 
| 544 | else () | |
| 545 |      val {lhs,rhs} = S.dest_eq proto_def'
 | |
| 546 | val (c,args) = S.strip_comb lhs | |
| 547 | val (name,Ty) = dest_atom c | |
| 548 | val defn = mk_const_def thy (name, Ty, S.list_mk_abs (args,rhs)) | |
| 549 | val ([def0], theory) = | |
| 550 | thy | |
| 27691 
ce171cbd4b93
PureThy: dropped note_thmss_qualified, dropped _i suffix
 haftmann parents: 
26939diff
changeset | 551 | |> PureThy.add_defs false | 
| 29579 | 552 | [Thm.no_attributes (Binding.name (fid ^ "_def"), defn)] | 
| 23150 | 553 | val def = Thm.freezeT def0; | 
| 26928 | 554 |      val dummy = if !trace then writeln ("DEF = " ^ Display.string_of_thm def)
 | 
| 23150 | 555 | else () | 
| 556 | (* val fconst = #lhs(S.dest_eq(concl def)) *) | |
| 557 | val tych = Thry.typecheck theory | |
| 558 | val full_rqt_prop = map (Dcterm.mk_prop o tych) full_rqt | |
| 559 | (*lcp: a lot of object-logic inference to remove*) | |
| 560 | val baz = R.DISCH_ALL | |
| 561 | (fold_rev R.DISCH full_rqt_prop | |
| 562 | (R.LIST_CONJ extractants)) | |
| 26928 | 563 |      val dum = if !trace then writeln ("baz = " ^ Display.string_of_thm baz)
 | 
| 23150 | 564 | else () | 
| 565 | val f_free = Free (fid, fastype_of f) (*'cos f is a Const*) | |
| 566 | val SV' = map tych SV; | |
| 567 | val SVrefls = map reflexive SV' | |
| 568 | val def0 = (fold (fn x => fn th => R.rbeta(combination th x)) | |
| 569 | SVrefls def) | |
| 570 | RS meta_eq_to_obj_eq | |
| 571 | val def' = R.MP (R.SPEC (tych R') (R.GEN (tych R1) baz)) def0 | |
| 572 | val body_th = R.LIST_CONJ (map R.ASSUME full_rqt_prop) | |
| 573 | val SELECT_AX = (*in this way we hope to avoid a STATIC dependence upon | |
| 574 | theory Hilbert_Choice*) | |
| 575 | thm "Hilbert_Choice.tfl_some" | |
| 576 | handle ERROR msg => cat_error msg | |
| 577 | "defer_recdef requires theory Main or at least Hilbert_Choice as parent" | |
| 578 | val bar = R.MP (R.ISPECL[tych R'abs, tych R1] SELECT_AX) body_th | |
| 579 |  in {theory = theory, R=R1, SV=SV,
 | |
| 580 | rules = fold (U.C R.MP) (R.CONJUNCTS bar) def', | |
| 581 | full_pats_TCs = merge (map pat_of pats) (ListPair.zip (givens pats, TCl)), | |
| 582 | patterns = pats} | |
| 583 | end; | |
| 584 | ||
| 585 | ||
| 586 | ||
| 587 | (*---------------------------------------------------------------------------- | |
| 588 | * | |
| 589 | * INDUCTION THEOREM | |
| 590 | * | |
| 591 | *---------------------------------------------------------------------------*) | |
| 592 | ||
| 593 | ||
| 594 | (*------------------------ Miscellaneous function -------------------------- | |
| 595 | * | |
| 596 | * [x_1,...,x_n] ?v_1...v_n. M[v_1,...,v_n] | |
| 597 | * ----------------------------------------------------------- | |
| 598 | * ( M[x_1,...,x_n], [(x_i,?v_1...v_n. M[v_1,...,v_n]), | |
| 599 | * ... | |
| 600 | * (x_j,?v_n. M[x_1,...,x_(n-1),v_n])] ) | |
| 601 | * | |
| 602 | * This function is totally ad hoc. Used in the production of the induction | |
| 603 | * theorem. The nchotomy theorem can have clauses that look like | |
| 604 | * | |
| 605 | * ?v1..vn. z = C vn..v1 | |
| 606 | * | |
| 607 | * in which the order of quantification is not the order of occurrence of the | |
| 608 | * quantified variables as arguments to C. Since we have no control over this | |
| 609 | * aspect of the nchotomy theorem, we make the correspondence explicit by | |
| 610 | * pairing the incoming new variable with the term it gets beta-reduced into. | |
| 611 | *---------------------------------------------------------------------------*) | |
| 612 | ||
| 613 | fun alpha_ex_unroll (xlist, tm) = | |
| 614 | let val (qvars,body) = S.strip_exists tm | |
| 615 | val vlist = #2(S.strip_comb (S.rhs body)) | |
| 616 | val plist = ListPair.zip (vlist, xlist) | |
| 617 | val args = map (the o AList.lookup (op aconv) plist) qvars | |
| 618 | handle Option => sys_error | |
| 619 | "TFL fault [alpha_ex_unroll]: no correspondence" | |
| 620 | fun build ex [] = [] | |
| 621 | | build (_$rex) (v::rst) = | |
| 622 | let val ex1 = Term.betapply(rex, v) | |
| 623 | in ex1 :: build ex1 rst | |
| 624 | end | |
| 625 | val (nex::exl) = rev (tm::build tm args) | |
| 626 | in | |
| 627 | (nex, ListPair.zip (args, rev exl)) | |
| 628 | end; | |
| 629 | ||
| 630 | ||
| 631 | ||
| 632 | (*---------------------------------------------------------------------------- | |
| 633 | * | |
| 634 | * PROVING COMPLETENESS OF PATTERNS | |
| 635 | * | |
| 636 | *---------------------------------------------------------------------------*) | |
| 637 | ||
| 638 | fun mk_case ty_info usednames thy = | |
| 639 | let | |
| 640 | val divide = ipartition (gvvariant usednames) | |
| 641 | val tych = Thry.typecheck thy | |
| 642 | fun tych_binding(x,y) = (tych x, tych y) | |
| 643 | fun fail s = raise TFL_ERR "mk_case" s | |
| 644 |  fun mk{rows=[],...} = fail"no rows"
 | |
| 645 |    | mk{path=[], rows = [([], (thm, bindings))]} =
 | |
| 646 | R.IT_EXISTS (map tych_binding bindings) thm | |
| 647 |    | mk{path = u::rstp, rows as (p::_, _)::_} =
 | |
| 648 | let val (pat_rectangle,rights) = ListPair.unzip rows | |
| 649 | val col0 = map hd pat_rectangle | |
| 650 | val pat_rectangle' = map tl pat_rectangle | |
| 651 | in | |
| 652 | if (forall is_Free col0) (* column 0 is all variables *) | |
| 653 | then let val rights' = map (fn ((thm,theta),v) => (thm,theta@[(u,v)])) | |
| 654 | (ListPair.zip (rights, col0)) | |
| 655 |           in mk{path = rstp, rows = ListPair.zip (pat_rectangle', rights')}
 | |
| 656 | end | |
| 657 | else (* column 0 is all constructors *) | |
| 658 | let val Type (ty_name,_) = type_of p | |
| 659 | in | |
| 660 | case (ty_info ty_name) | |
| 661 |      of NONE => fail("Not a known datatype: "^ty_name)
 | |
| 662 |       | SOME{constructors,nchotomy} =>
 | |
| 663 | let val thm' = R.ISPEC (tych u) nchotomy | |
| 664 | val disjuncts = S.strip_disj (concl thm') | |
| 665 | val subproblems = divide(constructors, rows) | |
| 666 | val groups = map #group subproblems | |
| 667 | and new_formals = map #new_formals subproblems | |
| 668 | val existentials = ListPair.map alpha_ex_unroll | |
| 669 | (new_formals, disjuncts) | |
| 670 | val constraints = map #1 existentials | |
| 671 | val vexl = map #2 existentials | |
| 672 | fun expnd tm (pats,(th,b)) = (pats,(R.SUBS[R.ASSUME(tych tm)]th,b)) | |
| 673 |             val news = map (fn (nf,rows,c) => {path = nf@rstp,
 | |
| 674 | rows = map (expnd c) rows}) | |
| 675 | (U.zip3 new_formals groups constraints) | |
| 676 | val recursive_thms = map mk news | |
| 677 | val build_exists = Library.foldr | |
| 678 | (fn((x,t), th) => | |
| 679 | R.CHOOSE (tych x, R.ASSUME (tych t)) th) | |
| 680 | val thms' = ListPair.map build_exists (vexl, recursive_thms) | |
| 681 | val same_concls = R.EVEN_ORS thms' | |
| 682 | in R.DISJ_CASESL thm' same_concls | |
| 683 | end | |
| 684 | end end | |
| 685 | in mk | |
| 686 | end; | |
| 687 | ||
| 688 | ||
| 689 | fun complete_cases thy = | |
| 690 | let val tych = Thry.typecheck thy | |
| 691 | val ty_info = Thry.induct_info thy | |
| 692 | in fn pats => | |
| 30190 | 693 | let val names = List.foldr OldTerm.add_term_names [] pats | 
| 23150 | 694 | val T = type_of (hd pats) | 
| 695 | val aname = Name.variant names "a" | |
| 696 | val vname = Name.variant (aname::names) "v" | |
| 697 | val a = Free (aname, T) | |
| 698 | val v = Free (vname, T) | |
| 699 | val a_eq_v = HOLogic.mk_eq(a,v) | |
| 700 |      val ex_th0 = R.EXISTS (tych (S.mk_exists{Bvar=v,Body=a_eq_v}), tych a)
 | |
| 701 | (R.REFL (tych a)) | |
| 702 | val th0 = R.ASSUME (tych a_eq_v) | |
| 703 | val rows = map (fn x => ([x], (th0,[]))) pats | |
| 704 | in | |
| 705 | R.GEN (tych a) | |
| 706 | (R.RIGHT_ASSOC | |
| 707 | (R.CHOOSE(tych v, ex_th0) | |
| 708 | (mk_case ty_info (vname::aname::names) | |
| 709 |                  thy {path=[v], rows=rows})))
 | |
| 710 | end end; | |
| 711 | ||
| 712 | ||
| 713 | (*--------------------------------------------------------------------------- | |
| 714 | * Constructing induction hypotheses: one for each recursive call. | |
| 715 | * | |
| 716 | * Note. R will never occur as a variable in the ind_clause, because | |
| 717 | * to do so, it would have to be from a nested definition, and we don't | |
| 718 | * allow nested defns to have R variable. | |
| 719 | * | |
| 720 | * Note. When the context is empty, there can be no local variables. | |
| 721 | *---------------------------------------------------------------------------*) | |
| 722 | (* | |
| 723 | local infix 5 ==> | |
| 724 |       fun (tm1 ==> tm2) = S.mk_imp{ant = tm1, conseq = tm2}
 | |
| 725 | in | |
| 726 | fun build_ih f P (pat,TCs) = | |
| 727 | let val globals = S.free_vars_lr pat | |
| 728 | fun nested tm = isSome (S.find_term (curry (op aconv) f) tm) | |
| 729 | fun dest_TC tm = | |
| 730 | let val (cntxt,R_y_pat) = S.strip_imp(#2(S.strip_forall tm)) | |
| 731 | val (R,y,_) = S.dest_relation R_y_pat | |
| 732 | val P_y = if (nested tm) then R_y_pat ==> P$y else P$y | |
| 733 | in case cntxt | |
| 734 | of [] => (P_y, (tm,[])) | |
| 735 | | _ => let | |
| 736 | val imp = S.list_mk_conj cntxt ==> P_y | |
| 737 | val lvs = gen_rems (op aconv) (S.free_vars_lr imp, globals) | |
| 738 | val locals = #2(U.pluck (curry (op aconv) P) lvs) handle U.ERR _ => lvs | |
| 739 | in (S.list_mk_forall(locals,imp), (tm,locals)) end | |
| 740 | end | |
| 741 | in case TCs | |
| 742 | of [] => (S.list_mk_forall(globals, P$pat), []) | |
| 743 | | _ => let val (ihs, TCs_locals) = ListPair.unzip(map dest_TC TCs) | |
| 744 | val ind_clause = S.list_mk_conj ihs ==> P$pat | |
| 745 | in (S.list_mk_forall(globals,ind_clause), TCs_locals) | |
| 746 | end | |
| 747 | end | |
| 748 | end; | |
| 749 | *) | |
| 750 | ||
| 751 | local infix 5 ==> | |
| 752 |       fun (tm1 ==> tm2) = S.mk_imp{ant = tm1, conseq = tm2}
 | |
| 753 | in | |
| 754 | fun build_ih f (P,SV) (pat,TCs) = | |
| 755 | let val pat_vars = S.free_vars_lr pat | |
| 756 | val globals = pat_vars@SV | |
| 757 | fun nested tm = isSome (S.find_term (curry (op aconv) f) tm) | |
| 758 | fun dest_TC tm = | |
| 759 | let val (cntxt,R_y_pat) = S.strip_imp(#2(S.strip_forall tm)) | |
| 760 | val (R,y,_) = S.dest_relation R_y_pat | |
| 761 | val P_y = if (nested tm) then R_y_pat ==> P$y else P$y | |
| 762 | in case cntxt | |
| 763 | of [] => (P_y, (tm,[])) | |
| 764 | | _ => let | |
| 765 | val imp = S.list_mk_conj cntxt ==> P_y | |
| 766 | val lvs = subtract (op aconv) globals (S.free_vars_lr imp) | |
| 767 | val locals = #2(U.pluck (curry (op aconv) P) lvs) handle U.ERR _ => lvs | |
| 768 | in (S.list_mk_forall(locals,imp), (tm,locals)) end | |
| 769 | end | |
| 770 | in case TCs | |
| 771 | of [] => (S.list_mk_forall(pat_vars, P$pat), []) | |
| 772 | | _ => let val (ihs, TCs_locals) = ListPair.unzip(map dest_TC TCs) | |
| 773 | val ind_clause = S.list_mk_conj ihs ==> P$pat | |
| 774 | in (S.list_mk_forall(pat_vars,ind_clause), TCs_locals) | |
| 775 | end | |
| 776 | end | |
| 777 | end; | |
| 778 | ||
| 779 | (*--------------------------------------------------------------------------- | |
| 780 | * This function makes good on the promise made in "build_ih". | |
| 781 | * | |
| 782 | * Input is tm = "(!y. R y pat ==> P y) ==> P pat", | |
| 783 | * TCs = TC_1[pat] ... TC_n[pat] | |
| 784 | * thm = ih1 /\ ... /\ ih_n |- ih[pat] | |
| 785 | *---------------------------------------------------------------------------*) | |
| 786 | fun prove_case f thy (tm,TCs_locals,thm) = | |
| 787 | let val tych = Thry.typecheck thy | |
| 788 | val antc = tych(#ant(S.dest_imp tm)) | |
| 789 | val thm' = R.SPEC_ALL thm | |
| 790 | fun nested tm = isSome (S.find_term (curry (op aconv) f) tm) | |
| 791 | fun get_cntxt TC = tych(#ant(S.dest_imp(#2(S.strip_forall(concl TC))))) | |
| 792 | fun mk_ih ((TC,locals),th2,nested) = | |
| 793 | R.GENL (map tych locals) | |
| 794 | (if nested then R.DISCH (get_cntxt TC) th2 handle U.ERR _ => th2 | |
| 795 | else if S.is_imp (concl TC) then R.IMP_TRANS TC th2 | |
| 796 | else R.MP th2 TC) | |
| 797 | in | |
| 798 | R.DISCH antc | |
| 799 | (if S.is_imp(concl thm') (* recursive calls in this clause *) | |
| 800 | then let val th1 = R.ASSUME antc | |
| 801 | val TCs = map #1 TCs_locals | |
| 802 | val ylist = map (#2 o S.dest_relation o #2 o S.strip_imp o | |
| 803 | #2 o S.strip_forall) TCs | |
| 804 | val TClist = map (fn(TC,lvs) => (R.SPEC_ALL(R.ASSUME(tych TC)),lvs)) | |
| 805 | TCs_locals | |
| 806 | val th2list = map (U.C R.SPEC th1 o tych) ylist | |
| 807 | val nlist = map nested TCs | |
| 808 | val triples = U.zip3 TClist th2list nlist | |
| 809 | val Pylist = map mk_ih triples | |
| 810 | in R.MP thm' (R.LIST_CONJ Pylist) end | |
| 811 | else thm') | |
| 812 | end; | |
| 813 | ||
| 814 | ||
| 815 | (*--------------------------------------------------------------------------- | |
| 816 | * | |
| 817 | * x = (v1,...,vn) |- M[x] | |
| 818 | * --------------------------------------------- | |
| 819 | * ?v1 ... vn. x = (v1,...,vn) |- M[x] | |
| 820 | * | |
| 821 | *---------------------------------------------------------------------------*) | |
| 822 | fun LEFT_ABS_VSTRUCT tych thm = | |
| 823 | let fun CHOOSER v (tm,thm) = | |
| 824 |         let val ex_tm = S.mk_exists{Bvar=v,Body=tm}
 | |
| 825 | in (ex_tm, R.CHOOSE(tych v, R.ASSUME (tych ex_tm)) thm) | |
| 826 | end | |
| 827 | val [veq] = List.filter (can S.dest_eq) (#1 (R.dest_thm thm)) | |
| 828 |       val {lhs,rhs} = S.dest_eq veq
 | |
| 829 | val L = S.free_vars_lr rhs | |
| 830 | in #2 (fold_rev CHOOSER L (veq,thm)) end; | |
| 831 | ||
| 832 | ||
| 833 | (*---------------------------------------------------------------------------- | |
| 834 | * Input : f, R, and [(pat1,TCs1),..., (patn,TCsn)] | |
| 835 | * | |
| 836 | * Instantiates WF_INDUCTION_THM, getting Sinduct and then tries to prove | |
| 837 | * recursion induction (Rinduct) by proving the antecedent of Sinduct from | |
| 838 | * the antecedent of Rinduct. | |
| 839 | *---------------------------------------------------------------------------*) | |
| 840 | fun mk_induction thy {fconst, R, SV, pat_TCs_list} =
 | |
| 841 | let val tych = Thry.typecheck thy | |
| 842 | val Sinduction = R.UNDISCH (R.ISPEC (tych R) Thms.WF_INDUCTION_THM) | |
| 843 | val (pats,TCsl) = ListPair.unzip pat_TCs_list | |
| 844 | val case_thm = complete_cases thy pats | |
| 845 | val domain = (type_of o hd) pats | |
| 30190 | 846 | val Pname = Name.variant (List.foldr (Library.foldr OldTerm.add_term_names) | 
| 23150 | 847 | [] (pats::TCsl)) "P" | 
| 848 | val P = Free(Pname, domain --> HOLogic.boolT) | |
| 849 | val Sinduct = R.SPEC (tych P) Sinduction | |
| 850 | val Sinduct_assumf = S.rand ((#ant o S.dest_imp o concl) Sinduct) | |
| 851 | val Rassums_TCl' = map (build_ih fconst (P,SV)) pat_TCs_list | |
| 852 | val (Rassums,TCl') = ListPair.unzip Rassums_TCl' | |
| 853 | val Rinduct_assum = R.ASSUME (tych (S.list_mk_conj Rassums)) | |
| 854 | val cases = map (fn pat => Term.betapply (Sinduct_assumf, pat)) pats | |
| 855 | val tasks = U.zip3 cases TCl' (R.CONJUNCTS Rinduct_assum) | |
| 856 | val proved_cases = map (prove_case fconst thy) tasks | |
| 30190 | 857 | val v = Free (Name.variant (List.foldr OldTerm.add_term_names [] (map concl proved_cases)) | 
| 23150 | 858 | "v", | 
| 859 | domain) | |
| 860 | val vtyped = tych v | |
| 861 | val substs = map (R.SYM o R.ASSUME o tych o (curry HOLogic.mk_eq v)) pats | |
| 862 | val proved_cases1 = ListPair.map (fn (th,th') => R.SUBS[th]th') | |
| 863 | (substs, proved_cases) | |
| 864 | val abs_cases = map (LEFT_ABS_VSTRUCT tych) proved_cases1 | |
| 865 | val dant = R.GEN vtyped (R.DISJ_CASESL (R.ISPEC vtyped case_thm) abs_cases) | |
| 866 | val dc = R.MP Sinduct dant | |
| 867 | val Parg_ty = type_of(#Bvar(S.dest_forall(concl dc))) | |
| 868 | val vars = map (gvvariant[Pname]) (S.strip_prod_type Parg_ty) | |
| 869 | val dc' = fold_rev (R.GEN o tych) vars | |
| 870 | (R.SPEC (tych(S.mk_vstruct Parg_ty vars)) dc) | |
| 871 | in | |
| 872 | R.GEN (tych P) (R.DISCH (tych(concl Rinduct_assum)) dc') | |
| 873 | end | |
| 874 | handle U.ERR _ => raise TFL_ERR "mk_induction" "failed derivation"; | |
| 875 | ||
| 876 | ||
| 877 | ||
| 878 | ||
| 879 | (*--------------------------------------------------------------------------- | |
| 880 | * | |
| 881 | * POST PROCESSING | |
| 882 | * | |
| 883 | *---------------------------------------------------------------------------*) | |
| 884 | ||
| 885 | ||
| 886 | fun simplify_induction thy hth ind = | |
| 887 | let val tych = Thry.typecheck thy | |
| 888 | val (asl,_) = R.dest_thm ind | |
| 889 | val (_,tc_eq_tc') = R.dest_thm hth | |
| 890 | val tc = S.lhs tc_eq_tc' | |
| 891 | fun loop [] = ind | |
| 892 | | loop (asm::rst) = | |
| 893 | if (can (Thry.match_term thy asm) tc) | |
| 894 | then R.UNDISCH | |
| 895 | (R.MATCH_MP | |
| 896 | (R.MATCH_MP Thms.simp_thm (R.DISCH (tych asm) ind)) | |
| 897 | hth) | |
| 898 | else loop rst | |
| 899 | in loop asl | |
| 900 | end; | |
| 901 | ||
| 902 | ||
| 903 | (*--------------------------------------------------------------------------- | |
| 904 | * The termination condition is an antecedent to the rule, and an | |
| 905 | * assumption to the theorem. | |
| 906 | *---------------------------------------------------------------------------*) | |
| 907 | fun elim_tc tcthm (rule,induction) = | |
| 908 | (R.MP rule tcthm, R.PROVE_HYP tcthm induction) | |
| 909 | ||
| 910 | ||
| 911 | fun trace_thms s L = | |
| 26928 | 912 | if !trace then writeln (cat_lines (s :: map Display.string_of_thm L)) | 
| 23150 | 913 | else (); | 
| 914 | ||
| 915 | fun trace_cterms s L = | |
| 26928 | 916 | if !trace then writeln (cat_lines (s :: map Display.string_of_cterm L)) | 
| 23150 | 917 | else ();; | 
| 918 | ||
| 919 | ||
| 920 | fun postprocess strict {wf_tac, terminator, simplifier} theory {rules,induction,TCs} =
 | |
| 921 | let val tych = Thry.typecheck theory | |
| 922 | val prove = R.prove strict; | |
| 923 | ||
| 924 | (*--------------------------------------------------------------------- | |
| 925 | * Attempt to eliminate WF condition. It's the only assumption of rules | |
| 926 | *---------------------------------------------------------------------*) | |
| 927 | val (rules1,induction1) = | |
| 928 | let val thm = prove(tych(HOLogic.mk_Trueprop | |
| 929 | (hd(#1(R.dest_thm rules)))), | |
| 930 | wf_tac) | |
| 931 | in (R.PROVE_HYP thm rules, R.PROVE_HYP thm induction) | |
| 932 | end handle U.ERR _ => (rules,induction); | |
| 933 | ||
| 934 | (*---------------------------------------------------------------------- | |
| 935 | * The termination condition (tc) is simplified to |- tc = tc' (there | |
| 936 | * might not be a change!) and then 3 attempts are made: | |
| 937 | * | |
| 938 | * 1. if |- tc = T, then eliminate it with eqT; otherwise, | |
| 939 | * 2. apply the terminator to tc'. If |- tc' = T then eliminate; else | |
| 940 | * 3. replace tc by tc' in both the rules and the induction theorem. | |
| 941 | *---------------------------------------------------------------------*) | |
| 942 | ||
| 943 | fun simplify_tc tc (r,ind) = | |
| 944 | let val tc1 = tych tc | |
| 945 | val _ = trace_cterms "TC before simplification: " [tc1] | |
| 946 | val tc_eq = simplifier tc1 | |
| 947 | val _ = trace_thms "result: " [tc_eq] | |
| 948 | in | |
| 949 | elim_tc (R.MATCH_MP Thms.eqT tc_eq) (r,ind) | |
| 950 | handle U.ERR _ => | |
| 951 | (elim_tc (R.MATCH_MP(R.MATCH_MP Thms.rev_eq_mp tc_eq) | |
| 952 | (prove(tych(HOLogic.mk_Trueprop(S.rhs(concl tc_eq))), | |
| 953 | terminator))) | |
| 954 | (r,ind) | |
| 955 | handle U.ERR _ => | |
| 956 | (R.UNDISCH(R.MATCH_MP (R.MATCH_MP Thms.simp_thm r) tc_eq), | |
| 957 | simplify_induction theory tc_eq ind)) | |
| 958 | end | |
| 959 | ||
| 960 | (*---------------------------------------------------------------------- | |
| 961 | * Nested termination conditions are harder to get at, since they are | |
| 962 | * left embedded in the body of the function (and in induction | |
| 963 | * theorem hypotheses). Our "solution" is to simplify them, and try to | |
| 964 | * prove termination, but leave the application of the resulting theorem | |
| 965 | * to a higher level. So things go much as in "simplify_tc": the | |
| 966 | * termination condition (tc) is simplified to |- tc = tc' (there might | |
| 967 | * not be a change) and then 2 attempts are made: | |
| 968 | * | |
| 969 | * 1. if |- tc = T, then return |- tc; otherwise, | |
| 970 | * 2. apply the terminator to tc'. If |- tc' = T then return |- tc; else | |
| 971 | * 3. return |- tc = tc' | |
| 972 | *---------------------------------------------------------------------*) | |
| 973 | fun simplify_nested_tc tc = | |
| 974 | let val tc_eq = simplifier (tych (#2 (S.strip_forall tc))) | |
| 975 | in | |
| 976 | R.GEN_ALL | |
| 977 | (R.MATCH_MP Thms.eqT tc_eq | |
| 978 | handle U.ERR _ => | |
| 979 | (R.MATCH_MP(R.MATCH_MP Thms.rev_eq_mp tc_eq) | |
| 980 | (prove(tych(HOLogic.mk_Trueprop (S.rhs(concl tc_eq))), | |
| 981 | terminator)) | |
| 982 | handle U.ERR _ => tc_eq)) | |
| 983 | end | |
| 984 | ||
| 985 | (*------------------------------------------------------------------- | |
| 986 | * Attempt to simplify the termination conditions in each rule and | |
| 987 | * in the induction theorem. | |
| 988 | *-------------------------------------------------------------------*) | |
| 989 | fun strip_imp tm = if S.is_neg tm then ([],tm) else S.strip_imp tm | |
| 990 | fun loop ([],extras,R,ind) = (rev R, ind, extras) | |
| 991 | | loop ((r,ftcs)::rst, nthms, R, ind) = | |
| 992 | let val tcs = #1(strip_imp (concl r)) | |
| 993 | val extra_tcs = subtract (op aconv) tcs ftcs | |
| 994 | val extra_tc_thms = map simplify_nested_tc extra_tcs | |
| 995 | val (r1,ind1) = fold simplify_tc tcs (r,ind) | |
| 996 | val r2 = R.FILTER_DISCH_ALL(not o S.is_WFR) r1 | |
| 997 | in loop(rst, nthms@extra_tc_thms, r2::R, ind1) | |
| 998 | end | |
| 999 | val rules_tcs = ListPair.zip (R.CONJUNCTS rules1, TCs) | |
| 1000 | val (rules2,ind2,extras) = loop(rules_tcs,[],[],induction1) | |
| 1001 | in | |
| 1002 |   {induction = ind2, rules = R.LIST_CONJ rules2, nested_tcs = extras}
 | |
| 1003 | end; | |
| 1004 | ||
| 1005 | ||
| 1006 | end; |