309 the subgoals. So after setting up the lemma, the goal state is always of the |
309 the subgoals. So after setting up the lemma, the goal state is always of the |
310 form @{text "C \<Longrightarrow> (C)"}; when the proof is finished we are left with @{text |
310 form @{text "C \<Longrightarrow> (C)"}; when the proof is finished we are left with @{text |
311 "(C)"}. Since the goal @{term C} can potentially be an implication, there is |
311 "(C)"}. Since the goal @{term C} can potentially be an implication, there is |
312 a ``protector'' wrapped around it (in from of an outermost constant @{text |
312 a ``protector'' wrapped around it (in from of an outermost constant @{text |
313 "Const (\"prop\", bool \<Rightarrow> bool)"}; however this constant |
313 "Const (\"prop\", bool \<Rightarrow> bool)"}; however this constant |
314 is invisible in the figure). This prevents that premises of @{text C} are |
314 is invisible in the figure). This wrapper prevents that premises of @{text C} are |
315 mis-interpreted as open subgoals. While tactics can operate on the subgoals |
315 mis-interpreted as open subgoals. While tactics can operate on the subgoals |
316 (the @{text "A\<^isub>i"} above), they are expected to leave the conclusion |
316 (the @{text "A\<^isub>i"} above), they are expected to leave the conclusion |
317 @{term C} intact, with the exception of possibly instantiating schematic |
317 @{term C} intact, with the exception of possibly instantiating schematic |
318 variables. If you use the predefined tactics, which we describe in the next |
318 variables. If you use the predefined tactics, which we describe in the next |
319 section, this will always be the case. |
319 section, this will always be the case. |
451 @{subgoals [display]} |
451 @{subgoals [display]} |
452 \end{minipage}*} |
452 \end{minipage}*} |
453 (*<*)oops(*>*) |
453 (*<*)oops(*>*) |
454 |
454 |
455 text {* |
455 text {* |
456 where the application of Rule @{text bspec} generates two subgoals involving the |
456 where the application of rule @{text bspec} generates two subgoals involving the |
457 meta-variable @{text "?x"}. Now, if you are not careful, tactics |
457 meta-variable @{text "?x"}. Now, if you are not careful, tactics |
458 applied to the first subgoal might instantiate this meta-variable in such a |
458 applied to the first subgoal might instantiate this meta-variable in such a |
459 way that the second subgoal becomes unprovable. If it is clear what the @{text "?x"} |
459 way that the second subgoal becomes unprovable. If it is clear what the @{text "?x"} |
460 should be, then this situation can be avoided by introducing a more |
460 should be, then this situation can be avoided by introducing a more |
461 constraint version of the @{text bspec}-rule. Such constraints can be given by |
461 constraint version of the @{text bspec}-rule. Such constraints can be given by |
574 conclusion: & @{term "B y x \<longrightarrow> C (z y) x"}\\ |
574 conclusion: & @{term "B y x \<longrightarrow> C (z y) x"}\\ |
575 premises: & @{term "A x y"} |
575 premises: & @{term "A x y"} |
576 \end{tabular} |
576 \end{tabular} |
577 \end{quote} |
577 \end{quote} |
578 |
578 |
579 Note in the actual output the brown colour of the variables @{term x} and |
579 Notice in the actual output the brown colour of the variables @{term x} and |
580 @{term y}. Although they are parameters in the original goal, they are fixed inside |
580 @{term y}. Although they are parameters in the original goal, they are fixed inside |
581 the subproof. By convention these fixed variables are printed in brown colour. |
581 the subproof. By convention these fixed variables are printed in brown colour. |
582 Similarly the schematic variable @{term z}. The assumption, or premise, |
582 Similarly the schematic variable @{term z}. The assumption, or premise, |
583 @{prop "A x y"} is bound as @{ML_type cterm} to the record-variable |
583 @{prop "A x y"} is bound as @{ML_type cterm} to the record-variable |
584 @{text asms}, but also as @{ML_type thm} to @{text prems}. |
584 @{text asms}, but also as @{ML_type thm} to @{text prems}. |
725 text {* |
725 text {* |
726 then we have to be careful to not apply the tactic to the two subgoals produced by |
726 then we have to be careful to not apply the tactic to the two subgoals produced by |
727 the first goal. To do this can result in quite messy code. In contrast, |
727 the first goal. To do this can result in quite messy code. In contrast, |
728 the ``reverse application'' is easy to implement. |
728 the ``reverse application'' is easy to implement. |
729 |
729 |
730 (FIXME: mention @{ML "CSUBGOAL"}) |
730 The tactic @{ML "CSUBGOAL"} is similar to @{ML SUBGOAL} except that it takes |
731 |
731 a @{ML_type cterm} instead of a @{ML_type term}. Of course, this example is |
732 Of course, this example is contrived: there are much simpler methods available in |
732 contrived: there are much simpler methods available in Isabelle for |
733 Isabelle for implementing a proof procedure analysing a goal according to its topmost |
733 implementing a proof procedure analysing a goal according to its topmost |
734 connective. These simpler methods use tactic combinators, which we will explain |
734 connective. These simpler methods use tactic combinators, which we will |
735 in the next section. |
735 explain in the next section. |
|
736 |
736 *} |
737 *} |
737 |
738 |
738 section {* Tactic Combinators *} |
739 section {* Tactic Combinators *} |
739 |
740 |
740 text {* |
741 text {* |
1025 |
1026 |
1026 section {* Simprocs *} |
1027 section {* Simprocs *} |
1027 |
1028 |
1028 text {* |
1029 text {* |
1029 In Isabelle you can also implement custom simplification procedures, called |
1030 In Isabelle you can also implement custom simplification procedures, called |
1030 \emph{simprocs}. Simprocs can be triggered on a specified term-pattern and |
1031 \emph{simprocs}. Simprocs can be triggered by the simplifier on a specified |
1031 rewrite a term according to a theorem. They are useful in cases where |
1032 term-pattern and rewrite a term according to a theorem. They are useful in |
1032 a rewriting rule must be produced on ``demand'' or when rewriting by |
1033 cases where a rewriting rule must be produced on ``demand'' or when |
1033 simplification is too unpredictable and potentially loops. |
1034 rewriting by simplification is too unpredictable and potentially loops. |
1034 |
1035 |
1035 To see how simprocs work, let us first write a simproc that just prints out |
1036 To see how simprocs work, let us first write a simproc that just prints out |
1036 the pattern which triggers it and otherwise does nothing. For this |
1037 the pattern which triggers it and otherwise does nothing. For this |
1037 you can use the function: |
1038 you can use the function: |
1038 *} |
1039 *} |
1050 arguments. In Lines 3 and~4, we first extract the context from the given |
1051 arguments. In Lines 3 and~4, we first extract the context from the given |
1051 simpset and then print out a message containing the redex. The function |
1052 simpset and then print out a message containing the redex. The function |
1052 returns @{ML NONE} (standing for an optional @{ML_type thm}) since at the |
1053 returns @{ML NONE} (standing for an optional @{ML_type thm}) since at the |
1053 moment we are \emph{not} interested in actually rewriting anything. We want |
1054 moment we are \emph{not} interested in actually rewriting anything. We want |
1054 that the simproc is triggered by the pattern @{term "Suc n"}. This can be |
1055 that the simproc is triggered by the pattern @{term "Suc n"}. This can be |
1055 done by adding the simproc to the current simproc as follows |
1056 done by adding the simproc to the current simpset as follows |
1056 *} |
1057 *} |
1057 |
1058 |
1058 simproc_setup fail_sp ("Suc n") = {* K fail_sp_aux *} |
1059 simproc_setup fail_sp ("Suc n") = {* K fail_sp_aux *} |
1059 |
1060 |
1060 text {* |
1061 text {* |
1128 |
1130 |
1129 text {* |
1131 text {* |
1130 Here the pattern is given as @{ML_type term} (instead of @{ML_type cterm}). |
1132 Here the pattern is given as @{ML_type term} (instead of @{ML_type cterm}). |
1131 The function also takes a list of patterns that can trigger the simproc. |
1133 The function also takes a list of patterns that can trigger the simproc. |
1132 Now the simproc is set up and can be explicitly added using |
1134 Now the simproc is set up and can be explicitly added using |
1133 {ML addsimprocs} to a simpset whenerver |
1135 @{ML addsimprocs} to a simpset whenerver |
1134 needed. |
1136 needed. |
1135 |
1137 |
1136 Simprocs are applied from inside to outside and from left to right. You can |
1138 Simprocs are applied from inside to outside and from left to right. You can |
1137 see this in the proof |
1139 see this in the proof |
1138 *} |
1140 *} |
1139 |
1141 |
1140 lemma shows "Suc (Suc 0) = (Suc 1)" |
1142 lemma shows "Suc (Suc 0) = (Suc 1)" |
1141 apply(tactic {* simp_tac (HOL_ss addsimprocs [fail_sp']) 1*}) |
1143 apply(tactic {* simp_tac (HOL_basic_ss addsimprocs [fail_sp']) 1*}) |
1142 (*<*)oops(*>*) |
1144 (*<*)oops(*>*) |
1143 |
1145 |
1144 text {* |
1146 text {* |
1145 The simproc @{ML fail_sp'} prints out the sequence |
1147 The simproc @{ML fail_sp'} prints out the sequence |
1146 |
1148 |
1188 a theorem if the term is not @{term "Suc 0"}. The result you can see |
1190 a theorem if the term is not @{term "Suc 0"}. The result you can see |
1189 in the following proof |
1191 in the following proof |
1190 *} |
1192 *} |
1191 |
1193 |
1192 lemma shows "P (Suc (Suc (Suc 0))) (Suc 0)" |
1194 lemma shows "P (Suc (Suc (Suc 0))) (Suc 0)" |
1193 apply(tactic {* simp_tac (HOL_ss addsimprocs [plus_one_sp]) 1*}) |
1195 apply(tactic {* simp_tac (HOL_basic_ss addsimprocs [plus_one_sp]) 1*}) |
1194 txt{* |
1196 txt{* |
1195 where the simproc produces the goal state |
1197 where the simproc produces the goal state |
1196 |
1198 |
1197 @{subgoals[display]} |
1199 @{subgoals[display]} |
1198 *} |
1200 *} |
1206 in choosing the right simpset to which you add a simproc. |
1208 in choosing the right simpset to which you add a simproc. |
1207 |
1209 |
1208 Next let us implement a simproc that replaces terms of the form @{term "Suc n"} |
1210 Next let us implement a simproc that replaces terms of the form @{term "Suc n"} |
1209 with the number @{text n} increase by one. First we implement a function that |
1211 with the number @{text n} increase by one. First we implement a function that |
1210 takes a term and produces the corresponding integer value. |
1212 takes a term and produces the corresponding integer value. |
1211 |
|
1212 *} |
1213 *} |
1213 |
1214 |
1214 ML{*fun dest_suc_trm ((Const (@{const_name "Suc"}, _)) $ t) = 1 + dest_suc_trm t |
1215 ML{*fun dest_suc_trm ((Const (@{const_name "Suc"}, _)) $ t) = 1 + dest_suc_trm t |
1215 | dest_suc_trm t = snd (HOLogic.dest_number t)*} |
1216 | dest_suc_trm t = snd (HOLogic.dest_number t)*} |
1216 |
1217 |
1410 You can see how this function works with the example |
1411 You can see how this function works with the example |
1411 @{term "True \<and> (Foo \<longrightarrow> Bar)"}: |
1412 @{term "True \<and> (Foo \<longrightarrow> Bar)"}: |
1412 |
1413 |
1413 @{ML_response_fake [display,gray] |
1414 @{ML_response_fake [display,gray] |
1414 "let |
1415 "let |
1415 val ctrm = @{cterm \"True \<and> (Foo \<longrightarrow> Bar)\"} |
1416 val ctrm = @{cterm \"True \<and> (Foo \<longrightarrow> Bar)\"} |
1416 in |
1417 in |
1417 Conv.rewr_conv @{thm true_conj1} ctrm |
1418 Conv.rewr_conv @{thm true_conj1} ctrm |
1418 end" |
1419 end" |
1419 "True \<and> (Foo \<longrightarrow> Bar) \<equiv> Foo \<longrightarrow> Bar"} |
1420 "True \<and> (Foo \<longrightarrow> Bar) \<equiv> Foo \<longrightarrow> Bar"} |
1420 |
1421 |
1437 (conv1 then_conv conv2) ctrm |
1438 (conv1 then_conv conv2) ctrm |
1438 end" |
1439 end" |
1439 "(\<lambda>x. x \<and> False) True \<equiv> False"} |
1440 "(\<lambda>x. x \<and> False) True \<equiv> False"} |
1440 |
1441 |
1441 where we first beta-reduce the term and then rewrite according to |
1442 where we first beta-reduce the term and then rewrite according to |
1442 @{thm [source] true_conj1}. |
1443 @{thm [source] true_conj1}. (Recall the problem with the pretty-printer |
|
1444 normalising all terms.) |
1443 |
1445 |
1444 The conversion combinator @{ML else_conv} tries out the |
1446 The conversion combinator @{ML else_conv} tries out the |
1445 first one, and if it does not apply, tries the second. For example |
1447 first one, and if it does not apply, tries the second. For example |
1446 |
1448 |
1447 @{ML_response_fake [display,gray] |
1449 @{ML_response_fake [display,gray] |
1457 Here the conversion of @{thm [source] true_conj1} only applies |
1459 Here the conversion of @{thm [source] true_conj1} only applies |
1458 in the first case, but fails in the second. The whole conversion |
1460 in the first case, but fails in the second. The whole conversion |
1459 does not fail, however, because the combinator @{ML else_conv} will then |
1461 does not fail, however, because the combinator @{ML else_conv} will then |
1460 try out @{ML Conv.all_conv}, which always succeeds. |
1462 try out @{ML Conv.all_conv}, which always succeeds. |
1461 |
1463 |
1462 Apart from the function @{ML beta_conversion in Thm}, which able to fully |
1464 Apart from the function @{ML beta_conversion in Thm}, which is able to fully |
1463 beta-normalise a term, the restriction of conversions so far is that they |
1465 beta-normalise a term, the conversions so far are restricted in that they |
1464 only apply to the outer-most level of a @{ML_type cterm}. In what follows we |
1466 only apply to the outer-most level of a @{ML_type cterm}. In what follows we |
1465 will lift this restriction. The combinator @{ML Conv.arg_conv} will apply |
1467 will lift this restriction. The combinator @{ML Conv.arg_conv} will apply |
1466 the conversion on the first argument of an application, that is the term |
1468 the conversion to the first argument of an application, that is the term |
1467 must be of the form @{ML "t1 $ t2" for t1 t2} and the conversion is applied |
1469 must be of the form @{ML "t1 $ t2" for t1 t2} and the conversion is applied |
1468 to @{text t2}. For example |
1470 to @{text t2}. For example |
1469 |
1471 |
1470 @{ML_response_fake [display,gray] |
1472 @{ML_response_fake [display,gray] |
1471 "let |
1473 "let |
1475 Conv.arg_conv conv ctrm |
1477 Conv.arg_conv conv ctrm |
1476 end" |
1478 end" |
1477 "P \<or> (True \<and> Q) \<equiv> P \<or> Q"} |
1479 "P \<or> (True \<and> Q) \<equiv> P \<or> Q"} |
1478 |
1480 |
1479 The reason for this behaviour is that @{text "(op \<or>)"} expects two |
1481 The reason for this behaviour is that @{text "(op \<or>)"} expects two |
1480 arguments. So the term must be of the form @{text "Const \<dots> $ t1 $ t2"}. The |
1482 arguments. So the term must be of the form @{text "(Const \<dots> $ t1) $ t2"}. The |
1481 conversion is then applied to @{text "t2"} which in the example above |
1483 conversion is then applied to @{text "t2"} which in the example above |
1482 stands for @{term "True \<and> Q"}. |
1484 stands for @{term "True \<and> Q"}. |
1483 |
1485 |
1484 The function @{ML Conv.abs_conv} applies a conversion under an abstractions. |
1486 The function @{ML Conv.abs_conv} applies a conversion under an abstractions. |
1485 For example: |
1487 For example: |
1513 | _ => Conv.all_conv ctrm*} |
1515 | _ => Conv.all_conv ctrm*} |
1514 |
1516 |
1515 text {* |
1517 text {* |
1516 This functions descends under applications (Line 6 and 7) and abstractions |
1518 This functions descends under applications (Line 6 and 7) and abstractions |
1517 (Line 8); and ``fires'' if the outer-most constant is an @{text "True \<and> \<dots>"} |
1519 (Line 8); and ``fires'' if the outer-most constant is an @{text "True \<and> \<dots>"} |
1518 (Lines 3-5); otherwise it leaves the term unchanged (Line 9). To see this |
1520 (Lines 3-5); otherwise it leaves the term unchanged (Line 9). In Line 2 |
1519 conversion in action, consider the following example. |
1521 we need to transform the @{ML_type cterm} into a @{ML_type term} in order |
|
1522 to be able to pattern-match the term. To see this |
|
1523 conversion in action, consider the following example |
1520 |
1524 |
1521 @{ML_response_fake [display,gray] |
1525 @{ML_response_fake [display,gray] |
1522 "let |
1526 "let |
1523 val ctxt = @{context} |
1527 val ctxt = @{context} |
1524 val ctrm = @{cterm \"distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x\"} |
1528 val ctrm = @{cterm \"distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x\"} |
1525 in |
1529 in |
1526 all_true1_conv ctxt ctrm |
1530 all_true1_conv ctxt ctrm |
1527 end" |
1531 end" |
1528 "distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x \<equiv> distinct [1, x] \<longrightarrow> 1 \<noteq> x"} |
1532 "distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x \<equiv> distinct [1, x] \<longrightarrow> 1 \<noteq> x"} |
1529 |
1533 |
1530 To see how much control you have about rewriting via conversions, let us |
1534 where the conversion is applied ``deep'' inside the term. |
|
1535 |
|
1536 To see how much control you have about rewriting by using conversions, let us |
1531 make the task a bit more complicated by rewriting according to the rule |
1537 make the task a bit more complicated by rewriting according to the rule |
1532 @{text true_conj1}, but only in the first arguments of @{term If}s. The |
1538 @{text true_conj1}, but only in the first arguments of @{term If}s. Then |
1533 the conversion should be as follows. |
1539 the conversion should be as follows. |
1534 *} |
1540 *} |
1535 |
1541 |
1536 ML{*fun if_true1_conv ctxt ctrm = |
1542 ML{*fun if_true1_conv ctxt ctrm = |
1537 case Thm.term_of ctrm of |
1543 case Thm.term_of ctrm of |
1541 (if_true1_conv ctxt) (if_true1_conv ctxt) ctrm |
1547 (if_true1_conv ctxt) (if_true1_conv ctxt) ctrm |
1542 | Abs _ => Conv.abs_conv (fn (_, ctxt) => if_true1_conv ctxt) ctxt ctrm |
1548 | Abs _ => Conv.abs_conv (fn (_, ctxt) => if_true1_conv ctxt) ctxt ctrm |
1543 | _ => Conv.all_conv ctrm *} |
1549 | _ => Conv.all_conv ctrm *} |
1544 |
1550 |
1545 text {* |
1551 text {* |
1546 Here is an application of this conversion: |
1552 Here is an example for this conversion: |
1547 |
1553 |
1548 @{ML_response_fake [display,gray] |
1554 @{ML_response_fake [display,gray] |
1549 "let |
1555 "let |
1550 val ctxt = @{context} |
1556 val ctxt = @{context} |
1551 val ctrm = |
1557 val ctrm = |
1558 *} |
1564 *} |
1559 |
1565 |
1560 text {* |
1566 text {* |
1561 So far we only applied conversions to @{ML_type cterm}s. Conversions can, however, |
1567 So far we only applied conversions to @{ML_type cterm}s. Conversions can, however, |
1562 also work on theorems using the function @{ML "Conv.fconv_rule"}. As an example, |
1568 also work on theorems using the function @{ML "Conv.fconv_rule"}. As an example, |
1563 consider @{ML all_true1_conv} and the lemma: |
1569 consider the conversion @{ML all_true1_conv} and the lemma: |
1564 *} |
1570 *} |
1565 |
1571 |
1566 lemma foo_test: "P \<or> (True \<and> \<not>P)" by simp |
1572 lemma foo_test: "P \<or> (True \<and> \<not>P)" by simp |
1567 |
1573 |
1568 text {* |
1574 text {* |
1581 Q"}); the function @{ML Conv.prems_conv} for applying the conversion to all |
1587 Q"}); the function @{ML Conv.prems_conv} for applying the conversion to all |
1582 premises of a goal, and @{ML Conv.concl_conv} for applying the conversion to |
1588 premises of a goal, and @{ML Conv.concl_conv} for applying the conversion to |
1583 the conclusion of a goal. |
1589 the conclusion of a goal. |
1584 |
1590 |
1585 Assume we want to apply @{ML all_true1_conv} only in the conclusion |
1591 Assume we want to apply @{ML all_true1_conv} only in the conclusion |
1586 of the goal, and @{ML if_true1_conv} should only be applied in the premises. |
1592 of the goal, and @{ML if_true1_conv} should only be applied to the premises. |
1587 Here is a tactic doing exactly that: |
1593 Here is a tactic doing exactly that: |
1588 *} |
1594 *} |
1589 |
1595 |
1590 ML{*val true1_tac = CSUBGOAL (fn (goal, i) => |
1596 ML{*val true1_tac = CSUBGOAL (fn (goal, i) => |
1591 let |
1597 let |
1609 "if True \<and> P then P else True \<and> False \<Longrightarrow> |
1615 "if True \<and> P then P else True \<and> False \<Longrightarrow> |
1610 (if True \<and> Q then True \<and> Q else P) \<longrightarrow> True \<and> (True \<and> Q)" |
1616 (if True \<and> Q then True \<and> Q else P) \<longrightarrow> True \<and> (True \<and> Q)" |
1611 apply(tactic {* true1_tac 1 *}) |
1617 apply(tactic {* true1_tac 1 *}) |
1612 txt {* where the tactic yields the goal state |
1618 txt {* where the tactic yields the goal state |
1613 |
1619 |
1614 \begin{minipage}{\textwidth} |
1620 @{subgoals [display]}*} |
1615 @{subgoals [display]} |
|
1616 \end{minipage} *} |
|
1617 (*<*)oops(*>*) |
1621 (*<*)oops(*>*) |
1618 |
1622 |
1619 text {* |
1623 text {* |
1620 As you can see, the premises are rewritten according to @{ML if_true1_conv}, while |
1624 As you can see, the premises are rewritten according to @{ML if_true1_conv}, while |
1621 the conclusion according to @{ML all_true1_conv}. |
1625 the conclusion according to @{ML all_true1_conv}. |