wenzelm@7674: (* Title: Pure/Isar/obtain.ML wenzelm@7674: ID: $Id$ wenzelm@7674: Author: Markus Wenzel, TU Muenchen wenzelm@7674: wenzelm@17858: The 'obtain' and 'guess' language elements -- generalized existence at wenzelm@17858: the level of proof texts: 'obtain' involves a proof that certain wenzelm@17858: fixes/assumes may be introduced into the present context; 'guess' is wenzelm@17858: similar, but derives these elements from the course of reasoning! wenzelm@7674: wenzelm@9468: wenzelm@9468: obtain x where "P x" == wenzelm@7674: wenzelm@12970: have "!!thesis. (!!x. P x ==> thesis) ==> thesis" wenzelm@12970: proof succeed wenzelm@9468: fix thesis wenzelm@12970: assume that [intro?]: "!!x. P x ==> thesis" wenzelm@12970: show thesis wenzelm@12970: qed wenzelm@10379: fix x assm (obtained) "P x" wenzelm@17858: wenzelm@17858: wenzelm@17858: wenzelm@17858: guess x == wenzelm@17858: wenzelm@17858: { wenzelm@17858: fix thesis wenzelm@17858: have "PROP ?guess" wenzelm@17974: apply magic -- {* turns goal into "thesis ==> Goal thesis" *} wenzelm@17858: wenzelm@17974: apply_end magic -- {* turns final "(!!x. P x ==> thesis) ==> Goal thesis" into wenzelm@17974: "Goal ((!!x. P x ==> thesis) ==> thesis)" which is a finished goal state *} wenzelm@17858: wenzelm@17858: } wenzelm@17858: fix x assm (obtained) "P x" wenzelm@8094: *) wenzelm@7674: wenzelm@7674: signature OBTAIN = wenzelm@7674: sig wenzelm@11890: val obtain: (string list * string option) list -> wenzelm@17111: ((string * Attrib.src list) * (string * (string list * string list)) list) list wenzelm@17357: -> bool -> Proof.state -> Proof.state wenzelm@11890: val obtain_i: (string list * typ option) list -> wenzelm@11890: ((string * Proof.context attribute list) * (term * (term list * term list)) list) list wenzelm@17357: -> bool -> Proof.state -> Proof.state wenzelm@17858: val guess: (string list * string option) list -> bool -> Proof.state -> Proof.state Seq.seq wenzelm@17858: val guess_i: (string list * typ option) list -> bool -> Proof.state -> Proof.state Seq.seq wenzelm@7674: end; wenzelm@7674: wenzelm@10379: structure Obtain: OBTAIN = wenzelm@7674: struct wenzelm@7674: wenzelm@17858: (** export_obtained **) wenzelm@8094: wenzelm@17858: fun export_obtained state parms rule cprops thm = wenzelm@9468: let wenzelm@17111: val {thy, prop, maxidx, ...} = Thm.rep_thm thm; wenzelm@17111: val cparms = map (Thm.cterm_of thy) parms; wenzelm@9468: wenzelm@9468: val thm' = thm wenzelm@11816: |> Drule.implies_intr_goals cprops wenzelm@9468: |> Drule.forall_intr_list cparms wenzelm@9468: |> Drule.forall_elim_vars (maxidx + 1); wenzelm@17974: val elim_tacs = replicate (length cprops) (Tactic.etac Drule.goalI); wenzelm@9468: wenzelm@9468: val concl = Logic.strip_assums_concl prop; wenzelm@9468: val bads = parms inter (Term.term_frees concl); wenzelm@9468: in wenzelm@9468: if not (null bads) then wenzelm@9481: raise Proof.STATE ("Conclusion contains obtained parameters: " ^ wenzelm@12055: space_implode " " (map (ProofContext.string_of_term (Proof.context_of state)) bads), state) wenzelm@17858: else if not (ObjectLogic.is_judgment thy concl) then wenzelm@17858: raise Proof.STATE ("Conclusion in obtained context must be object-logic judgments", state) wenzelm@9468: else (Tactic.rtac thm' THEN' RANGE elim_tacs) 1 rule wenzelm@9468: end; wenzelm@9468: wenzelm@9468: wenzelm@9468: wenzelm@17858: (** obtain **) wenzelm@17858: wenzelm@17858: fun bind_judgment ctxt name = wenzelm@17858: let val (t as _ $ Free v) = wenzelm@17858: ObjectLogic.fixed_judgment (ProofContext.theory_of ctxt) name wenzelm@17858: |> ProofContext.bind_skolem ctxt [name] wenzelm@17858: in (v, t) end; wenzelm@17858: wenzelm@17858: local wenzelm@8094: wenzelm@7674: val thatN = "that"; wenzelm@7674: wenzelm@17357: fun gen_obtain prep_att prep_vars prep_propp raw_vars raw_asms int state = wenzelm@7674: let wenzelm@9468: val _ = Proof.assert_forward_or_chain state; wenzelm@17858: val ctxt = Proof.context_of state; wenzelm@17357: val chain_facts = if can Proof.assert_chain state then Proof.the_facts state else []; wenzelm@7674: wenzelm@8543: (*obtain vars*) wenzelm@17858: val (vars, vars_ctxt) = fold_map prep_vars raw_vars ctxt; wenzelm@17858: val fix_ctxt = vars_ctxt |> ProofContext.fix_i vars; skalberg@15570: val xs = List.concat (map fst vars); wenzelm@7674: wenzelm@8543: (*obtain asms*) wenzelm@11890: val (asms_ctxt, proppss) = prep_propp (fix_ctxt, map snd raw_asms); skalberg@15570: val asm_props = List.concat (map (map fst) proppss); wenzelm@17858: val asms = map fst (Attrib.map_specs (prep_att (Proof.theory_of state)) raw_asms) ~~ proppss; wenzelm@10464: wenzelm@10582: val _ = ProofContext.warn_extra_tfrees fix_ctxt asms_ctxt; wenzelm@7674: wenzelm@12970: (*obtain statements*) wenzelm@16606: val thesisN = Term.variant xs AutoBind.thesisN; wenzelm@17858: val (thesis_var, thesis) = bind_judgment fix_ctxt thesisN; wenzelm@9468: wenzelm@10582: fun occs_var x = Library.get_first (fn t => wenzelm@10582: ProofContext.find_free t (ProofContext.get_skolem fix_ctxt x)) asm_props; wenzelm@10582: val raw_parms = map occs_var xs; skalberg@15570: val parms = List.mapPartial I raw_parms; wenzelm@10582: val parm_names = skalberg@15570: List.mapPartial (fn (SOME (Free a), x) => SOME (a, x) | _ => NONE) (raw_parms ~~ xs); wenzelm@10582: wenzelm@10582: val that_prop = wenzelm@17858: Term.list_all_free (map #1 parm_names, Logic.list_implies (asm_props, thesis)) wenzelm@10582: |> Library.curry Logic.list_rename_params (map #2 parm_names); wenzelm@12970: val obtain_prop = wenzelm@12970: Logic.list_rename_params ([AutoBind.thesisN], wenzelm@17858: Term.list_all_free ([thesis_var], Logic.mk_implies (that_prop, thesis))); wenzelm@7674: wenzelm@17357: fun after_qed _ _ = wenzelm@17357: Proof.local_qed (NONE, false) wenzelm@17858: #> Seq.map (`Proof.the_fact #-> (fn rule => wenzelm@17357: Proof.fix_i vars wenzelm@17858: #> Proof.assm_i (K (export_obtained state parms rule)) asms)); wenzelm@7674: in wenzelm@8094: state wenzelm@9468: |> Proof.enter_forward wenzelm@17858: |> Proof.have_i NONE (K (K Seq.single)) [(("", []), [(obtain_prop, ([], []))])] int wenzelm@17858: |> Proof.proof (SOME Method.succeed_text) |> Seq.hd skalberg@15531: |> Proof.fix_i [([thesisN], NONE)] skalberg@15531: |> Proof.assume_i [((thatN, [ContextRules.intro_query_local NONE]), [(that_prop, ([], []))])] wenzelm@16842: |> `Proof.the_facts wenzelm@17357: ||> Proof.chain_facts chain_facts wenzelm@17858: ||> Proof.show_i NONE after_qed [(("", []), [(thesis, ([], []))])] false wenzelm@17357: |-> (Proof.refine o Method.Basic o K o Method.insert) |> Seq.hd wenzelm@7674: end; wenzelm@7674: wenzelm@17858: in wenzelm@17858: wenzelm@17111: val obtain = gen_obtain Attrib.local_attribute ProofContext.read_vars ProofContext.read_propp; wenzelm@17111: val obtain_i = gen_obtain (K I) ProofContext.cert_vars ProofContext.cert_propp; wenzelm@8094: wenzelm@8094: end; wenzelm@17858: wenzelm@17858: wenzelm@17858: wenzelm@17858: (** guess **) wenzelm@17858: wenzelm@17858: local wenzelm@17858: wenzelm@17858: fun match_params state vars rule = wenzelm@17858: let wenzelm@17858: val ctxt = Proof.context_of state; wenzelm@17858: val thy = Proof.theory_of state; wenzelm@17891: val string_of_typ = ProofContext.string_of_typ ctxt; wenzelm@17858: val string_of_term = setmp show_types true (ProofContext.string_of_term ctxt); wenzelm@17891: wenzelm@17891: fun err msg th = raise Proof.STATE (msg ^ ":\n" ^ ProofContext.string_of_thm ctxt th, state); wenzelm@17858: wenzelm@17858: val params = RuleCases.strip_params (Logic.nth_prem (1, Thm.prop_of rule)); wenzelm@17858: val m = length vars; wenzelm@17858: val n = length params; wenzelm@17891: val _ = conditional (m > n) wenzelm@17891: (fn () => err "More variables than parameters in obtained rule" rule); wenzelm@17858: wenzelm@17858: fun match ((x, SOME T), (y, U)) tyenv = wenzelm@17858: ((x, T), Sign.typ_match thy (U, T) tyenv handle Type.TYPE_MATCH => wenzelm@17891: err ("Failed to match variable " ^ wenzelm@17858: string_of_term (Free (x, T)) ^ " against parameter " ^ wenzelm@17891: string_of_term (Syntax.mark_boundT (y, Envir.norm_type tyenv U)) ^ " in") rule) wenzelm@17858: | match ((x, NONE), (_, U)) tyenv = ((x, U), tyenv); wenzelm@17858: val (xs, tyenv) = fold_map match (vars ~~ Library.take (m, params)) Vartab.empty; wenzelm@17858: val ys = Library.drop (m, params); wenzelm@17858: val norm_type = Envir.norm_type tyenv; wenzelm@17858: wenzelm@17858: val xs' = xs |> map (apsnd norm_type); wenzelm@17858: val ys' = wenzelm@17858: map Syntax.internal (Term.variantlist (map fst ys, map fst xs)) ~~ wenzelm@17858: map (norm_type o snd) ys; wenzelm@17858: val instT = wenzelm@17858: fold (Term.add_tvarsT o #2) params [] wenzelm@17858: |> map (TVar #> (fn T => (Thm.ctyp_of thy T, Thm.ctyp_of thy (norm_type T)))); wenzelm@17858: val rule' = rule |> Thm.instantiate (instT, []); wenzelm@17891: wenzelm@17891: val tvars = Drule.tvars_of rule'; wenzelm@17891: val vars = fold (remove op =) (Term.add_vars (Thm.concl_of rule') []) (Drule.vars_of rule'); wenzelm@17891: val _ = wenzelm@17891: if null tvars andalso null vars then () wenzelm@17891: else err ("Illegal schematic variable(s) " ^ wenzelm@17891: commas (map (string_of_typ o TVar) tvars @ map (string_of_term o Var) vars) ^ " in") rule'; wenzelm@17858: in (xs' @ ys', rule') end; wenzelm@17858: wenzelm@17858: fun gen_guess prep_vars raw_vars int state = wenzelm@17858: let wenzelm@17858: val _ = Proof.assert_forward_or_chain state; wenzelm@17858: val thy = Proof.theory_of state; wenzelm@17858: val ctxt = Proof.context_of state; wenzelm@17858: val chain_facts = if can Proof.assert_chain state then Proof.the_facts state else []; wenzelm@17858: wenzelm@17858: val (thesis_var, thesis) = bind_judgment ctxt AutoBind.thesisN; wenzelm@17858: val varss = #1 (fold_map prep_vars raw_vars ctxt); wenzelm@17858: val vars = List.concat (map (fn (xs, T) => map (rpair T) xs) varss); wenzelm@17858: wenzelm@17974: fun check_result th = wenzelm@17974: (case Thm.prems_of th of wenzelm@17974: [prem] => wenzelm@17974: if Thm.concl_of th aconv thesis andalso wenzelm@17974: Logic.strip_assums_concl prem aconv thesis then () wenzelm@17974: else raise Proof.STATE ("Guessed a different clause:\n" ^ wenzelm@17974: ProofContext.string_of_thm ctxt th, state) wenzelm@17974: | [] => raise Proof.STATE ("Goal solved -- nothing guessed.", state) wenzelm@17858: | _ => raise Proof.STATE ("Guess split into several cases:\n" ^ wenzelm@17974: ProofContext.string_of_thm ctxt th, state)); wenzelm@17891: wenzelm@17858: fun guess_context raw_rule = wenzelm@17858: let wenzelm@17858: val (parms, rule) = match_params state vars raw_rule; wenzelm@17858: val ts = map (ProofContext.bind_skolem ctxt (map #1 parms) o Free) parms; wenzelm@17858: val ps = map dest_Free ts; wenzelm@17858: val asms = wenzelm@17858: Logic.strip_assums_hyp (Logic.nth_prem (1, Thm.prop_of rule)) wenzelm@17858: |> map (fn asm => (Library.foldl Term.betapply (Term.list_abs (ps, asm), ts), ([], []))); wenzelm@17974: val _ = conditional (null asms) (fn () => wenzelm@17974: raise Proof.STATE ("Trivial result -- nothing guessed", state)); wenzelm@17858: in wenzelm@17858: Proof.fix_i (map (fn (x, T) => ([x], SOME T)) parms) wenzelm@17858: #> Proof.assm_i (K (export_obtained state ts rule)) [(("", []), asms)] wenzelm@17974: #> Proof.add_binds_i AutoBind.no_facts wenzelm@17858: end; wenzelm@17858: wenzelm@17858: val before_qed = SOME (Method.primitive_text (fn th => wenzelm@17974: let val th' = Goal.conclude th wenzelm@17974: in th' COMP Drule.incr_indexes_wrt [] [] [] [th'] Drule.goalI end)); wenzelm@17974: fun after_qed _ [[res]] = wenzelm@17974: (check_result res; Proof.end_block #> Seq.map (`Proof.the_fact #-> guess_context)); wenzelm@17858: in wenzelm@17858: state wenzelm@17858: |> Proof.enter_forward wenzelm@17858: |> Proof.begin_block wenzelm@17858: |> Proof.fix_i [([AutoBind.thesisN], NONE)] wenzelm@17858: |> Proof.chain_facts chain_facts wenzelm@17858: |> Proof.local_goal (ProofDisplay.print_results int) (K I) (apsnd (rpair I)) wenzelm@17858: "guess" before_qed after_qed [(("", []), [Var (("guess", 0), propT)])] wenzelm@17974: |> Proof.refine (Method.primitive_text (K (Goal.init (Thm.cterm_of thy thesis)))) wenzelm@17858: end; wenzelm@17858: wenzelm@17858: in wenzelm@17858: wenzelm@17858: val guess = gen_guess ProofContext.read_vars; wenzelm@17858: val guess_i = gen_guess ProofContext.cert_vars; wenzelm@17858: wenzelm@17858: end; wenzelm@17858: wenzelm@17858: end;