CookBook/Tactical.thy
changeset 149 253ea99c1441
parent 148 84d1392186d3
child 150 cb39c41548bd
child 169 d3fcc1a0272c
equal deleted inserted replaced
148:84d1392186d3 149:253ea99c1441
   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 {*
  1112 end*}
  1113 end*}
  1113 
  1114 
  1114 text {*
  1115 text {*
  1115   Here the redex is given as a @{ML_type term}, instead of a @{ML_type cterm}
  1116   Here the redex is given as a @{ML_type term}, instead of a @{ML_type cterm}
  1116   (therefore we printing it out using the function @{ML string_of_term in Syntax}).
  1117   (therefore we printing it out using the function @{ML string_of_term in Syntax}).
  1117   We can turn this function into a proper simproc using
  1118   We can turn this function into a proper simproc using the function 
       
  1119   @{ML Simplifier.simproc_i}:
  1118 *}
  1120 *}
  1119 
  1121 
  1120 
  1122 
  1121 ML{*val fail_sp' = 
  1123 ML{*val fail_sp' = 
  1122 let 
  1124 let 
  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}.