CookBook/Tactical.thy
changeset 130 a21d7b300616
parent 129 e0d368a45537
child 131 8db9195bb3e9
equal deleted inserted replaced
129:e0d368a45537 130:a21d7b300616
  1025 section {* Simprocs *}
  1025 section {* Simprocs *}
  1026 
  1026 
  1027 text {*
  1027 text {*
  1028   In Isabelle you can also implement custom simplification procedures, called
  1028   In Isabelle you can also implement custom simplification procedures, called
  1029   \emph{simprocs}. Simprocs can be triggered on a specified term-pattern and
  1029   \emph{simprocs}. Simprocs can be triggered on a specified term-pattern and
  1030   rewrite a term according to a given theorem. They are useful in cases where
  1030   rewrite a term according to theorem. They are useful in cases where
  1031   a rewriting rule must be produced on ``demand'' or when rewriting by
  1031   a rewriting rule must be produced on ``demand'' or when rewriting by
  1032   simplification is too unpredictable and potentially loops.
  1032   simplification is too unpredictable and potentially loops.
  1033 
  1033 
  1034   To see how simprocs work, let us first write a simproc that just prints out
  1034   To see how simprocs work, let us first write a simproc that just prints out
  1035   the pattern that triggers it and otherwise does nothing. For this
  1035   the pattern that triggers it and otherwise does nothing. For this
  1048   This function takes a simpset and a redex (a @{ML_type cterm}) as
  1048   This function takes a simpset and a redex (a @{ML_type cterm}) as
  1049   argument. In Lines 3 and~4, we first extract the context from the given
  1049   argument. In Lines 3 and~4, we first extract the context from the given
  1050   simpset and then print out a message containing the redex.  The function
  1050   simpset and then print out a message containing the redex.  The function
  1051   returns @{ML NONE} (standing for an optional @{ML_type thm}) since at the
  1051   returns @{ML NONE} (standing for an optional @{ML_type thm}) since at the
  1052   moment we are \emph{not} interested in actually rewriting anything. We want
  1052   moment we are \emph{not} interested in actually rewriting anything. We want
  1053   that the simproc is triggered by the pattern @{term "Suc n"}. For this we
  1053   that the simproc is triggered by the pattern @{term "Suc n"}. This can be
  1054   can add the simproc with this pattern to the current simpset as follows
  1054   done by adding the simproc to the current simproc as follows
  1055 *}
  1055 *}
  1056 
  1056 
  1057 simproc_setup fail_sp ("Suc n") = {* K fail_sp_aux *}
  1057 simproc_setup fail_sp ("Suc n") = {* K fail_sp_aux *}
  1058 
  1058 
  1059 text {*
  1059 text {*
  1060   where the second argument specifies the pattern and the right-hand side
  1060   where the second argument specifies the pattern and the right-hand side
  1061   contains the code of the simproc (we have to use @{ML K} since we ignoring
  1061   contains the code of the simproc (we have to use @{ML K} since we ignoring
  1062   an argument). After this setup, the simplifier is aware of
  1062   an argument about a morphism\footnote{FIXME: what does the morphism do?}). 
  1063   this simproc and you can test whether it fires with the lemma
  1063   After this, the simplifier is aware of the simproc and you can test whether 
       
  1064   it fires on the lemma
  1064 *}
  1065 *}
  1065 
  1066 
  1066 lemma shows "Suc 0 = 1"
  1067 lemma shows "Suc 0 = 1"
  1067 apply(simp)
  1068 apply(simp)
  1068 (*<*)oops(*>*)
  1069 (*<*)oops(*>*)
  1069 
  1070 
  1070 text {*
  1071 text {*
  1071   This will print out the message twice: once for the left-hand side and
  1072   This will print out the message twice: once for the left-hand side and
  1072   once for the right-hand side. This is because during simplification the
  1073   once for the right-hand side. The reason is that during simplification the
  1073   simplifier will by default reduce the term @{term "1::nat"} to @{term "Suc
  1074   simplifier will at some point reduce the term @{term "1::nat"} to @{term "Suc
  1074   0"}, and then the simproc ``fires'' also on that term. 
  1075   0"}, and then the simproc ``fires'' also on that term. 
  1075 
  1076 
  1076   We can add or delete the simproc by the usual methods. For example
  1077   We can add or delete the simproc by the usual \isacommand{declare}-statement. 
  1077   the simproc will be deleted by the declaration:
  1078   For example the simproc will be deleted if you type:
  1078 *}
  1079 *}
  1079 
  1080 
  1080 declare [[simproc del: fail_sp]]
  1081 declare [[simproc del: fail_sp]]
  1081 
  1082 
  1082 text {*
  1083 text {*
  1090 (*<*)oops(*>*)
  1091 (*<*)oops(*>*)
  1091 
  1092 
  1092 text {*
  1093 text {*
  1093   (FIXME: should one use HOL-basic-ss or HOL-ss?)
  1094   (FIXME: should one use HOL-basic-ss or HOL-ss?)
  1094 
  1095 
  1095   Now the message shows up only once. 
  1096   Now the message shows up only once since @{term "1::nat"} is left unchanged. 
  1096 
  1097 
  1097   Setting up a simproc using the command \isacommand{setup\_simproc} will
  1098   Setting up a simproc using the command \isacommand{setup\_simproc} will
  1098   always add automatically the simproc to the current simpset. If you do not
  1099   always add automatically the simproc to the current simpset. If you do not
  1099   want this, then you have to use a slightly different method for setting 
  1100   want this, then you have to use a slightly different method for setting 
  1100   up the simproc. First the function @{ML fail_sp_aux} needs to be modified
  1101   up the simproc. First the function @{ML fail_sp_aux} needs to be modified
  1108 in
  1109 in
  1109   NONE
  1110   NONE
  1110 end*}
  1111 end*}
  1111 
  1112 
  1112 text {*
  1113 text {*
  1113   Here the redex is given as a @{ML_type term}, instead of a @{ML_type cterm}.
  1114   Here the redex is given as a @{ML_type term}, instead of a @{ML_type cterm}
  1114   We can turn this function into a simproc using
  1115   (therefore we printing it out using the function @{ML string_of_term in Syntax}).
       
  1116   We can turn this function into a proper simproc using
  1115 *}
  1117 *}
  1116 
  1118 
  1117 
  1119 
  1118 ML{*val fail_sp' = 
  1120 ML{*val fail_sp' = 
  1119       Simplifier.simproc_i @{theory} "fail_sp'" [@{term "Suc n"}] 
  1121   let 
  1120         (K fail_sp_aux')*}
  1122     val thy = @{theory}
       
  1123     val pat = [@{term "Suc n"}]
       
  1124   in
       
  1125     Simplifier.simproc_i thy "fail_sp'" pat (K fail_sp_aux')
       
  1126   end*}
  1121 
  1127 
  1122 text {*
  1128 text {*
  1123   Here the pattern is given as @{ML_type term} (instead of @{ML_type cterm}).
  1129   Here the pattern is given as @{ML_type term} (instead of @{ML_type cterm}).
  1124 
  1130   The function also takes a list of patterns that can trigger the simproc.
  1125   Simprocs are applied from inside to outside, from left to right. You can see
  1131   It might be interesting to notice that simprocs are applied from inside 
  1126   this in the proof
  1132   to outside and from left to right. You can see this in the proof
  1127 *}
  1133 *}
  1128 
  1134 
  1129 lemma shows "Suc (Suc 0) = (Suc 1)"
  1135 lemma shows "Suc (Suc 0) = (Suc 1)"
  1130 apply(tactic {* simp_tac (HOL_ss addsimprocs [fail_sp']) 1*})
  1136 apply(tactic {* simp_tac (HOL_ss addsimprocs [fail_sp']) 1*})
  1131 (*<*)oops(*>*)
  1137 (*<*)oops(*>*)
  1132 
  1138 
  1133 text {*
  1139 text {*
  1134   since it prints out the sequence @{term "Suc 0"}, @{term "Suc (Suc 0)"} and 
  1140   since the @{ML fail_sp'}-simproc prints out the sequence 
  1135   @{term "Suc 1"}.
  1141 
  1136 
  1142 @{text [display]
  1137   To see how a simproc applies a theorem  let us rewrite terms according to the 
  1143 "> Suc 0
  1138   equation:
  1144 > Suc (Suc 0) 
       
  1145 > Suc 1"}
       
  1146 
       
  1147   To see how a simproc applies a theorem  let us implement a simproc that
       
  1148   rewrites terms according to the equation:
  1139 *}
  1149 *}
  1140 
  1150 
  1141 lemma plus_one: 
  1151 lemma plus_one: 
  1142   shows "Suc n \<equiv> n + 1" by simp
  1152   shows "Suc n \<equiv> n + 1" by simp
  1143 
  1153 
  1144 text {*
  1154 text {*
  1145   The simproc expects the equation to be a meta-equation, however it can contain 
  1155   Simprocs expect that the given equation is a meta-equation, however the
  1146   possible preconditions (the simproc then only fires if the preconditions can be 
  1156   equation can contain preconditions (the simproc then will only fire if the 
  1147   solved). Let us further assume we want to only rewrite terms greater than 
  1157   preconditions can be solved). To see how rewriting can be tuned precisely, let us 
       
  1158   further assume we want that the simproc only rewrites terms greater than 
  1148   @{term "Suc 0"}. For this we can write
  1159   @{term "Suc 0"}. For this we can write
  1149 *}
  1160 *}
  1150 
  1161 
  1151 ML{*fun plus_one_sp_aux thy ss redex =
  1162 ML{*fun plus_one_sp_aux ss redex =
  1152   case redex of
  1163   case redex of
  1153     @{term "Suc 0"} => NONE
  1164     @{term "Suc 0"} => NONE
  1154   | _ => SOME @{thm plus_one}*}
  1165   | _ => SOME @{thm plus_one}*}
  1155 
  1166 
  1156 text {*
  1167 text {*
  1157   and set up the simproc as follows.
  1168   and set up the simproc as follows.
  1158 *}
  1169 *}
  1159 
  1170 
  1160 ML{*val plus_one_sproc = 
  1171 ML{*val plus_one_sp =
  1161        Simplifier.simproc_i @{theory} "sproc +1" [@{term "Suc n"}] 
  1172   let
  1162          plus_one_sp_aux*}
  1173     val thy = @{theory}
  1163 
  1174     val pat = [@{term "Suc n"}] 
  1164 text {*
  1175   in
  1165   Now the simproc fires one every term of the for @{term "Suc n"}, but
  1176     Simplifier.simproc_i thy "sproc +1" pat (K plus_one_sp_aux)
  1166   the lemma is only applied whenever the term is not @{term "Suc 0"}. So
  1177   end*}
  1167   in 
  1178 
       
  1179 text {*
       
  1180   Now the have set up the simproc so that it is triggered by terms
       
  1181   of the form @{term "Suc n"}, but inside the simproc we only produce
       
  1182   a theorem if the term is not @{term "Suc 0"}. The result you can see
       
  1183   in the following proof.
  1168 *}
  1184 *}
  1169 
  1185 
  1170 lemma shows "P (Suc (Suc (Suc 0))) (Suc 0)"
  1186 lemma shows "P (Suc (Suc (Suc 0))) (Suc 0)"
  1171 apply(tactic {* simp_tac (HOL_ss addsimprocs [plus_one_sproc]) 1*})
  1187 apply(tactic {* simp_tac (HOL_ss addsimprocs [plus_one_sp]) 1*})
  1172 txt{*
  1188 txt{*
  1173   the simproc produces the goal state
  1189   the simproc produces the goal state
  1174 
  1190 
  1175   @{subgoals[display]}
  1191   @{subgoals[display]}
  1176 *}  
  1192 *}  
  1177 (*<*)oops(*>*)
  1193 (*<*)oops(*>*)
  1178 
  1194 
  1179 text {*
  1195 text {*
  1180   where the first argument is rewritten, but not the second. With @{ML
  1196   As usual with simplification you have to be careful with loops: you already have
  1181   plus_one_sproc} you already have to be careful to not cause the simplifier
  1197   one @{ML plus_one_sp}, if you apply if with the default simpset (because
  1182   to loop. If we call this simproc together with the default simpset, we
  1198   the default simpset contains a rule which just undoes the rewriting 
  1183   already have a loop as it contains a rule which just undoes the rewriting of
  1199   @{ML plus_one_sp}).
  1184   the simproc.
  1200 
       
  1201   Let us next implement a simproc that replaces terms of the form @{term "Suc n"}
       
  1202   with the number @{text n} increase by one. First we implement a function that
       
  1203   takes a term and produces the corresponding integer value, if it can.
       
  1204 
  1185 *}
  1205 *}
  1186 
  1206 
  1187 ML{*fun dest_suc_trm ((Const (@{const_name "Suc"}, _)) $ t) = 1 + dest_suc_trm t
  1207 ML{*fun dest_suc_trm ((Const (@{const_name "Suc"}, _)) $ t) = 1 + dest_suc_trm t
  1188   | dest_suc_trm t = snd (HOLogic.dest_number t)*}
  1208   | dest_suc_trm t = snd (HOLogic.dest_number t)*}
  1189 
  1209 
  1190 text {* This function might raise @{ML TERM}. *}
  1210 text {* 
       
  1211   It uses the library function @{ML dest_number in HOLogic} that transforms
       
  1212   (Isabelle) terms, like @{term "0::nat"}, @{term "1::nat"}, @{term "2::nat"} and so
       
  1213   on, into integer values. This function raises the exception @{ML TERM} if
       
  1214   the term is not a number. The next function expects a pair consisting of a term
       
  1215   @{text t} and the corresponding integer value @{text n}. 
       
  1216 *}
       
  1217 
       
  1218 ML %linenosgray{*fun get_thm ctxt (t, n) =
       
  1219 let
       
  1220   val num = HOLogic.mk_number @{typ "nat"} n
       
  1221   val goal = HOLogic.mk_Trueprop (HOLogic.mk_eq (t,num))
       
  1222 in
       
  1223   mk_meta_eq (Goal.prove ctxt [] [] goal (K (simp_tac @{simpset} 1)))
       
  1224 end*}
       
  1225 
       
  1226 text {*
       
  1227   From the integer value it generates the corresponding Isabelle term, called 
       
  1228   @{text num} (Line 3), and then generates the equation @{text "t = num"} (Line 4)
       
  1229   which it proves by simplification in Line 6. The function @{ML mk_meta_eq} 
       
  1230   at the outside of the proof just transforms the equality to a meta-equality.
       
  1231 
       
  1232   Both functions can be stringed together in the function that produces the 
       
  1233   actual theorem for the simproc.
       
  1234 *}
  1191 
  1235 
  1192 ML{*fun nat_number_sp_aux ss t =
  1236 ML{*fun nat_number_sp_aux ss t =
  1193 let 
  1237 let 
  1194   val ctxt = Simplifier.the_context ss
  1238   val ctxt = Simplifier.the_context ss
  1195 
       
  1196   fun get_thm (t, n) =
       
  1197   let
       
  1198     val num = HOLogic.mk_number @{typ "nat"} n
       
  1199     val goal = HOLogic.mk_Trueprop (HOLogic.mk_eq (t,num))
       
  1200   in
       
  1201     Goal.prove ctxt [] [] goal (K (simp_tac @{simpset} 1))
       
  1202   end
       
  1203 in
  1239 in
  1204   SOME (mk_meta_eq (get_thm (t,dest_suc_trm t)))
  1240   SOME (get_thm ctxt (t, dest_suc_trm t))
  1205   handle TERM _ => NONE
  1241   handle TERM _ => NONE
  1206 end*}
  1242 end*}
  1207 
  1243 
  1208 text {*
  1244 text {*
  1209   (FIXME: is @{text "@{simpset}"} kosher here? Otherwise the following loops.)
  1245   (FIXME: is @{text "@{simpset}"} kosher here? Otherwise the following will loop.)
  1210 *}
  1246 
  1211 
  1247   This function uses the fact that @{ML dest_suc_trm} might throw an exception 
  1212 ML{*val nat_number_sp = 
  1248   @{ML TERM}. In this case there is nothing that can be rewritten and therefore no
  1213        Simplifier.simproc_i @{theory} "nat_number" [@{term "Suc n"}] 
  1249   theorem is produced. To try out the simproc on an example, you can set it up as 
  1214          (K nat_number_sp_aux) *}
  1250   follows:
       
  1251 *}
       
  1252 
       
  1253 ML{*val nat_number_sp =
       
  1254   let
       
  1255     val thy = @{theory}
       
  1256     val pat = [@{term "Suc n"}]
       
  1257   in 
       
  1258     Simplifier.simproc_i thy "nat_number" pat (K nat_number_sp_aux) 
       
  1259   end*}
       
  1260 
       
  1261 text {* 
       
  1262   Now in the lemma
       
  1263   *}
  1215 
  1264 
  1216 lemma "P (Suc (Suc 2)) (Suc 99) (0::nat) (Suc 4 + Suc 0) (Suc (0 + 0))"
  1265 lemma "P (Suc (Suc 2)) (Suc 99) (0::nat) (Suc 4 + Suc 0) (Suc (0 + 0))"
  1217   apply(tactic {* simp_tac (HOL_ss addsimprocs [nat_number_sp]) 1*})
  1266   apply(tactic {* simp_tac (HOL_ss addsimprocs [nat_number_sp]) 1*})
  1218 txt {* 
  1267 txt {* 
       
  1268   you obtain the more legible goal state
       
  1269   
  1219   @{subgoals [display]}
  1270   @{subgoals [display]}
  1220   *}
  1271   *}
  1221 (*<*)oops(*>*)
  1272 (*<*)oops(*>*)
  1222 
  1273 
       
  1274 text {*
       
  1275   where the simproc rewrites all @{term "Suc"}s except in the last arguments. There it cannot 
       
  1276   rewrite anything, because it does not know how to transform the term @{term "Suc (0 + 0)"}
       
  1277   into a number. To solve this problem have a look at the next exercise. 
       
  1278 
       
  1279   \begin{exercise}\label{ex:addsimproc}
       
  1280   Write a simproc that replaces terms of the form @{term "t\<^isub>1 + t\<^isub>2"} by their 
       
  1281   result. You can assume the terms are ``proper'' numbers, that is of the form
       
  1282   @{term "0::nat"}, @{term "1::nat"}, @{term "2::nat"} and so on.
       
  1283   \end{exercise}
       
  1284 
       
  1285   (FIXME: We did not do anything with morphisms. Anything interesting
       
  1286   one can say about them?)
       
  1287 *}
  1223 
  1288 
  1224 section {* Structured Proofs *}
  1289 section {* Structured Proofs *}
  1225 
  1290 
  1226 text {* TBD *}
  1291 text {* TBD *}
  1227 
  1292