130 |
130 |
131 (FIXME @{ML Toplevel.debug} @{ML Toplevel.profiling}) |
131 (FIXME @{ML Toplevel.debug} @{ML Toplevel.profiling}) |
132 *} |
132 *} |
133 |
133 |
134 (* |
134 (* |
135 ML {* set Toplevel.debug *} |
135 ML {* reset Toplevel.debug *} |
136 |
136 |
137 ML {* |
137 ML {* fun dodgy_fun () = (raise TYPE ("",[],[]); 1) *} |
138 fun dodgy_fun () = (raise (ERROR "bar"); 1) |
138 |
139 *} |
139 ML {* fun innocent () = dodgy_fun () *} |
140 |
140 ML {* exception_trace (fn () => cterm_of @{theory} (Bound 0)) *} |
141 ML {* fun f1 () = dodgy_fun () *} |
141 ML {* exception_trace (fn () => innocent ()) *} |
142 |
142 |
143 ML {* f1 () *} |
143 ML {* Toplevel.program (fn () => cterm_of @{theory} (Bound 0)) *} |
|
144 |
|
145 ML {* Toplevel.program (fn () => innocent ()) *} |
144 *) |
146 *) |
145 |
147 |
146 text {* |
148 text {* |
147 Most often you want to inspect data of type @{ML_type term}, @{ML_type cterm} |
149 Most often you want to inspect data of type @{ML_type term}, @{ML_type cterm} |
148 or @{ML_type thm}. Isabelle contains elaborate pretty-printing functions for printing them, |
150 or @{ML_type thm}. Isabelle contains elaborate pretty-printing functions for printing them, |
205 |
207 |
206 fun str_of_thm_no_vars ctxt thm = |
208 fun str_of_thm_no_vars ctxt thm = |
207 str_of_cterm ctxt (#prop (crep_thm (no_vars ctxt thm)))*} |
209 str_of_cterm ctxt (#prop (crep_thm (no_vars ctxt thm)))*} |
208 |
210 |
209 text {* |
211 text {* |
210 Theorem @{thm [source] conjI} looks now as follows: |
212 Theorem @{thm [source] conjI} is now printed as follows: |
211 |
213 |
212 @{ML_response_fake [display, gray] |
214 @{ML_response_fake [display, gray] |
213 "warning (str_of_thm_no_vars @{context} @{thm conjI})" |
215 "warning (str_of_thm_no_vars @{context} @{thm conjI})" |
214 "\<lbrakk>P; Q\<rbrakk> \<Longrightarrow> P \<and> Q"} |
216 "\<lbrakk>P; Q\<rbrakk> \<Longrightarrow> P \<and> Q"} |
215 |
217 |
518 local_setup %gray {* |
520 local_setup %gray {* |
519 snd o LocalTheory.define Thm.internalK |
521 snd o LocalTheory.define Thm.internalK |
520 ((@{binding "TrueConj"}, NoSyn), |
522 ((@{binding "TrueConj"}, NoSyn), |
521 (Attrib.empty_binding, @{term "True \<and> True"})) *} |
523 (Attrib.empty_binding, @{term "True \<and> True"})) *} |
522 |
524 |
523 <<<<<<< local |
|
524 text {* |
525 text {* |
525 Now querying the definition you obtain: |
526 Now querying the definition you obtain: |
526 |
527 |
527 \begin{isabelle} |
528 \begin{isabelle} |
528 \isacommand{thm}~@{text "TrueConj_def"}\\ |
529 \isacommand{thm}~@{text "TrueConj_def"}\\ |
558 @{ML_response [display,gray] |
559 @{ML_response [display,gray] |
559 "@{term \"(a::nat) + b = c\"}" |
560 "@{term \"(a::nat) + b = c\"}" |
560 "Const (\"op =\", \<dots>) $ |
561 "Const (\"op =\", \<dots>) $ |
561 (Const (\"HOL.plus_class.plus\", \<dots>) $ \<dots> $ \<dots>) $ \<dots>"} |
562 (Const (\"HOL.plus_class.plus\", \<dots>) $ \<dots> $ \<dots>) $ \<dots>"} |
562 |
563 |
563 will show the term @{term "(a::nat) + b = c"}, but printed using an internal |
564 will show the term @{term "(a::nat) + b = c"}, but printed using the internal |
564 representation corresponding to the data type @{ML_type "term"}. |
565 representation corresponding to the data type @{ML_type "term"}. |
565 |
566 |
566 This internal representation uses the usual de Bruijn index mechanism---where |
567 This internal representation uses the usual de Bruijn index mechanism---where |
567 bound variables are represented by the constructor @{ML Bound}. The index in |
568 bound variables are represented by the constructor @{ML Bound}. The index in |
568 @{ML Bound} refers to the number of Abstractions (@{ML Abs}) we have to skip |
569 @{ML Bound} refers to the number of Abstractions (@{ML Abs}) we have to skip |
933 you obtain the certified type for the Isabelle type @{typ "nat \<Rightarrow> bool"} on |
934 you obtain the certified type for the Isabelle type @{typ "nat \<Rightarrow> bool"} on |
934 the ML-level as follows: |
935 the ML-level as follows: |
935 |
936 |
936 @{ML_response_fake [display,gray] |
937 @{ML_response_fake [display,gray] |
937 "ctyp_of @{theory} (@{typ nat} --> @{typ bool})" |
938 "ctyp_of @{theory} (@{typ nat} --> @{typ bool})" |
|
939 "nat \<Rightarrow> bool"} |
|
940 |
|
941 or with the antiquotation: |
|
942 |
|
943 @{ML_response_fake [display,gray] |
|
944 "@{ctyp \"nat \<Rightarrow> bool\"}" |
938 "nat \<Rightarrow> bool"} |
945 "nat \<Rightarrow> bool"} |
939 |
946 |
940 \begin{readmore} |
947 \begin{readmore} |
941 For functions related to @{ML_type cterm}s and @{ML_type ctyp}s see |
948 For functions related to @{ML_type cterm}s and @{ML_type ctyp}s see |
942 the file @{ML_file "Pure/thm.ML"}. |
949 the file @{ML_file "Pure/thm.ML"}. |
1070 For the functions @{text "assume"}, @{text "forall_elim"} etc |
1077 For the functions @{text "assume"}, @{text "forall_elim"} etc |
1071 see \isccite{sec:thms}. The basic functions for theorems are defined in |
1078 see \isccite{sec:thms}. The basic functions for theorems are defined in |
1072 @{ML_file "Pure/thm.ML"}. |
1079 @{ML_file "Pure/thm.ML"}. |
1073 \end{readmore} |
1080 \end{readmore} |
1074 |
1081 |
1075 (FIXME: handy functions working on theorems; how to add case-names to goal |
1082 (FIXME: handy functions working on theorems, like @{ML ObjectLogic.rulify} and so on) |
1076 states - maybe in the next section) |
1083 |
|
1084 (FIXME how to add case-names to goal states - maybe in the |
|
1085 next section) |
1077 *} |
1086 *} |
1078 |
1087 |
1079 section {* Theorem Attributes *} |
1088 section {* Theorem Attributes *} |
1080 |
1089 |
1081 text {* |
1090 text {* |
1097 @{text "> HOL.elim: declaration of Classical elimination rule"}\\ |
1106 @{text "> HOL.elim: declaration of Classical elimination rule"}\\ |
1098 @{text "> \<dots>"} |
1107 @{text "> \<dots>"} |
1099 \end{isabelle} |
1108 \end{isabelle} |
1100 |
1109 |
1101 The theorem attributes fall roughly into two categories: the first category manipulates |
1110 The theorem attributes fall roughly into two categories: the first category manipulates |
1102 the proved theorem (like @{text "[symmetric]"} and @{text "[THEN \<dots>]"}), and the second |
1111 the proved theorem (for example @{text "[symmetric]"} and @{text "[THEN \<dots>]"}), and the second |
1103 stores the proved theorem somewhere as data (like @{text "[simp]"}, which adds |
1112 stores the proved theorem somewhere as data (for example @{text "[simp]"}, which adds |
1104 the theorem to the current simpset). |
1113 the theorem to the current simpset). |
1105 |
1114 |
1106 To explain how to write your own attribute, let us start with an extremely simple |
1115 To explain how to write your own attribute, let us start with an extremely simple |
1107 version of the attribute @{text "[symmetric]"}. The purpose of this attribute is |
1116 version of the attribute @{text "[symmetric]"}. The purpose of this attribute is |
1108 to produce the ``symmetric'' version of an equation. The main function behind |
1117 to produce the ``symmetric'' version of an equation. The main function behind |
1112 ML{*val my_symmetric = Thm.rule_attribute (fn _ => fn thm => thm RS @{thm sym})*} |
1121 ML{*val my_symmetric = Thm.rule_attribute (fn _ => fn thm => thm RS @{thm sym})*} |
1113 |
1122 |
1114 text {* |
1123 text {* |
1115 where the function @{ML "Thm.rule_attribute"} expects a function taking a |
1124 where the function @{ML "Thm.rule_attribute"} expects a function taking a |
1116 context (which we ignore in the code above) and a theorem (@{text thm}), and |
1125 context (which we ignore in the code above) and a theorem (@{text thm}), and |
1117 returns another theorem (namely @{text thm} resolved with the lemma |
1126 returns another theorem (namely @{text thm} resolved with the theorem |
1118 @{thm [source] sym}: @{thm sym[no_vars]}). The function @{ML "Thm.rule_attribute"} then returns |
1127 @{thm [source] sym}: @{thm sym[no_vars]}).\footnote{The function @{ML RS} is explained |
|
1128 later on in Section~\ref{sec:simpletacs}.} The function |
|
1129 @{ML "Thm.rule_attribute"} then returns |
1119 an attribute. |
1130 an attribute. |
1120 |
1131 |
1121 Before we can use the attribute, we need to set it up. This can be done |
1132 Before we can use the attribute, we need to set it up. This can be done |
1122 using the Isabelle command \isacommand{attribute\_setup} as follows: |
1133 using the Isabelle command \isacommand{attribute\_setup} as follows: |
1123 *} |
1134 *} |
1124 |
1135 |
1125 attribute_setup %gray my_sym = {* Scan.succeed my_symmetric *} |
1136 attribute_setup %gray my_sym = {* Scan.succeed my_symmetric *} |
1126 "applying the sym rule" |
1137 "applying the sym rule" |
1127 |
1138 |
1128 text {* |
1139 text {* |
1129 The attribute does not expect any further arguments (unlike @{text "[THEN |
1140 Inside the @{text "\<verbopen> \<dots> \<verbclose>"}, we have to specify a parser |
1130 \<dots>]"}, for example). Therefore |
1141 for the theorem attribute. Since the attribute does not expect any further |
1131 we use the parser @{ML Scan.succeed}. Later on we will also consider |
1142 arguments (unlike @{text "[THEN \<dots>]"}, for example), we use the parser @{ML |
1132 attributes taking further arguments. An example for the attribute @{text |
1143 Scan.succeed}. Later on we will also consider attributes taking further |
1133 "[my_sym]"} is the proof |
1144 arguments. An example for the attribute @{text "[my_sym]"} is the proof |
1134 *} |
1145 *} |
1135 |
1146 |
1136 lemma test[my_sym]: "2 = Suc (Suc 0)" by simp |
1147 lemma test[my_sym]: "2 = Suc (Suc 0)" by simp |
1137 |
1148 |
1138 text {* |
1149 text {* |
1150 \isacommand{thm}~@{text "test[my_sym]"}\\ |
1161 \isacommand{thm}~@{text "test[my_sym]"}\\ |
1151 @{text "> "}~@{thm test[my_sym]} |
1162 @{text "> "}~@{thm test[my_sym]} |
1152 \end{isabelle} |
1163 \end{isabelle} |
1153 |
1164 |
1154 As an example of a slightly more complicated theorem attribute, we implement |
1165 As an example of a slightly more complicated theorem attribute, we implement |
1155 our version of @{text "[THEN \<dots>]"}. This attribute will take a list of theorems |
1166 our own version of @{text "[THEN \<dots>]"}. This attribute will take a list of theorems |
1156 as argument and resolve the proved theorem with this list (one theorem |
1167 as argument and resolve the proved theorem with this list (one theorem |
1157 after another). The code for this attribute is |
1168 after another). The code for this attribute is |
1158 *} |
1169 *} |
1159 |
1170 |
1160 ML{*fun MY_THEN thms = |
1171 ML{*fun MY_THEN thms = |
1161 Thm.rule_attribute (fn _ => fn thm => foldl ((op RS) o swap) thm thms)*} |
1172 Thm.rule_attribute (fn _ => fn thm => foldl ((op RS) o swap) thm thms)*} |
1162 |
1173 |
1163 text {* |
1174 text {* |
1164 where @{ML swap} swaps the components of a pair (@{ML RS} is explained |
1175 where @{ML swap} swaps the components of a pair. The setup of this theorem |
1165 later on in Section~\ref{sec:simpletacs}). The setup of this theorem |
|
1166 attribute uses the parser @{ML Attrib.thms}, which parses a list of |
1176 attribute uses the parser @{ML Attrib.thms}, which parses a list of |
1167 theorems. |
1177 theorems. |
1168 *} |
1178 *} |
1169 |
1179 |
1170 attribute_setup %gray MY_THEN = {* Attrib.thms >> MY_THEN *} |
1180 attribute_setup %gray MY_THEN = {* Attrib.thms >> MY_THEN *} |
1214 *} |
1224 *} |
1215 |
1225 |
1216 ML{*val my_thms = ref ([] : thm list)*} |
1226 ML{*val my_thms = ref ([] : thm list)*} |
1217 |
1227 |
1218 text {* |
1228 text {* |
1219 A word of warning: such references must not be used in any code that |
1229 The purpose of this reference is that we are going to add and delete theorems |
1220 is meant to be more than just for testing purposes! Here it is only used |
1230 to the referenced list. However, a word of warning: such references must not |
1221 to illustrate matters. We will show later how to store data properly without |
1231 be used in any code that is meant to be more than just for testing purposes! |
1222 using references. |
1232 Here it is only used to illustrate matters. We will show later how to store |
|
1233 data properly without using references. |
1223 |
1234 |
1224 The function @{ML Thm.declaration_attribute} expects us to |
1235 We need to provide two functions that add and delete theorems from this list. |
1225 provide two functions that add and delete theorems from this list. |
|
1226 For this we use the two functions: |
1236 For this we use the two functions: |
1227 *} |
1237 *} |
1228 |
1238 |
1229 ML{*fun my_thms_add thm ctxt = |
1239 ML{*fun my_thm_add thm ctxt = |
1230 (my_thms := Thm.add_thm thm (!my_thms); ctxt) |
1240 (my_thms := Thm.add_thm thm (!my_thms); ctxt) |
1231 |
1241 |
1232 fun my_thms_del thm ctxt = |
1242 fun my_thm_del thm ctxt = |
1233 (my_thms := Thm.del_thm thm (!my_thms); ctxt)*} |
1243 (my_thms := Thm.del_thm thm (!my_thms); ctxt)*} |
1234 |
1244 |
1235 text {* |
1245 text {* |
1236 These functions take a theorem and a context and, for what we are explaining |
1246 These functions take a theorem and a context and, for what we are explaining |
1237 here it is sufficient that they just return the context unchanged. They change |
1247 here it is sufficient that they just return the context unchanged. They change |
1240 Thm.del_thm} deletes one (both functions use the predicate @{ML |
1250 Thm.del_thm} deletes one (both functions use the predicate @{ML |
1241 Thm.eq_thm_prop}, which compares theorems according to their proved |
1251 Thm.eq_thm_prop}, which compares theorems according to their proved |
1242 propositions modulo alpha-equivalence). |
1252 propositions modulo alpha-equivalence). |
1243 |
1253 |
1244 |
1254 |
1245 You can turn functions @{ML my_thms_add} and @{ML my_thms_del} into |
1255 You can turn functions @{ML my_thm_add} and @{ML my_thm_del} into |
1246 attributes with the code |
1256 attributes with the code |
1247 *} |
1257 *} |
1248 |
1258 |
1249 ML{*val my_add = Thm.declaration_attribute my_thms_add |
1259 ML{*val my_add = Thm.declaration_attribute my_thm_add |
1250 val my_del = Thm.declaration_attribute my_thms_del *} |
1260 val my_del = Thm.declaration_attribute my_thm_del *} |
1251 |
1261 |
1252 text {* |
1262 text {* |
1253 and set up the attributes as follows |
1263 and set up the attributes as follows |
1254 *} |
1264 *} |
1255 |
1265 |
1256 attribute_setup %gray my_thms = {* Attrib.add_del my_add my_del *} |
1266 attribute_setup %gray my_thms = {* Attrib.add_del my_add my_del *} |
1257 "maintaining a list of my_thms" |
1267 "maintaining a list of my_thms - rough test only!" |
1258 |
1268 |
1259 text {* |
1269 text {* |
1260 The parser @{ML Attrib.add_del} is a predefined parser for |
1270 The parser @{ML Attrib.add_del} is a pre-defined parser for |
1261 adding and deleting lemmas. Now if you prove the next lemma |
1271 adding and deleting lemmas. Now if you prove the next lemma |
1262 and attach the attribute |
1272 and attach to it the attribute @{text "[my_thms]"} |
1263 @{text "[my_thms]"} |
|
1264 *} |
1273 *} |
1265 |
1274 |
1266 lemma trueI_2[my_thms]: "True" by simp |
1275 lemma trueI_2[my_thms]: "True" by simp |
1267 |
1276 |
1268 text {* |
1277 text {* |
1272 "!my_thms" "[\"True\"]"} |
1281 "!my_thms" "[\"True\"]"} |
1273 |
1282 |
1274 You can also add theorems using the command \isacommand{declare}. |
1283 You can also add theorems using the command \isacommand{declare}. |
1275 *} |
1284 *} |
1276 |
1285 |
1277 declare test[my_thms] trueI_2[my_thms add] |
1286 declare test[my_thms] trueI_2[my_thms add] |
1278 |
1287 |
1279 text {* |
1288 text {* |
1280 The @{text "add"} is the default operation and does not need |
1289 With this attribute, the @{text "add"} operation is the default and does |
1281 to be explicitly given. These two declarations will cause the |
1290 not need to be explicitly given. These three declarations will cause the |
1282 theorem list to be updated as: |
1291 theorem list to be updated as: |
1283 |
1292 |
1284 @{ML_response_fake [display,gray] |
1293 @{ML_response_fake [display,gray] |
1285 "!my_thms" |
1294 "!my_thms" |
1286 "[\"True\", \"Suc (Suc 0) = 2\"]"} |
1295 "[\"True\", \"Suc (Suc 0) = 2\"]"} |
1302 but there can be any number of them. We just have to change the parser for reading |
1311 but there can be any number of them. We just have to change the parser for reading |
1303 the arguments accordingly. |
1312 the arguments accordingly. |
1304 |
1313 |
1305 However, as said at the beginning of this example, using references for storing theorems is |
1314 However, as said at the beginning of this example, using references for storing theorems is |
1306 \emph{not} the received way of doing such things. The received way is to |
1315 \emph{not} the received way of doing such things. The received way is to |
1307 start a ``data slot'', below called @{text MyThmsData}, by using the functor |
1316 start a ``data slot'', below called @{text MyThmsData}, generated by the functor |
1308 @{text GenericDataFun}: |
1317 @{text GenericDataFun}: |
1309 *} |
1318 *} |
1310 |
1319 |
1311 ML {*structure MyThmsData = GenericDataFun |
1320 ML {*structure MyThmsData = GenericDataFun |
1312 (type T = thm list |
1321 (type T = thm list |
1313 val empty = [] |
1322 val empty = [] |
1314 val extend = I |
1323 val extend = I |
1315 fun merge _ = Thm.merge_thms) *} |
1324 fun merge _ = Thm.merge_thms) *} |
1316 |
1325 |
1317 text {* |
1326 text {* |
1318 To use this data slot, you only have to change @{ML my_thms_add} and |
1327 The type @{text "T"} of this data slot is @{ML_type "thm list"}.\footnote{FIXME: give a pointer |
1319 @{ML my_thms_del} to: |
1328 to where data slots are explained properly.} |
1320 *} |
1329 To use this data slot, you only have to change @{ML my_thm_add} and |
1321 |
1330 @{ML my_thm_del} to: |
1322 ML{*val thm_add = MyThmsData.map o Thm.add_thm |
1331 *} |
1323 val thm_del = MyThmsData.map o Thm.del_thm*} |
1332 |
|
1333 ML{*val my_thm_add = MyThmsData.map o Thm.add_thm |
|
1334 val my_thm_del = MyThmsData.map o Thm.del_thm*} |
1324 |
1335 |
1325 text {* |
1336 text {* |
1326 where @{ML MyThmsData.map} updates the data appropriately. The |
1337 where @{ML MyThmsData.map} updates the data appropriately. The |
1327 corresponding theorem addtributes are |
1338 corresponding theorem addtributes are |
1328 *} |
1339 *} |
1329 |
1340 |
1330 ML{*val add = Thm.declaration_attribute thm_add |
1341 ML{*val my_add = Thm.declaration_attribute my_thm_add |
1331 val del = Thm.declaration_attribute thm_del *} |
1342 val my_del = Thm.declaration_attribute my_thm_del *} |
1332 |
1343 |
1333 text {* |
1344 text {* |
1334 and the setup is as follows |
1345 and the setup is as follows |
1335 *} |
1346 *} |
1336 |
1347 |
1337 attribute_setup %gray my_thms2 = {* Attrib.add_del add del *} |
1348 attribute_setup %gray my_thms2 = {* Attrib.add_del my_add my_del *} |
1338 "properly maintaining a list of my_thms" |
1349 "properly maintaining a list of my_thms" |
1339 |
1350 |
1340 text {* |
1351 text {* |
1341 Initially, the data slot is empty |
1352 Initially, the data slot is empty |
1342 |
1353 |
1343 @{ML_response_fake [display,gray] |
1354 @{ML_response_fake [display,gray] |
1344 "MyThmsData.get (Context.Proof @{context})" |
1355 "MyThmsData.get (Context.Proof @{context})" |
1345 "[]"} |
1356 "[]"} |
1346 |
1357 |
1348 *} |
1359 *} |
1349 |
1360 |
1350 lemma three[my_thms2]: "3 = Suc (Suc (Suc 0))" by simp |
1361 lemma three[my_thms2]: "3 = Suc (Suc (Suc 0))" by simp |
1351 |
1362 |
1352 text {* |
1363 text {* |
1353 the lemma is now included |
1364 then the lemma is recorded. |
1354 |
1365 |
1355 @{ML_response_fake [display,gray] |
1366 @{ML_response_fake [display,gray] |
1356 "MyThmsData.get (Context.Proof @{context})" |
1367 "MyThmsData.get (Context.Proof @{context})" |
1357 "[\"3 = Suc (Suc (Suc 0))\"]"} |
1368 "[\"3 = Suc (Suc (Suc 0))\"]"} |
1358 |
1369 |
1359 With @{text my_thms2} you can also nicely see why it is important to |
1370 With theorem attribute @{text my_thms2} you can also nicely see why it |
|
1371 is important to |
1360 store data in a ``data slot'' and \emph{not} in a reference. Backtrack |
1372 store data in a ``data slot'' and \emph{not} in a reference. Backtrack |
1361 to the point just before the lemma @{thm [source] three} is proved and |
1373 to the point just before the lemma @{thm [source] three} was proved and |
1362 check the the content of @{ML_struct "MyThmsData"}: it is now again |
1374 check the the content of @{ML_struct "MyThmsData"}: it should be empty. |
1363 empty. The addition has been properly retracted. Now consider the proof: |
1375 The addition has been properly retracted. Now consider the proof: |
1364 *} |
1376 *} |
1365 |
1377 |
1366 lemma four[my_thms]: "4 = Suc (Suc (Suc (Suc 0)))" by simp |
1378 lemma four[my_thms]: "4 = Suc (Suc (Suc (Suc 0)))" by simp |
1367 |
1379 |
1368 text {* |
1380 text {* |
1370 |
1382 |
1371 @{ML_response_fake [display,gray] |
1383 @{ML_response_fake [display,gray] |
1372 "!my_thms" |
1384 "!my_thms" |
1373 "[\"4 = Suc (Suc (Suc (Suc 0)))\", \"True\"]"} |
1385 "[\"4 = Suc (Suc (Suc (Suc 0)))\", \"True\"]"} |
1374 |
1386 |
1375 as expected, but if we backtrack before the lemma @{thm [source] four}, the |
1387 as expected, but if you backtrack before the lemma @{thm [source] four}, the |
1376 content of @{ML my_thms} is unchanged. The backtracking mechanism |
1388 content of @{ML my_thms} is unchanged. The backtracking mechanism |
1377 of Isabelle is completely oblivious about what to do with references! |
1389 of Isabelle is completely oblivious about what to do with references, but |
1378 |
1390 properly treats ``data slots''! |
1379 Since storing theorems in a special list is such a common task, there is the |
1391 |
|
1392 Since storing theorems in a list is such a common task, there is the special |
1380 functor @{text NamedThmsFun}, which does most of the work for you. To obtain |
1393 functor @{text NamedThmsFun}, which does most of the work for you. To obtain |
1381 such a named theorem lists, you just declare |
1394 a named theorem lists, you just declare |
1382 *} |
1395 *} |
1383 |
1396 |
1384 ML{*structure FooRules = NamedThmsFun |
1397 ML{*structure FooRules = NamedThmsFun |
1385 (val name = "foo" |
1398 (val name = "foo" |
1386 val description = "Rules for foo") *} |
1399 val description = "Rules for foo") *} |