1 theory FirstSteps |
|
2 imports Base |
|
3 begin |
|
4 |
|
5 chapter {* First Steps *} |
|
6 |
|
7 text {* |
|
8 |
|
9 Isabelle programming is done in ML. Just like lemmas and proofs, ML-code |
|
10 in Isabelle is part of a theory. If you want to follow the code given in |
|
11 this chapter, we assume you are working inside the theory starting with |
|
12 |
|
13 \begin{center} |
|
14 \begin{tabular}{@ {}l} |
|
15 \isacommand{theory} FirstSteps\\ |
|
16 \isacommand{imports} Main\\ |
|
17 \isacommand{begin}\\ |
|
18 \ldots |
|
19 \end{tabular} |
|
20 \end{center} |
|
21 |
|
22 We also generally assume you are working with HOL. The given examples might |
|
23 need to be adapted slightly if you work in a different logic. |
|
24 *} |
|
25 |
|
26 section {* Including ML-Code *} |
|
27 |
|
28 |
|
29 |
|
30 text {* |
|
31 The easiest and quickest way to include code in a theory is |
|
32 by using the \isacommand{ML}-command. For example: |
|
33 |
|
34 \begin{isabelle} |
|
35 \begin{graybox} |
|
36 \isacommand{ML}~@{text "\<verbopen>"}\isanewline |
|
37 \hspace{5mm}@{ML "3 + 4"}\isanewline |
|
38 @{text "\<verbclose>"}\isanewline |
|
39 @{text "> 7"}\smallskip |
|
40 \end{graybox} |
|
41 \end{isabelle} |
|
42 |
|
43 Like normal Isabelle proof scripts, \isacommand{ML}-commands can be |
|
44 evaluated by using the advance and undo buttons of your Isabelle |
|
45 environment. The code inside the \isacommand{ML}-command can also contain |
|
46 value and function bindings, and even those can be undone when the proof |
|
47 script is retracted. As mentioned in the Introduction, we will drop the |
|
48 \isacommand{ML}~@{text "\<verbopen> \<dots> \<verbclose>"} scaffolding whenever we |
|
49 show code. The lines prefixed with @{text [quotes] ">"} are not part of the |
|
50 code, rather they indicate what the response is when the code is evaluated. |
|
51 |
|
52 Once a portion of code is relatively stable, you usually want to export it |
|
53 to a separate ML-file. Such files can then be included in a theory by using |
|
54 the \isacommand{uses}-command in the header of the theory, like: |
|
55 |
|
56 \begin{center} |
|
57 \begin{tabular}{@ {}l} |
|
58 \isacommand{theory} FirstSteps\\ |
|
59 \isacommand{imports} Main\\ |
|
60 \isacommand{uses} @{text "\"file_to_be_included.ML\""} @{text "\<dots>"}\\ |
|
61 \isacommand{begin}\\ |
|
62 \ldots |
|
63 \end{tabular} |
|
64 \end{center} |
|
65 |
|
66 *} |
|
67 |
|
68 section {* Debugging and Printing\label{sec:printing} *} |
|
69 |
|
70 text {* |
|
71 |
|
72 During development you might find it necessary to inspect some data |
|
73 in your code. This can be done in a ``quick-and-dirty'' fashion using |
|
74 the function @{ML "warning"}. For example |
|
75 |
|
76 @{ML_response_fake [display,gray] "warning \"any string\"" "\"any string\""} |
|
77 |
|
78 will print out @{text [quotes] "any string"} inside the response buffer |
|
79 of Isabelle. This function expects a string as argument. If you develop under PolyML, |
|
80 then there is a convenient, though again ``quick-and-dirty'', method for |
|
81 converting values into strings, namely the function @{ML makestring}: |
|
82 |
|
83 @{ML_response_fake [display,gray] "warning (makestring 1)" "\"1\""} |
|
84 |
|
85 However @{ML makestring} only works if the type of what is converted is monomorphic |
|
86 and not a function. |
|
87 |
|
88 The function @{ML "warning"} should only be used for testing purposes, because any |
|
89 output this function generates will be overwritten as soon as an error is |
|
90 raised. For printing anything more serious and elaborate, the |
|
91 function @{ML tracing} is more appropriate. This function writes all output into |
|
92 a separate tracing buffer. For example: |
|
93 |
|
94 @{ML_response_fake [display,gray] "tracing \"foo\"" "\"foo\""} |
|
95 |
|
96 It is also possible to redirect the ``channel'' where the string @{text "foo"} is |
|
97 printed to a separate file, e.g.~to prevent ProofGeneral from choking on massive |
|
98 amounts of trace output. This redirection can be achieved with the code: |
|
99 *} |
|
100 |
|
101 ML{*val strip_specials = |
|
102 let |
|
103 fun strip ("\^A" :: _ :: cs) = strip cs |
|
104 | strip (c :: cs) = c :: strip cs |
|
105 | strip [] = []; |
|
106 in implode o strip o explode end; |
|
107 |
|
108 fun redirect_tracing stream = |
|
109 Output.tracing_fn := (fn s => |
|
110 (TextIO.output (stream, (strip_specials s)); |
|
111 TextIO.output (stream, "\n"); |
|
112 TextIO.flushOut stream)) *} |
|
113 |
|
114 text {* |
|
115 Calling @{ML "redirect_tracing"} with @{ML "(TextIO.openOut \"foo.bar\")"} |
|
116 will cause that all tracing information is printed into the file @{text "foo.bar"}. |
|
117 |
|
118 You can print out error messages with the function @{ML error}; for example: |
|
119 |
|
120 @{ML_response_fake [display,gray] "if 0=1 then true else (error \"foo\")" |
|
121 "Exception- ERROR \"foo\" raised |
|
122 At command \"ML\"."} |
|
123 |
|
124 Most often you want to inspect data of type @{ML_type term}, @{ML_type cterm} |
|
125 or @{ML_type thm}. Isabelle contains elaborate pretty-printing functions for printing them, |
|
126 but for quick-and-dirty solutions they are far too unwieldy. A simple way to transform |
|
127 a term into a string is to use the function @{ML Syntax.string_of_term}. |
|
128 |
|
129 @{ML_response_fake [display,gray] |
|
130 "Syntax.string_of_term @{context} @{term \"1::nat\"}" |
|
131 "\"\\^E\\^Fterm\\^E\\^E\\^Fconst\\^Fname=HOL.one_class.one\\^E1\\^E\\^F\\^E\\^E\\^F\\^E\""} |
|
132 |
|
133 This produces a string with some additional information encoded in it. The string |
|
134 can be properly printed by using the function @{ML warning}. |
|
135 |
|
136 @{ML_response_fake [display,gray] |
|
137 "warning (Syntax.string_of_term @{context} @{term \"1::nat\"})" |
|
138 "\"1\""} |
|
139 |
|
140 A @{ML_type cterm} can be transformed into a string by the following function. |
|
141 *} |
|
142 |
|
143 ML{*fun str_of_cterm ctxt t = |
|
144 Syntax.string_of_term ctxt (term_of t)*} |
|
145 |
|
146 text {* |
|
147 In this example the function @{ML term_of} extracts the @{ML_type term} from |
|
148 a @{ML_type cterm}. If there are more than one @{ML_type cterm}s to be |
|
149 printed, you can use the function @{ML commas} to separate them. |
|
150 *} |
|
151 |
|
152 ML{*fun str_of_cterms ctxt ts = |
|
153 commas (map (str_of_cterm ctxt) ts)*} |
|
154 |
|
155 text {* |
|
156 The easiest way to get the string of a theorem is to transform it |
|
157 into a @{ML_type cterm} using the function @{ML crep_thm}. Theorems |
|
158 also include schematic variables, such as @{text "?P"}, @{text "?Q"} |
|
159 and so on. In order to improve the readability of theorems we convert |
|
160 these schematic variables into free variables using the |
|
161 function @{ML Variable.import_thms}. |
|
162 *} |
|
163 |
|
164 ML{*fun no_vars ctxt thm = |
|
165 let |
|
166 val ((_, [thm']), _) = Variable.import_thms true [thm] ctxt |
|
167 in |
|
168 thm' |
|
169 end |
|
170 |
|
171 fun str_of_thm ctxt thm = |
|
172 str_of_cterm ctxt (#prop (crep_thm (no_vars ctxt thm)))*} |
|
173 |
|
174 text {* |
|
175 Again the function @{ML commas} helps with printing more than one theorem. |
|
176 *} |
|
177 |
|
178 ML{*fun str_of_thms ctxt thms = |
|
179 commas (map (str_of_thm ctxt) thms)*} |
|
180 |
|
181 text {* |
|
182 (FIXME @{ML Toplevel.debug} @{ML Toplevel.profiling} @{ML Toplevel.debug}) |
|
183 *} |
|
184 |
|
185 section {* Combinators\label{sec:combinators} *} |
|
186 |
|
187 text {* |
|
188 For beginners perhaps the most puzzling parts in the existing code of Isabelle are |
|
189 the combinators. At first they seem to greatly obstruct the |
|
190 comprehension of the code, but after getting familiar with them, they |
|
191 actually ease the understanding and also the programming. |
|
192 |
|
193 The simplest combinator is @{ML I}, which is just the identity function defined as |
|
194 *} |
|
195 |
|
196 ML{*fun I x = x*} |
|
197 |
|
198 text {* Another simple combinator is @{ML K}, defined as *} |
|
199 |
|
200 ML{*fun K x = fn _ => x*} |
|
201 |
|
202 text {* |
|
203 @{ML K} ``wraps'' a function around the argument @{text "x"}. However, this |
|
204 function ignores its argument. As a result, @{ML K} defines a constant function |
|
205 always returning @{text x}. |
|
206 |
|
207 The next combinator is reverse application, @{ML "|>"}, defined as: |
|
208 *} |
|
209 |
|
210 ML{*fun x |> f = f x*} |
|
211 |
|
212 text {* While just syntactic sugar for the usual function application, |
|
213 the purpose of this combinator is to implement functions in a |
|
214 ``waterfall fashion''. Consider for example the function *} |
|
215 |
|
216 ML %linenosgray{*fun inc_by_five x = |
|
217 x |> (fn x => x + 1) |
|
218 |> (fn x => (x, x)) |
|
219 |> fst |
|
220 |> (fn x => x + 4)*} |
|
221 |
|
222 text {* |
|
223 which increments its argument @{text x} by 5. It does this by first incrementing |
|
224 the argument by 1 (Line 2); then storing the result in a pair (Line 3); taking |
|
225 the first component of the pair (Line 4) and finally incrementing the first |
|
226 component by 4 (Line 5). This kind of cascading manipulations of values is quite |
|
227 common when dealing with theories (for example by adding a definition, followed by |
|
228 lemmas and so on). The reverse application allows you to read what happens in |
|
229 a top-down manner. This kind of coding should also be familiar, |
|
230 if you have been exposed to Haskell's do-notation. Writing the function @{ML inc_by_five} using |
|
231 the reverse application is much clearer than writing |
|
232 *} |
|
233 |
|
234 ML{*fun inc_by_five x = fst ((fn x => (x, x)) (x + 1)) + 4*} |
|
235 |
|
236 text {* or *} |
|
237 |
|
238 ML{*fun inc_by_five x = |
|
239 ((fn x => x + 4) o fst o (fn x => (x, x)) o (fn x => x + 1)) x*} |
|
240 |
|
241 text {* and typographically more economical than *} |
|
242 |
|
243 ML{*fun inc_by_five x = |
|
244 let val y1 = x + 1 |
|
245 val y2 = (y1, y1) |
|
246 val y3 = fst y2 |
|
247 val y4 = y3 + 4 |
|
248 in y4 end*} |
|
249 |
|
250 text {* |
|
251 Another reason why the let-bindings in the code above are better to be |
|
252 avoided: it is more than easy to get the intermediate values wrong, not to |
|
253 mention the nightmares the maintenance of this code causes! |
|
254 |
|
255 In the context of Isabelle, a ``real world'' example for a function written in |
|
256 the waterfall fashion might be the following code: |
|
257 *} |
|
258 |
|
259 ML %linenosgray{*fun apply_fresh_args pred ctxt = |
|
260 pred |> fastype_of |
|
261 |> binder_types |
|
262 |> map (pair "z") |
|
263 |> Variable.variant_frees ctxt [pred] |
|
264 |> map Free |
|
265 |> (curry list_comb) pred *} |
|
266 |
|
267 text {* |
|
268 This code extracts the argument types of a given |
|
269 predicate and then generates for each argument type a distinct variable; finally it |
|
270 applies the generated variables to the predicate. For example |
|
271 |
|
272 @{ML_response_fake [display,gray] |
|
273 "apply_fresh_args @{term \"P::nat \<Rightarrow> int \<Rightarrow> unit \<Rightarrow> bool\"} @{context} |
|
274 |> Syntax.string_of_term @{context} |
|
275 |> warning" |
|
276 "P z za zb"} |
|
277 |
|
278 You can read off this behaviour from how @{ML apply_fresh_args} is |
|
279 coded: in Line 2, the function @{ML fastype_of} calculates the type of the |
|
280 predicate; @{ML binder_types} in the next line produces the list of argument |
|
281 types (in the case above the list @{text "[nat, int, unit]"}); Line 4 |
|
282 pairs up each type with the string @{text "z"}; the |
|
283 function @{ML variant_frees in Variable} generates for each @{text "z"} a |
|
284 unique name avoiding the given @{text pred}; the list of name-type pairs is turned |
|
285 into a list of variable terms in Line 6, which in the last line is applied |
|
286 by the function @{ML list_comb} to the predicate. We have to use the |
|
287 function @{ML curry}, because @{ML list_comb} expects the predicate and the |
|
288 variables list as a pair. |
|
289 |
|
290 The combinator @{ML "#>"} is the reverse function composition. It can be |
|
291 used to define the following function |
|
292 *} |
|
293 |
|
294 ML{*val inc_by_six = |
|
295 (fn x => x + 1) |
|
296 #> (fn x => x + 2) |
|
297 #> (fn x => x + 3)*} |
|
298 |
|
299 text {* |
|
300 which is the function composed of first the increment-by-one function and then |
|
301 increment-by-two, followed by increment-by-three. Again, the reverse function |
|
302 composition allows you to read the code top-down. |
|
303 |
|
304 The remaining combinators described in this section add convenience for the |
|
305 ``waterfall method'' of writing functions. The combinator @{ML tap} allows |
|
306 you to get hold of an intermediate result (to do some side-calculations for |
|
307 instance). The function |
|
308 |
|
309 *} |
|
310 |
|
311 ML %linenosgray{*fun inc_by_three x = |
|
312 x |> (fn x => x + 1) |
|
313 |> tap (fn x => tracing (makestring x)) |
|
314 |> (fn x => x + 2)*} |
|
315 |
|
316 text {* |
|
317 increments the argument first by @{text "1"} and then by @{text "2"}. In the |
|
318 middle (Line 3), however, it uses @{ML tap} for printing the ``plus-one'' |
|
319 intermediate result inside the tracing buffer. The function @{ML tap} can |
|
320 only be used for side-calculations, because any value that is computed |
|
321 cannot be merged back into the ``main waterfall''. To do this, you can use |
|
322 the next combinator. |
|
323 |
|
324 The combinator @{ML "`"} is similar to @{ML tap}, but applies a function to the value |
|
325 and returns the result together with the value (as a pair). For example |
|
326 the function |
|
327 *} |
|
328 |
|
329 ML{*fun inc_as_pair x = |
|
330 x |> `(fn x => x + 1) |
|
331 |> (fn (x, y) => (x, y + 1))*} |
|
332 |
|
333 text {* |
|
334 takes @{text x} as argument, and then increments @{text x}, but also keeps |
|
335 @{text x}. The intermediate result is therefore the pair @{ML "(x + 1, x)" |
|
336 for x}. After that, the function increments the right-hand component of the |
|
337 pair. So finally the result will be @{ML "(x + 1, x + 1)" for x}. |
|
338 |
|
339 The combinators @{ML "|>>"} and @{ML "||>"} are defined for |
|
340 functions manipulating pairs. The first applies the function to |
|
341 the first component of the pair, defined as |
|
342 *} |
|
343 |
|
344 ML{*fun (x, y) |>> f = (f x, y)*} |
|
345 |
|
346 text {* |
|
347 and the second combinator to the second component, defined as |
|
348 *} |
|
349 |
|
350 ML{*fun (x, y) ||> f = (x, f y)*} |
|
351 |
|
352 text {* |
|
353 With the combinator @{ML "|->"} you can re-combine the elements from a pair. |
|
354 This combinator is defined as |
|
355 *} |
|
356 |
|
357 ML{*fun (x, y) |-> f = f x y*} |
|
358 |
|
359 text {* and can be used to write the following roundabout version |
|
360 of the @{text double} function: |
|
361 *} |
|
362 |
|
363 ML{*fun double x = |
|
364 x |> (fn x => (x, x)) |
|
365 |-> (fn x => fn y => x + y)*} |
|
366 |
|
367 text {* |
|
368 Recall that @{ML "|>"} is the reverse function applications. Recall also that the related |
|
369 reverse function composition is @{ML "#>"}. In fact all the combinators @{ML "|->"}, |
|
370 @{ML "|>>"} and @{ML "||>"} described above have related combinators for function |
|
371 composition, namely @{ML "#->"}, @{ML "#>>"} and @{ML "##>"}. Using @{ML "#->"}, |
|
372 for example, the function @{text double} can also be written as: |
|
373 *} |
|
374 |
|
375 ML{*val double = |
|
376 (fn x => (x, x)) |
|
377 #-> (fn x => fn y => x + y)*} |
|
378 |
|
379 text {* |
|
380 |
|
381 (FIXME: find a good exercise for combinators) |
|
382 |
|
383 \begin{readmore} |
|
384 The most frequently used combinator are defined in the files @{ML_file "Pure/library.ML"} |
|
385 and @{ML_file "Pure/General/basics.ML"}. Also \isccite{sec:ML-linear-trans} |
|
386 contains further information about combinators. |
|
387 \end{readmore} |
|
388 |
|
389 *} |
|
390 |
|
391 |
|
392 section {* Antiquotations *} |
|
393 |
|
394 text {* |
|
395 The main advantage of embedding all code in a theory is that the code can |
|
396 contain references to entities defined on the logical level of Isabelle. By |
|
397 this we mean definitions, theorems, terms and so on. This kind of reference is |
|
398 realised with antiquotations. For example, one can print out the name of the current |
|
399 theory by typing |
|
400 |
|
401 |
|
402 @{ML_response [display,gray] "Context.theory_name @{theory}" "\"FirstSteps\""} |
|
403 |
|
404 where @{text "@{theory}"} is an antiquotation that is substituted with the |
|
405 current theory (remember that we assumed we are inside the theory |
|
406 @{text FirstSteps}). The name of this theory can be extracted using |
|
407 the function @{ML "Context.theory_name"}. |
|
408 |
|
409 Note, however, that antiquotations are statically linked, that is their value is |
|
410 determined at ``compile-time'', not ``run-time''. For example the function |
|
411 *} |
|
412 |
|
413 ML{*fun not_current_thyname () = Context.theory_name @{theory} *} |
|
414 |
|
415 text {* |
|
416 |
|
417 does \emph{not} return the name of the current theory, if it is run in a |
|
418 different theory. Instead, the code above defines the constant function |
|
419 that always returns the string @{text [quotes] "FirstSteps"}, no matter where the |
|
420 function is called. Operationally speaking, the antiquotation @{text "@{theory}"} is |
|
421 \emph{not} replaced with code that will look up the current theory in |
|
422 some data structure and return it. Instead, it is literally |
|
423 replaced with the value representing the theory name. |
|
424 |
|
425 In a similar way you can use antiquotations to refer to proved theorems: |
|
426 @{text "@{thm \<dots>}"} for a single theorem |
|
427 |
|
428 @{ML_response_fake [display,gray] "@{thm allI}" "(\<And>x. ?P x) \<Longrightarrow> \<forall>x. ?P x"} |
|
429 |
|
430 and @{text "@{thms \<dots>}"} for more than one |
|
431 |
|
432 @{ML_response_fake [display,gray] "@{thms conj_ac}" |
|
433 "(?P \<and> ?Q) = (?Q \<and> ?P) |
|
434 (?P \<and> ?Q \<and> ?R) = (?Q \<and> ?P \<and> ?R) |
|
435 ((?P \<and> ?Q) \<and> ?R) = (?P \<and> ?Q \<and> ?R)"} |
|
436 |
|
437 You can also refer to the current simpset. To illustrate this we implement the |
|
438 function that extracts the theorem names stored in a simpset. |
|
439 *} |
|
440 |
|
441 ML{*fun get_thm_names_from_ss simpset = |
|
442 let |
|
443 val {simps,...} = MetaSimplifier.dest_ss simpset |
|
444 in |
|
445 map #1 simps |
|
446 end*} |
|
447 |
|
448 text {* |
|
449 The function @{ML dest_ss in MetaSimplifier} returns a record containing all |
|
450 information stored in the simpset. We are only interested in the names of the |
|
451 simp-rules. So now you can feed in the current simpset into this function. |
|
452 The simpset can be referred to using the antiquotation @{text "@{simpset}"}. |
|
453 |
|
454 @{ML_response_fake [display,gray] |
|
455 "get_thm_names_from_ss @{simpset}" |
|
456 "[\"Nat.of_nat_eq_id\", \"Int.of_int_eq_id\", \"Nat.One_nat_def\", \<dots>]"} |
|
457 |
|
458 Again, this way or referencing simpsets makes you independent from additions |
|
459 of lemmas to the simpset by the user that potentially cause loops. |
|
460 |
|
461 While antiquotations have many applications, they were originally introduced in order |
|
462 to avoid explicit bindings for theorems such as: |
|
463 *} |
|
464 |
|
465 ML{*val allI = thm "allI" *} |
|
466 |
|
467 text {* |
|
468 These bindings are difficult to maintain and also can be accidentally |
|
469 overwritten by the user. This often broke Isabelle |
|
470 packages. Antiquotations solve this problem, since they are ``linked'' |
|
471 statically at compile-time. However, this static linkage also limits their |
|
472 usefulness in cases where data needs to be build up dynamically. In the |
|
473 course of this chapter you will learn more about these antiquotations: |
|
474 they can simplify Isabelle programming since one can directly access all |
|
475 kinds of logical elements from th ML-level. |
|
476 *} |
|
477 |
|
478 text {* |
|
479 (FIXME: say something about @{text "@{binding \<dots>}"} |
|
480 *} |
|
481 |
|
482 section {* Terms and Types *} |
|
483 |
|
484 text {* |
|
485 One way to construct terms of Isabelle is by using the antiquotation |
|
486 \mbox{@{text "@{term \<dots>}"}}. For example: |
|
487 |
|
488 @{ML_response [display,gray] |
|
489 "@{term \"(a::nat) + b = c\"}" |
|
490 "Const (\"op =\", \<dots>) $ |
|
491 (Const (\"HOL.plus_class.plus\", \<dots>) $ \<dots> $ \<dots>) $ \<dots>"} |
|
492 |
|
493 This will show the term @{term "(a::nat) + b = c"}, but printed using the internal |
|
494 representation of this term. This internal representation corresponds to the |
|
495 datatype @{ML_type "term"}. |
|
496 |
|
497 The internal representation of terms uses the usual de Bruijn index mechanism where bound |
|
498 variables are represented by the constructor @{ML Bound}. The index in @{ML Bound} refers to |
|
499 the number of Abstractions (@{ML Abs}) we have to skip until we hit the @{ML Abs} that |
|
500 binds the corresponding variable. However, in Isabelle the names of bound variables are |
|
501 kept at abstractions for printing purposes, and so should be treated only as ``comments''. |
|
502 Application in Isabelle is realised with the term-constructor @{ML $}. |
|
503 |
|
504 \begin{readmore} |
|
505 Terms are described in detail in \isccite{sec:terms}. Their |
|
506 definition and many useful operations are implemented in @{ML_file "Pure/term.ML"}. |
|
507 \end{readmore} |
|
508 |
|
509 Sometimes the internal representation of terms can be surprisingly different |
|
510 from what you see at the user-level, because the layers of |
|
511 parsing/type-checking/pretty printing can be quite elaborate. |
|
512 |
|
513 \begin{exercise} |
|
514 Look at the internal term representation of the following terms, and |
|
515 find out why they are represented like this: |
|
516 |
|
517 \begin{itemize} |
|
518 \item @{term "case x of 0 \<Rightarrow> 0 | Suc y \<Rightarrow> y"} |
|
519 \item @{term "\<lambda>(x,y). P y x"} |
|
520 \item @{term "{ [x::int] | x. x \<le> -2 }"} |
|
521 \end{itemize} |
|
522 |
|
523 Hint: The third term is already quite big, and the pretty printer |
|
524 may omit parts of it by default. If you want to see all of it, you |
|
525 can use the following ML-function to set the printing depth to a higher |
|
526 value: |
|
527 |
|
528 @{ML [display,gray] "print_depth 50"} |
|
529 \end{exercise} |
|
530 |
|
531 The antiquotation @{text "@{prop \<dots>}"} constructs terms of propositional type, |
|
532 inserting the invisible @{text "Trueprop"}-coercions whenever necessary. |
|
533 Consider for example the pairs |
|
534 |
|
535 @{ML_response [display,gray] "(@{term \"P x\"}, @{prop \"P x\"})" |
|
536 "(Free (\"P\", \<dots>) $ Free (\"x\", \<dots>), |
|
537 Const (\"Trueprop\", \<dots>) $ (Free (\"P\", \<dots>) $ Free (\"x\", \<dots>)))"} |
|
538 |
|
539 where a coercion is inserted in the second component and |
|
540 |
|
541 @{ML_response [display,gray] "(@{term \"P x \<Longrightarrow> Q x\"}, @{prop \"P x \<Longrightarrow> Q x\"})" |
|
542 "(Const (\"==>\", \<dots>) $ \<dots> $ \<dots>, Const (\"==>\", \<dots>) $ \<dots> $ \<dots>)"} |
|
543 |
|
544 where it is not (since it is already constructed by a meta-implication). |
|
545 |
|
546 Types can be constructed using the antiquotation @{text "@{typ \<dots>}"}. For example: |
|
547 |
|
548 @{ML_response_fake [display,gray] "@{typ \"bool \<Rightarrow> nat\"}" "bool \<Rightarrow> nat"} |
|
549 |
|
550 \begin{readmore} |
|
551 Types are described in detail in \isccite{sec:types}. Their |
|
552 definition and many useful operations are implemented |
|
553 in @{ML_file "Pure/type.ML"}. |
|
554 \end{readmore} |
|
555 *} |
|
556 |
|
557 |
|
558 section {* Constructing Terms and Types Manually\label{sec:terms_types_manually} *} |
|
559 |
|
560 text {* |
|
561 While antiquotations are very convenient for constructing terms, they can |
|
562 only construct fixed terms (remember they are ``linked'' at compile-time). |
|
563 However, you often need to construct terms dynamically. For example, a |
|
564 function that returns the implication @{text "\<And>(x::nat). P x \<Longrightarrow> Q x"} taking |
|
565 @{term P} and @{term Q} as arguments can only be written as: |
|
566 |
|
567 *} |
|
568 |
|
569 ML{*fun make_imp P Q = |
|
570 let |
|
571 val x = Free ("x", @{typ nat}) |
|
572 in |
|
573 Logic.all x (Logic.mk_implies (P $ x, Q $ x)) |
|
574 end *} |
|
575 |
|
576 text {* |
|
577 The reason is that you cannot pass the arguments @{term P} and @{term Q} |
|
578 into an antiquotation. For example the following does \emph{not} work. |
|
579 *} |
|
580 |
|
581 ML{*fun make_wrong_imp P Q = @{prop "\<And>(x::nat). P x \<Longrightarrow> Q x"} *} |
|
582 |
|
583 text {* |
|
584 To see this apply @{text "@{term S}"} and @{text "@{term T}"} |
|
585 to both functions. With @{ML make_imp} we obtain the intended term involving |
|
586 the given arguments |
|
587 |
|
588 @{ML_response [display,gray] "make_imp @{term S} @{term T}" |
|
589 "Const \<dots> $ |
|
590 Abs (\"x\", Type (\"nat\",[]), |
|
591 Const \<dots> $ (Free (\"S\",\<dots>) $ \<dots>) $ (Free (\"T\",\<dots>) $ \<dots>))"} |
|
592 |
|
593 whereas with @{ML make_wrong_imp} we obtain a term involving the @{term "P"} |
|
594 and @{text "Q"} from the antiquotation. |
|
595 |
|
596 @{ML_response [display,gray] "make_wrong_imp @{term S} @{term T}" |
|
597 "Const \<dots> $ |
|
598 Abs (\"x\", \<dots>, |
|
599 Const \<dots> $ (Const \<dots> $ (Free (\"P\",\<dots>) $ \<dots>)) $ |
|
600 (Const \<dots> $ (Free (\"Q\",\<dots>) $ \<dots>)))"} |
|
601 |
|
602 (FIXME: handy functions for constructing terms: @{ML list_comb}, @{ML lambda}) |
|
603 *} |
|
604 |
|
605 |
|
606 text {* |
|
607 \begin{readmore} |
|
608 There are many functions in @{ML_file "Pure/term.ML"}, @{ML_file "Pure/logic.ML"} and |
|
609 @{ML_file "HOL/Tools/hologic.ML"} that make such manual constructions of terms |
|
610 and types easier.\end{readmore} |
|
611 |
|
612 Have a look at these files and try to solve the following two exercises: |
|
613 *} |
|
614 |
|
615 text {* |
|
616 |
|
617 \begin{exercise}\label{fun:revsum} |
|
618 Write a function @{text "rev_sum : term -> term"} that takes a |
|
619 term of the form @{text "t\<^isub>1 + t\<^isub>2 + \<dots> + t\<^isub>n"} (whereby @{text "n"} might be zero) |
|
620 and returns the reversed sum @{text "t\<^isub>n + \<dots> + t\<^isub>2 + t\<^isub>1"}. Assume |
|
621 the @{text "t\<^isub>i"} can be arbitrary expressions and also note that @{text "+"} |
|
622 associates to the left. Try your function on some examples. |
|
623 \end{exercise} |
|
624 |
|
625 \begin{exercise}\label{fun:makesum} |
|
626 Write a function which takes two terms representing natural numbers |
|
627 in unary notation (like @{term "Suc (Suc (Suc 0))"}), and produce the |
|
628 number representing their sum. |
|
629 \end{exercise} |
|
630 |
|
631 There are a few subtle issues with constants. They usually crop up when |
|
632 pattern matching terms or types, or when constructing them. While it is perfectly ok |
|
633 to write the function @{text is_true} as follows |
|
634 *} |
|
635 |
|
636 ML{*fun is_true @{term True} = true |
|
637 | is_true _ = false*} |
|
638 |
|
639 text {* |
|
640 this does not work for picking out @{text "\<forall>"}-quantified terms. Because |
|
641 the function |
|
642 *} |
|
643 |
|
644 ML{*fun is_all (@{term All} $ _) = true |
|
645 | is_all _ = false*} |
|
646 |
|
647 text {* |
|
648 will not correctly match the formula @{prop "\<forall>x::nat. P x"}: |
|
649 |
|
650 @{ML_response [display,gray] "is_all @{term \"\<forall>x::nat. P x\"}" "false"} |
|
651 |
|
652 The problem is that the @{text "@term"}-antiquotation in the pattern |
|
653 fixes the type of the constant @{term "All"} to be @{typ "('a \<Rightarrow> bool) \<Rightarrow> bool"} for |
|
654 an arbitrary, but fixed type @{typ "'a"}. A properly working alternative |
|
655 for this function is |
|
656 *} |
|
657 |
|
658 ML{*fun is_all (Const ("All", _) $ _) = true |
|
659 | is_all _ = false*} |
|
660 |
|
661 text {* |
|
662 because now |
|
663 |
|
664 @{ML_response [display,gray] "is_all @{term \"\<forall>x::nat. P x\"}" "true"} |
|
665 |
|
666 matches correctly (the first wildcard in the pattern matches any type and the |
|
667 second any term). |
|
668 |
|
669 However there is still a problem: consider the similar function that |
|
670 attempts to pick out @{text "Nil"}-terms: |
|
671 *} |
|
672 |
|
673 ML{*fun is_nil (Const ("Nil", _)) = true |
|
674 | is_nil _ = false *} |
|
675 |
|
676 text {* |
|
677 Unfortunately, also this function does \emph{not} work as expected, since |
|
678 |
|
679 @{ML_response [display,gray] "is_nil @{term \"Nil\"}" "false"} |
|
680 |
|
681 The problem is that on the ML-level the name of a constant is more |
|
682 subtle than you might expect. The function @{ML is_all} worked correctly, |
|
683 because @{term "All"} is such a fundamental constant, which can be referenced |
|
684 by @{ML "Const (\"All\", some_type)" for some_type}. However, if you look at |
|
685 |
|
686 @{ML_response [display,gray] "@{term \"Nil\"}" "Const (\"List.list.Nil\", \<dots>)"} |
|
687 |
|
688 the name of the constant @{text "Nil"} depends on the theory in which the |
|
689 term constructor is defined (@{text "List"}) and also in which datatype |
|
690 (@{text "list"}). Even worse, some constants have a name involving |
|
691 type-classes. Consider for example the constants for @{term "zero"} and |
|
692 \mbox{@{text "(op *)"}}: |
|
693 |
|
694 @{ML_response [display,gray] "(@{term \"0::nat\"}, @{term \"op *\"})" |
|
695 "(Const (\"HOL.zero_class.zero\", \<dots>), |
|
696 Const (\"HOL.times_class.times\", \<dots>))"} |
|
697 |
|
698 While you could use the complete name, for example |
|
699 @{ML "Const (\"List.list.Nil\", some_type)" for some_type}, for referring to or |
|
700 matching against @{text "Nil"}, this would make the code rather brittle. |
|
701 The reason is that the theory and the name of the datatype can easily change. |
|
702 To make the code more robust, it is better to use the antiquotation |
|
703 @{text "@{const_name \<dots>}"}. With this antiquotation you can harness the |
|
704 variable parts of the constant's name. Therefore a functions for |
|
705 matching against constants that have a polymorphic type should |
|
706 be written as follows. |
|
707 *} |
|
708 |
|
709 ML{*fun is_nil_or_all (Const (@{const_name "Nil"}, _)) = true |
|
710 | is_nil_or_all (Const (@{const_name "All"}, _) $ _) = true |
|
711 | is_nil_or_all _ = false *} |
|
712 |
|
713 text {* |
|
714 Occasional you have to calculate what the ``base'' name of a given |
|
715 constant is. For this you can use the function @{ML Sign.extern_const} or |
|
716 @{ML Long_Name.base_name}. For example: |
|
717 |
|
718 @{ML_response [display,gray] "Sign.extern_const @{theory} \"List.list.Nil\"" "\"Nil\""} |
|
719 |
|
720 The difference between both functions is that @{ML extern_const in Sign} returns |
|
721 the smallest name that is still unique, whereas @{ML base_name in Long_Name} always |
|
722 strips off all qualifiers. |
|
723 |
|
724 \begin{readmore} |
|
725 Functions about naming are implemented in @{ML_file "Pure/General/name_space.ML"}; |
|
726 functions about signatures in @{ML_file "Pure/sign.ML"}. |
|
727 \end{readmore} |
|
728 |
|
729 Although types of terms can often be inferred, there are many |
|
730 situations where you need to construct types manually, especially |
|
731 when defining constants. For example the function returning a function |
|
732 type is as follows: |
|
733 |
|
734 *} |
|
735 |
|
736 ML{*fun make_fun_type tau1 tau2 = Type ("fun", [tau1, tau2]) *} |
|
737 |
|
738 text {* This can be equally written with the combinator @{ML "-->"} as: *} |
|
739 |
|
740 ML{*fun make_fun_type tau1 tau2 = tau1 --> tau2 *} |
|
741 |
|
742 text {* |
|
743 A handy function for manipulating terms is @{ML map_types}: it takes a |
|
744 function and applies it to every type in a term. You can, for example, |
|
745 change every @{typ nat} in a term into an @{typ int} using the function: |
|
746 *} |
|
747 |
|
748 ML{*fun nat_to_int t = |
|
749 (case t of |
|
750 @{typ nat} => @{typ int} |
|
751 | Type (s, ts) => Type (s, map nat_to_int ts) |
|
752 | _ => t)*} |
|
753 |
|
754 text {* |
|
755 An example as follows: |
|
756 |
|
757 @{ML_response_fake [display,gray] |
|
758 "map_types nat_to_int @{term \"a = (1::nat)\"}" |
|
759 "Const (\"op =\", \"int \<Rightarrow> int \<Rightarrow> bool\") |
|
760 $ Free (\"a\", \"int\") $ Const (\"HOL.one_class.one\", \"int\")"} |
|
761 |
|
762 (FIXME: readmore about types) |
|
763 *} |
|
764 |
|
765 |
|
766 section {* Type-Checking *} |
|
767 |
|
768 text {* |
|
769 |
|
770 You can freely construct and manipulate @{ML_type "term"}s and @{ML_type |
|
771 typ}es, since they are just arbitrary unchecked trees. However, you |
|
772 eventually want to see if a term is well-formed, or type-checks, relative to |
|
773 a theory. Type-checking is done via the function @{ML cterm_of}, which |
|
774 converts a @{ML_type term} into a @{ML_type cterm}, a \emph{certified} |
|
775 term. Unlike @{ML_type term}s, which are just trees, @{ML_type "cterm"}s are |
|
776 abstract objects that are guaranteed to be type-correct, and they can only |
|
777 be constructed via ``official interfaces''. |
|
778 |
|
779 |
|
780 Type-checking is always relative to a theory context. For now we use |
|
781 the @{ML "@{theory}"} antiquotation to get hold of the current theory. |
|
782 For example you can write: |
|
783 |
|
784 @{ML_response_fake [display,gray] "cterm_of @{theory} @{term \"(a::nat) + b = c\"}" "a + b = c"} |
|
785 |
|
786 This can also be written with an antiquotation: |
|
787 |
|
788 @{ML_response_fake [display,gray] "@{cterm \"(a::nat) + b = c\"}" "a + b = c"} |
|
789 |
|
790 Attempting to obtain the certified term for |
|
791 |
|
792 @{ML_response_fake_both [display,gray] "@{cterm \"1 + True\"}" "Type unification failed \<dots>"} |
|
793 |
|
794 yields an error (since the term is not typable). A slightly more elaborate |
|
795 example that type-checks is: |
|
796 |
|
797 @{ML_response_fake [display,gray] |
|
798 "let |
|
799 val natT = @{typ \"nat\"} |
|
800 val zero = @{term \"0::nat\"} |
|
801 in |
|
802 cterm_of @{theory} |
|
803 (Const (@{const_name plus}, natT --> natT --> natT) $ zero $ zero) |
|
804 end" "0 + 0"} |
|
805 |
|
806 In Isabelle not just terms need to be certified, but also types. For example, |
|
807 you obtain the certified type for the Isablle type @{typ "nat \<Rightarrow> bool"} on |
|
808 the ML-level as follows: |
|
809 |
|
810 @{ML_response_fake [display,gray] |
|
811 "ctyp_of @{theory} (@{typ nat} --> @{typ bool})" |
|
812 "nat \<Rightarrow> bool"} |
|
813 |
|
814 \begin{readmore} |
|
815 For functions related to @{ML_type cterm}s and @{ML_type ctyp}s see |
|
816 the file @{ML_file "Pure/thm.ML"}. |
|
817 \end{readmore} |
|
818 |
|
819 \begin{exercise} |
|
820 Check that the function defined in Exercise~\ref{fun:revsum} returns a |
|
821 result that type-checks. |
|
822 \end{exercise} |
|
823 |
|
824 Remember Isabelle foolows the Church-style typing for terms, i.e.~a term contains |
|
825 enough typing information (constants, free variables and abstractions all have typing |
|
826 information) so that it is always clear what the type of a term is. |
|
827 Given a well-typed term, the function @{ML type_of} returns the |
|
828 type of a term. Consider for example: |
|
829 |
|
830 @{ML_response [display,gray] |
|
831 "type_of (@{term \"f::nat \<Rightarrow> bool\"} $ @{term \"x::nat\"})" "bool"} |
|
832 |
|
833 To calculate the type, this function traverses the whole term and will |
|
834 detect any typing inconsistency. For examle changing the type of the variable |
|
835 @{term "x"} from @{typ "nat"} to @{typ "int"} will result in the error message: |
|
836 |
|
837 @{ML_response_fake [display,gray] |
|
838 "type_of (@{term \"f::nat \<Rightarrow> bool\"} $ @{term \"x::int\"})" |
|
839 "*** Exception- TYPE (\"type_of: type mismatch in application\" \<dots>"} |
|
840 |
|
841 Since the complete traversal might sometimes be too costly and |
|
842 not necessary, there is the function @{ML fastype_of}, which |
|
843 also returns the type of a term. |
|
844 |
|
845 @{ML_response [display,gray] |
|
846 "fastype_of (@{term \"f::nat \<Rightarrow> bool\"} $ @{term \"x::nat\"})" "bool"} |
|
847 |
|
848 However, efficiency is gained on the expense of skipping some tests. You |
|
849 can see this in the following example |
|
850 |
|
851 @{ML_response [display,gray] |
|
852 "fastype_of (@{term \"f::nat \<Rightarrow> bool\"} $ @{term \"x::int\"})" "bool"} |
|
853 |
|
854 where no error is detected. |
|
855 |
|
856 Sometimes it is a bit inconvenient to construct a term with |
|
857 complete typing annotations, especially in cases where the typing |
|
858 information is redundant. A short-cut is to use the ``place-holder'' |
|
859 type @{ML "dummyT"} and then let type-inference figure out the |
|
860 complete type. An example is as follows: |
|
861 |
|
862 @{ML_response_fake [display,gray] |
|
863 "let |
|
864 val c = Const (@{const_name \"plus\"}, dummyT) |
|
865 val o = @{term \"1::nat\"} |
|
866 val v = Free (\"x\", dummyT) |
|
867 in |
|
868 Syntax.check_term @{context} (c $ o $ v) |
|
869 end" |
|
870 "Const (\"HOL.plus_class.plus\", \"nat \<Rightarrow> nat \<Rightarrow> nat\") $ |
|
871 Const (\"HOL.one_class.one\", \"nat\") $ Free (\"x\", \"nat\")"} |
|
872 |
|
873 Instead of giving explicitly the type for the constant @{text "plus"} and the free |
|
874 variable @{text "x"}, the type-inference filles in the missing information. |
|
875 |
|
876 \begin{readmore} |
|
877 See @{ML_file "Pure/Syntax/syntax.ML"} where more functions about reading, |
|
878 checking and pretty-printing of terms are defined. Fuctions related to the |
|
879 type inference are implemented in @{ML_file "Pure/type.ML"} and |
|
880 @{ML_file "Pure/type_infer.ML"}. |
|
881 \end{readmore} |
|
882 |
|
883 (FIXME: say something about sorts) |
|
884 *} |
|
885 |
|
886 |
|
887 section {* Theorems *} |
|
888 |
|
889 text {* |
|
890 Just like @{ML_type cterm}s, theorems are abstract objects of type @{ML_type thm} |
|
891 that can only be build by going through interfaces. As a consequence, every proof |
|
892 in Isabelle is correct by construction. This follows the tradition of the LCF approach |
|
893 \cite{GordonMilnerWadsworth79}. |
|
894 |
|
895 |
|
896 To see theorems in ``action'', let us give a proof on the ML-level for the following |
|
897 statement: |
|
898 *} |
|
899 |
|
900 lemma |
|
901 assumes assm\<^isub>1: "\<And>(x::nat). P x \<Longrightarrow> Q x" |
|
902 and assm\<^isub>2: "P t" |
|
903 shows "Q t" (*<*)oops(*>*) |
|
904 |
|
905 text {* |
|
906 The corresponding ML-code is as follows: |
|
907 |
|
908 @{ML_response_fake [display,gray] |
|
909 "let |
|
910 val assm1 = @{cprop \"\<And>(x::nat). P x \<Longrightarrow> Q x\"} |
|
911 val assm2 = @{cprop \"(P::nat\<Rightarrow>bool) t\"} |
|
912 |
|
913 val Pt_implies_Qt = |
|
914 assume assm1 |
|
915 |> forall_elim @{cterm \"t::nat\"}; |
|
916 |
|
917 val Qt = implies_elim Pt_implies_Qt (assume assm2); |
|
918 in |
|
919 Qt |
|
920 |> implies_intr assm2 |
|
921 |> implies_intr assm1 |
|
922 end" "\<lbrakk>\<And>x. P x \<Longrightarrow> Q x; P t\<rbrakk> \<Longrightarrow> Q t"} |
|
923 |
|
924 This code-snippet constructs the following proof: |
|
925 |
|
926 \[ |
|
927 \infer[(@{text "\<Longrightarrow>"}$-$intro)]{\vdash @{prop "(\<And>x. P x \<Longrightarrow> Q x) \<Longrightarrow> P t \<Longrightarrow> Q t"}} |
|
928 {\infer[(@{text "\<Longrightarrow>"}$-$intro)]{@{prop "\<And>x. P x \<Longrightarrow> Q x"} \vdash @{prop "P t \<Longrightarrow> Q t"}} |
|
929 {\infer[(@{text "\<Longrightarrow>"}$-$elim)]{@{prop "\<And>x. P x \<Longrightarrow> Q x"}, @{prop "P t"} \vdash @{prop "Q t"}} |
|
930 {\infer[(@{text "\<And>"}$-$elim)]{@{prop "\<And>x. P x \<Longrightarrow> Q x"} \vdash @{prop "P t \<Longrightarrow> Q t"}} |
|
931 {\infer[(assume)]{@{prop "\<And>x. P x \<Longrightarrow> Q x"} \vdash @{prop "\<And>x. P x \<Longrightarrow> Q x"}}{}} |
|
932 & |
|
933 \infer[(assume)]{@{prop "P t"} \vdash @{prop "P t"}}{} |
|
934 } |
|
935 } |
|
936 } |
|
937 \] |
|
938 |
|
939 However, while we obtained a theorem as result, this theorem is not |
|
940 yet stored in Isabelle's theorem database. So it cannot be referenced later |
|
941 on. How to store theorems will be explained in Section~\ref{sec:storing}. |
|
942 |
|
943 \begin{readmore} |
|
944 For the functions @{text "assume"}, @{text "forall_elim"} etc |
|
945 see \isccite{sec:thms}. The basic functions for theorems are defined in |
|
946 @{ML_file "Pure/thm.ML"}. |
|
947 \end{readmore} |
|
948 |
|
949 (FIXME: how to add case-names to goal states - maybe in the next section) |
|
950 *} |
|
951 |
|
952 section {* Theorem Attributes *} |
|
953 |
|
954 text {* |
|
955 Theorem attributes are @{text "[simp]"}, @{text "[OF \<dots>]"}, @{text |
|
956 "[symmetric]"} and so on. Such attributes are \emph{neither} tags \emph{nor} flags |
|
957 annotated to theorems, but functions that do further processing once a |
|
958 theorem is proven. In particular, it is not possible to find out |
|
959 what are all theorems that have a given attribute in common, unless of course |
|
960 the function behind the attribute stores the theorems in a retrivable |
|
961 datastructure. |
|
962 |
|
963 If you want to print out all currently known attributes a theorem |
|
964 can have, you can use the function: |
|
965 |
|
966 @{ML_response_fake [display,gray] "Attrib.print_attributes @{theory}" |
|
967 "COMP: direct composition with rules (no lifting) |
|
968 HOL.dest: declaration of Classical destruction rule |
|
969 HOL.elim: declaration of Classical elimination rule |
|
970 \<dots>"} |
|
971 |
|
972 To explain how to write your own attribute, let us start with an extremely simple |
|
973 version of the attribute @{text "[symmetric]"}. The purpose of this attribute is |
|
974 to produce the ``symmetric'' version of an equation. The main function behind |
|
975 this attribute is |
|
976 *} |
|
977 |
|
978 ML{*val my_symmetric = Thm.rule_attribute (fn _ => fn thm => thm RS @{thm sym})*} |
|
979 |
|
980 text {* |
|
981 where the function @{ML "Thm.rule_attribute"} expects a function taking a |
|
982 context (which we ignore in the code above) and a theorem (@{text thm}), and |
|
983 returns another theorem (namely @{text thm} resolved with the lemma |
|
984 @{thm [source] sym}: @{thm sym[no_vars]}). The function @{ML "Thm.rule_attribute"} then returns |
|
985 an attribute. |
|
986 |
|
987 Before we can use the attribute, we need to set it up. This can be done |
|
988 using the function @{ML Attrib.setup} as follows. |
|
989 *} |
|
990 |
|
991 setup %gray {* Attrib.setup @{binding "my_sym"} |
|
992 (Scan.succeed my_symmetric) "applying the sym rule"*} |
|
993 |
|
994 text {* |
|
995 The attribute does not expect any further arguments (unlike @{text "[OF |
|
996 \<dots>]"}, for example, which can take a list of theorems as argument). Therefore |
|
997 we use the parser @{ML Scan.succeed}. Later on we will also consider |
|
998 attributes taking further arguments. An example for the attribute @{text |
|
999 "[my_sym]"} is the proof |
|
1000 *} |
|
1001 |
|
1002 lemma test[my_sym]: "2 = Suc (Suc 0)" by simp |
|
1003 |
|
1004 text {* |
|
1005 which stores the theorem @{thm test} under the name @{thm [source] test}. We |
|
1006 can also use the attribute when referring to this theorem. |
|
1007 |
|
1008 \begin{isabelle} |
|
1009 \isacommand{thm}~@{text "test[my_sym]"}\\ |
|
1010 @{text "> "}~@{thm test[my_sym]} |
|
1011 \end{isabelle} |
|
1012 |
|
1013 The purpose of @{ML Thm.rule_attribute} is to directly manipulate theorems. |
|
1014 Another usage of attributes is to add and delete theorems from stored data. |
|
1015 For example the attribute @{text "[simp]"} adds or deletes a theorem from the |
|
1016 current simpset. For these applications, you can use @{ML Thm.declaration_attribute}. |
|
1017 To illustrate this function, let us introduce a reference containing a list |
|
1018 of theorems. |
|
1019 *} |
|
1020 |
|
1021 ML{*val my_thms = ref ([]:thm list)*} |
|
1022 |
|
1023 text {* |
|
1024 A word of warning: such references must not be used in any code that |
|
1025 is meant to be more than just for testing purposes! Here it is only used |
|
1026 to illustrate matters. We will show later how to store data properly without |
|
1027 using references. The function @{ML Thm.declaration_attribute} expects us to |
|
1028 provide two functions that add and delete theorems from this list. |
|
1029 For this we use the two functions: |
|
1030 *} |
|
1031 |
|
1032 ML{*fun my_thms_add thm ctxt = |
|
1033 (my_thms := Thm.add_thm thm (!my_thms); ctxt) |
|
1034 |
|
1035 fun my_thms_del thm ctxt = |
|
1036 (my_thms := Thm.del_thm thm (!my_thms); ctxt)*} |
|
1037 |
|
1038 text {* |
|
1039 These functions take a theorem and a context and, for what we are explaining |
|
1040 here it is sufficient that they just return the context unchanged. They change |
|
1041 however the reference @{ML my_thms}, whereby the function @{ML Thm.add_thm} |
|
1042 adds a theorem if it is not already included in the list, and @{ML |
|
1043 Thm.del_thm} deletes one. Both functions use the predicate @{ML |
|
1044 Thm.eq_thm_prop} which compares theorems according to their proved |
|
1045 propositions (modulo alpha-equivalence). |
|
1046 |
|
1047 |
|
1048 You can turn both functions into attributes using |
|
1049 *} |
|
1050 |
|
1051 ML{*val my_add = Thm.declaration_attribute my_thms_add |
|
1052 val my_del = Thm.declaration_attribute my_thms_del *} |
|
1053 |
|
1054 text {* |
|
1055 and set up the attributes as follows |
|
1056 *} |
|
1057 |
|
1058 setup %gray {* Attrib.setup @{binding "my_thms"} |
|
1059 (Attrib.add_del my_add my_del) "maintaining a list of my_thms" *} |
|
1060 |
|
1061 text {* |
|
1062 Now if you prove the lemma attaching the attribute @{text "[my_thms]"} |
|
1063 *} |
|
1064 |
|
1065 lemma trueI_2[my_thms]: "True" by simp |
|
1066 |
|
1067 text {* |
|
1068 then you can see the lemma is added to the initially empty list. |
|
1069 |
|
1070 @{ML_response_fake [display,gray] |
|
1071 "!my_thms" "[\"True\"]"} |
|
1072 |
|
1073 You can also add theorems using the command \isacommand{declare}. |
|
1074 *} |
|
1075 |
|
1076 declare test[my_thms] trueI_2[my_thms add] |
|
1077 |
|
1078 text {* |
|
1079 The @{text "add"} is the default operation and does not need |
|
1080 to be given. This declaration will cause the theorem list to be |
|
1081 updated as follows. |
|
1082 |
|
1083 @{ML_response_fake [display,gray] |
|
1084 "!my_thms" |
|
1085 "[\"True\", \"Suc (Suc 0) = 2\"]"} |
|
1086 |
|
1087 The theorem @{thm [source] trueI_2} only appears once, since the |
|
1088 function @{ML Thm.add_thm} tests for duplicates, before extending |
|
1089 the list. Deletion from the list works as follows: |
|
1090 *} |
|
1091 |
|
1092 declare test[my_thms del] |
|
1093 |
|
1094 text {* After this, the theorem list is again: |
|
1095 |
|
1096 @{ML_response_fake [display,gray] |
|
1097 "!my_thms" |
|
1098 "[\"True\"]"} |
|
1099 |
|
1100 We used in this example two functions declared as @{ML Thm.declaration_attribute}, |
|
1101 but there can be any number of them. We just have to change the parser for reading |
|
1102 the arguments accordingly. |
|
1103 |
|
1104 However, as said at the beginning of this example, using references for storing theorems is |
|
1105 \emph{not} the received way of doing such things. The received way is to |
|
1106 start a ``data slot'' in a context by using the functor @{text GenericDataFun}: |
|
1107 *} |
|
1108 |
|
1109 ML {*structure Data = GenericDataFun |
|
1110 (type T = thm list |
|
1111 val empty = [] |
|
1112 val extend = I |
|
1113 fun merge _ = Thm.merge_thms) *} |
|
1114 |
|
1115 text {* |
|
1116 To use this data slot, you only have to change @{ML my_thms_add} and |
|
1117 @{ML my_thms_del} to: |
|
1118 *} |
|
1119 |
|
1120 ML{*val thm_add = Data.map o Thm.add_thm |
|
1121 val thm_del = Data.map o Thm.del_thm*} |
|
1122 |
|
1123 text {* |
|
1124 where @{ML Data.map} updates the data appropriately in the context. Since storing |
|
1125 theorems in a special list is such a common task, there is the functor @{text NamedThmsFun}, |
|
1126 which does most of the work for you. To obtain such a named theorem lists, you just |
|
1127 declare |
|
1128 *} |
|
1129 |
|
1130 ML{*structure FooRules = NamedThmsFun |
|
1131 (val name = "foo" |
|
1132 val description = "Rules for foo"); *} |
|
1133 |
|
1134 text {* |
|
1135 and set up the @{ML_struct FooRules} with the command |
|
1136 *} |
|
1137 |
|
1138 setup %gray {* FooRules.setup *} |
|
1139 |
|
1140 text {* |
|
1141 This code declares a data slot where the theorems are stored, |
|
1142 an attribute @{text foo} (with the @{text add} and @{text del} options |
|
1143 for adding and deleting theorems) and an internal ML interface to retrieve and |
|
1144 modify the theorems. |
|
1145 |
|
1146 Furthermore, the facts are made available on the user-level under the dynamic |
|
1147 fact name @{text foo}. For example you can declare three lemmas to be of the kind |
|
1148 @{text foo} by: |
|
1149 *} |
|
1150 |
|
1151 lemma rule1[foo]: "A" sorry |
|
1152 lemma rule2[foo]: "B" sorry |
|
1153 lemma rule3[foo]: "C" sorry |
|
1154 |
|
1155 text {* and undeclare the first one by: *} |
|
1156 |
|
1157 declare rule1[foo del] |
|
1158 |
|
1159 text {* and query the remaining ones with: |
|
1160 |
|
1161 \begin{isabelle} |
|
1162 \isacommand{thm}~@{text "foo"}\\ |
|
1163 @{text "> ?C"}\\ |
|
1164 @{text "> ?B"} |
|
1165 \end{isabelle} |
|
1166 |
|
1167 On the ML-level the rules marked with @{text "foo"} can be retrieved |
|
1168 using the function @{ML FooRules.get}: |
|
1169 |
|
1170 @{ML_response_fake [display,gray] "FooRules.get @{context}" "[\"?C\",\"?B\"]"} |
|
1171 |
|
1172 \begin{readmore} |
|
1173 For more information see @{ML_file "Pure/Tools/named_thms.ML"} and also |
|
1174 the recipe in Section~\ref{recipe:storingdata} about storing arbitrary |
|
1175 data. |
|
1176 \end{readmore} |
|
1177 |
|
1178 (FIXME What are: @{text "theory_attributes"}, @{text "proof_attributes"}?) |
|
1179 |
|
1180 |
|
1181 \begin{readmore} |
|
1182 FIXME: @{ML_file "Pure/more_thm.ML"} @{ML_file "Pure/Isar/attrib.ML"} |
|
1183 \end{readmore} |
|
1184 *} |
|
1185 |
|
1186 |
|
1187 section {* Setups (TBD) *} |
|
1188 |
|
1189 text {* |
|
1190 In the previous section we used \isacommand{setup} in order to make |
|
1191 a theorem attribute known to Isabelle. What happens behind the scenes |
|
1192 is that \isacommand{setup} expects a functions of type |
|
1193 @{ML_type "theory -> theory"}: the input theory is the current theory and the |
|
1194 output the theory where the theory attribute has been stored. |
|
1195 |
|
1196 This is a fundamental principle in Isabelle. A similar situation occurs |
|
1197 for example with declaring constants. The function that declares a |
|
1198 constant on the ML-level is @{ML Sign.add_consts_i}. |
|
1199 If you write\footnote{Recall that ML-code needs to be |
|
1200 enclosed in \isacommand{ML}~@{text "\<verbopen> \<dots> \<verbclose>"}.} |
|
1201 *} |
|
1202 |
|
1203 ML{*Sign.add_consts_i [(@{binding "BAR"}, @{typ "nat"}, NoSyn)] @{theory} *} |
|
1204 |
|
1205 text {* |
|
1206 for declaring the constant @{text "BAR"} with type @{typ nat} and |
|
1207 run the code, then you indeed obtain a theory as result. But if you |
|
1208 query the constant on the Isabelle level using the command \isacommand{term} |
|
1209 |
|
1210 \begin{isabelle} |
|
1211 \isacommand{term}~@{text [quotes] "BAR"}\\ |
|
1212 @{text "> \"BAR\" :: \"'a\""} |
|
1213 \end{isabelle} |
|
1214 |
|
1215 you do not obtain a constant of type @{typ nat}, but a free variable (printed in |
|
1216 blue) of polymorphic type. The problem is that the ML-expression above did |
|
1217 not register the declaration with the current theory. This is what the command |
|
1218 \isacommand{setup} is for. The constant is properly declared with |
|
1219 *} |
|
1220 |
|
1221 setup %gray {* Sign.add_consts_i [(@{binding "BAR"}, @{typ "nat"}, NoSyn)] *} |
|
1222 |
|
1223 text {* |
|
1224 Now |
|
1225 |
|
1226 \begin{isabelle} |
|
1227 \isacommand{term}~@{text [quotes] "BAR"}\\ |
|
1228 @{text "> \"BAR\" :: \"nat\""} |
|
1229 \end{isabelle} |
|
1230 |
|
1231 returns a (black) constant with the type @{typ nat}. |
|
1232 |
|
1233 A similar command is \isacommand{local\_setup}, which expects a function |
|
1234 of type @{ML_type "local_theory -> local_theory"}. Later on we will also |
|
1235 use the commands \isacommand{method\_setup} for installing methods in the |
|
1236 current theory and \isacommand{simproc\_setup} for adding new simprocs to |
|
1237 the current simpset. |
|
1238 *} |
|
1239 |
|
1240 section {* Theories, Contexts and Local Theories (TBD) *} |
|
1241 |
|
1242 text {* |
|
1243 There are theories, proof contexts and local theories (in this order, if you |
|
1244 want to order them). |
|
1245 |
|
1246 In contrast to an ordinary theory, which simply consists of a type |
|
1247 signature, as well as tables for constants, axioms and theorems, a local |
|
1248 theory also contains additional context information, such as locally fixed |
|
1249 variables and local assumptions that may be used by the package. The type |
|
1250 @{ML_type local_theory} is identical to the type of \emph{proof contexts} |
|
1251 @{ML_type "Proof.context"}, although not every proof context constitutes a |
|
1252 valid local theory. |
|
1253 *} |
|
1254 |
|
1255 section {* Storing Theorems\label{sec:storing} (TBD) *} |
|
1256 |
|
1257 text {* @{ML PureThy.add_thms_dynamic} *} |
|
1258 |
|
1259 |
|
1260 |
|
1261 (* FIXME: some code below *) |
|
1262 |
|
1263 (*<*) |
|
1264 (* |
|
1265 setup {* |
|
1266 Sign.add_consts_i [(Binding"bar", @{typ "nat"},NoSyn)] |
|
1267 *} |
|
1268 *) |
|
1269 lemma "bar = (1::nat)" |
|
1270 oops |
|
1271 |
|
1272 (* |
|
1273 setup {* |
|
1274 Sign.add_consts_i [("foo", @{typ "nat"},NoSyn)] |
|
1275 #> PureThy.add_defs false [((@{binding "foo_def"}, |
|
1276 Logic.mk_equals (Const ("FirstSteps.foo", @{typ "nat"}), @{term "1::nat"})), [])] |
|
1277 #> snd |
|
1278 *} |
|
1279 *) |
|
1280 (* |
|
1281 lemma "foo = (1::nat)" |
|
1282 apply(simp add: foo_def) |
|
1283 done |
|
1284 |
|
1285 thm foo_def |
|
1286 *) |
|
1287 (*>*) |
|
1288 |
|
1289 section {* Pretty-Printing (TBD) *} |
|
1290 |
|
1291 text {* |
|
1292 @{ML Pretty.big_list}, |
|
1293 @{ML Pretty.brk}, |
|
1294 @{ML Pretty.block}, |
|
1295 @{ML Pretty.chunks} |
|
1296 *} |
|
1297 |
|
1298 section {* Misc (TBD) *} |
|
1299 |
|
1300 ML {*DatatypePackage.get_datatype @{theory} "List.list"*} |
|
1301 |
|
1302 end |
|