ProgTutorial/Tactical.thy
changeset 405 f8d020bbc2c0
parent 404 3d27d77c351f
child 406 f399b6855546
equal deleted inserted replaced
404:3d27d77c351f 405:f8d020bbc2c0
  2075 section {* Conversions\label{sec:conversion} *}
  2075 section {* Conversions\label{sec:conversion} *}
  2076 
  2076 
  2077 text {*
  2077 text {*
  2078   Conversions are a thin layer on top of Isabelle's inference kernel, and 
  2078   Conversions are a thin layer on top of Isabelle's inference kernel, and 
  2079   can be viewed as a controllable, bare-bone version of Isabelle's simplifier.
  2079   can be viewed as a controllable, bare-bone version of Isabelle's simplifier.
  2080   One difference between conversions and the simplifier is that the former
  2080   One difference between a conversion and the simplifier is that the former
  2081   act on @{ML_type cterm}s while the latter acts on @{ML_type thm}s. 
  2081   acts on @{ML_type cterm}s while the latter acts on @{ML_type thm}s. 
  2082   However, we will also show in this section how conversions can be applied
  2082   However, we will also show in this section how conversions can be applied
  2083   to theorems via tactics. The type for conversions is
  2083   to theorems and used as tactics. The type for conversions is
  2084 *}
  2084 *}
  2085 
  2085 
  2086 ML{*type conv = cterm -> thm*}
  2086 ML{*type conv = cterm -> thm*}
  2087 
  2087 
  2088 text {*
  2088 text {*
  2116   "((\<lambda>x y. x + y) 2) 10 \<equiv> 2 + 10"}
  2116   "((\<lambda>x y. x + y) 2) 10 \<equiv> 2 + 10"}
  2117 
  2117 
  2118   If you run this example, you will notice that the actual response is the 
  2118   If you run this example, you will notice that the actual response is the 
  2119   seemingly nonsensical @{term
  2119   seemingly nonsensical @{term
  2120   "2 + 10 \<equiv> 2 + (10::nat)"}. The reason is that the pretty-printer for
  2120   "2 + 10 \<equiv> 2 + (10::nat)"}. The reason is that the pretty-printer for
  2121   @{ML_type cterm}s eta-normalises terms and therefore produces this output.
  2121   @{ML_type cterm}s eta-normalises (sic) terms and therefore produces this output.
  2122   If we get hold of the ``raw'' representation of the produced theorem, 
  2122   If we get hold of the ``raw'' representation of the produced theorem, 
  2123   we obtain the expected result.
  2123   we obtain the expected result.
  2124 
  2124 
  2125 
  2125 
  2126   @{ML_response [display,gray]
  2126   @{ML_response [display,gray]
  2202 "(True \<and> Q \<equiv> Q, P \<or> True \<and> Q \<equiv> P \<or> True \<and> Q)"}
  2202 "(True \<and> Q \<equiv> Q, P \<or> True \<and> Q \<equiv> P \<or> True \<and> Q)"}
  2203 
  2203 
  2204   Here the conversion of @{thm [source] true_conj1} only applies
  2204   Here the conversion of @{thm [source] true_conj1} only applies
  2205   in the first case, but fails in the second. The whole conversion
  2205   in the first case, but fails in the second. The whole conversion
  2206   does not fail, however, because the combinator @{ML else_conv in Conv} will then 
  2206   does not fail, however, because the combinator @{ML else_conv in Conv} will then 
  2207   try out @{ML all_conv in Conv}, which always succeeds.
  2207   try out @{ML all_conv in Conv}, which always succeeds. The same
  2208 
  2208   behaviour can also be achieved with conversion combinator @{ML_ind try_conv in Conv}.
  2209   The conversion combinator @{ML_ind try_conv in Conv} constructs a conversion 
       
  2210   which is tried out on a term, but in case of failure just does nothing.
       
  2211   For example
  2209   For example
  2212   
  2210   
  2213   @{ML_response_fake [display,gray]
  2211   @{ML_response_fake [display,gray]
  2214 "let
  2212 "let
  2215   val conv = Conv.try_conv (Conv.rewr_conv @{thm true_conj1})
  2213   val conv = Conv.try_conv (Conv.rewr_conv @{thm true_conj1})
  2216   val ctrm = @{cterm \"True \<or> P\"}
  2214   val ctrm = @{cterm \"True \<or> P\"}
  2217 in
  2215 in
  2218   conv ctrm
  2216   conv ctrm
  2219 end"
  2217 end"
  2220   "True \<or> P \<equiv> True \<or> P"}
  2218   "True \<or> P \<equiv> True \<or> P"}
       
  2219 
       
  2220   Rewriting with more than one theorem can be done using the function 
       
  2221   @{ML_ind rewrs_conv in More_Conv} from the structure @{ML_struct More_Conv}.
       
  2222   
  2221 
  2223 
  2222   Apart from the function @{ML beta_conversion in Thm}, which is able to fully
  2224   Apart from the function @{ML beta_conversion in Thm}, which is able to fully
  2223   beta-normalise a term, the conversions so far are restricted in that they
  2225   beta-normalise a term, the conversions so far are restricted in that they
  2224   only apply to the outer-most level of a @{ML_type cterm}. In what follows we
  2226   only apply to the outer-most level of a @{ML_type cterm}. In what follows we
  2225   will lift this restriction. The combinators @{ML_ind fun_conv in Conv} 
  2227   will lift this restriction. The combinators @{ML_ind fun_conv in Conv} 
  2267   We can now apply all these functions in a conversion that recursively
  2269   We can now apply all these functions in a conversion that recursively
  2268   descends a term and applies a ``@{thm [source] true_conj1}''-conversion 
  2270   descends a term and applies a ``@{thm [source] true_conj1}''-conversion 
  2269   in all possible positions.
  2271   in all possible positions.
  2270 *}
  2272 *}
  2271 
  2273 
  2272 ML %linenosgray{*fun all_true1_conv ctxt ctrm =
  2274 ML %linenosgray{*fun true_conj1_conv ctxt ctrm =
  2273   case (Thm.term_of ctrm) of
  2275   case (Thm.term_of ctrm) of
  2274     @{term "op \<and>"} $ @{term True} $ _ => 
  2276     @{term "op \<and>"} $ @{term True} $ _ => 
  2275       (Conv.arg_conv (all_true1_conv ctxt) then_conv
  2277       (Conv.arg_conv (true_conj1_conv ctxt) then_conv
  2276          Conv.rewr_conv @{thm true_conj1}) ctrm
  2278          Conv.rewr_conv @{thm true_conj1}) ctrm
  2277   | _ $ _ => Conv.comb_conv (all_true1_conv ctxt) ctrm
  2279   | _ $ _ => Conv.comb_conv (true_conj1_conv ctxt) ctrm
  2278   | Abs _ => Conv.abs_conv (fn (_, ctxt) => all_true1_conv ctxt) ctxt ctrm
  2280   | Abs _ => Conv.abs_conv (fn (_, ctxt) => true_conj1_conv ctxt) ctxt ctrm
  2279   | _ => Conv.all_conv ctrm*}
  2281   | _ => Conv.all_conv ctrm*}
  2280 
  2282 
  2281 text {*
  2283 text {*
  2282   This function ``fires'' if the terms is of the form @{text "(True \<and> \<dots>)"}. 
  2284   This function ``fires'' if the terms is of the form @{text "(True \<and> \<dots>)"}. 
  2283   It descends under applications (Line 6 and 7) and abstractions 
  2285   It descends under applications (Line 6 and 7) and abstractions 
  2286   to be able to pattern-match the term. To see this 
  2288   to be able to pattern-match the term. To see this 
  2287   conversion in action, consider the following example:
  2289   conversion in action, consider the following example:
  2288 
  2290 
  2289 @{ML_response_fake [display,gray]
  2291 @{ML_response_fake [display,gray]
  2290 "let
  2292 "let
  2291   val conv = all_true1_conv @{context}
  2293   val conv = true_conj1_conv @{context}
  2292   val ctrm = @{cterm \"distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x\"}
  2294   val ctrm = @{cterm \"distinct [1::nat, x] \<longrightarrow> True \<and> 1 \<noteq> x\"}
  2293 in
  2295 in
  2294   conv ctrm
  2296   conv ctrm
  2295 end"
  2297 end"
  2296   "distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x \<equiv> distinct [1, x] \<longrightarrow> 1 \<noteq> x"}
  2298   "distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x \<equiv> distinct [1, x] \<longrightarrow> 1 \<noteq> x"}
       
  2299 *}
       
  2300 
       
  2301 text {*
       
  2302   Conversions that traverse terms, like @{ML true_conj1_conv} above, can be
       
  2303   implemented more succinctly with the combinators @{ML_ind bottom_conv in
       
  2304   More_Conv} and @{ML_ind top_conv in More_Conv}. For example:
       
  2305 *}
       
  2306 
       
  2307 ML{*fun true_conj1_conv ctxt =
       
  2308 let
       
  2309   val conv = Conv.try_conv (Conv.rewr_conv @{thm true_conj1})
       
  2310 in
       
  2311   More_Conv.bottom_conv (K conv) ctxt
       
  2312 end*}
       
  2313 
       
  2314 text {*
       
  2315   The function @{ML bottom_conv in More_Conv} traverses first the the term and
       
  2316   on the ``way down'' applies the conversion, whereas @{ML top_conv in
       
  2317   More_Conv} applies the on the ``way up''. To see the difference, 
       
  2318   assume the following two meta-equations.
       
  2319 *}
       
  2320 
       
  2321 lemma conj_assoc:
       
  2322   fixes A B C::bool
       
  2323   shows "A \<and> (B \<and> C) \<equiv> (A \<and> B) \<and> C"
       
  2324   and   "(A \<and> B) \<and> C \<equiv> A \<and> (B \<and> C)"
       
  2325 by simp_all
       
  2326 
       
  2327 text {*
       
  2328   and the @{ML_type cterm} @{text "(a \<and> (b \<and> c)) \<and> d"}. The reader should
       
  2329   pause for a moment to be convinced that rewriting top-down and bottom-up 
       
  2330   according to the lemma produces two results. 
       
  2331 
       
  2332   @{ML_response_fake [display, gray]
       
  2333   "let
       
  2334   val ctxt = @{context}
       
  2335   val conv = Conv.try_conv (More_Conv.rewrs_conv @{thms conj_assoc})
       
  2336   val conv_top = More_Conv.top_conv (K conv) ctxt
       
  2337   val conv_bot = More_Conv.bottom_conv (K conv) ctxt
       
  2338   val ctrm = @{cterm \"(a \<and> (b \<and> c)) \<and> d\"}
       
  2339 in
       
  2340   (conv_top ctrm, conv_bot ctrm)
       
  2341 end"
       
  2342   "(\"(a \<and> (b \<and> c)) \<and> d \<equiv> a \<and> (b \<and> (c \<and> d))\",
       
  2343  \"(a \<and> (b \<and> c)) \<and> d \<equiv> (a \<and> b) \<and> (c \<and> d)\")"}
  2297 
  2344 
  2298   To see how much control you have about rewriting by using conversions, let us 
  2345   To see how much control you have about rewriting by using conversions, let us 
  2299   make the task a bit more complicated by rewriting according to the rule
  2346   make the task a bit more complicated by rewriting according to the rule
  2300   @{text true_conj1}, but only in the first arguments of @{term If}s. Then 
  2347   @{text true_conj1}, but only in the first arguments of @{term If}s. Then 
  2301   the conversion should be as follows.
  2348   the conversion should be as follows.
  2302 *}
  2349 *}
  2303 
  2350 
  2304 ML{*fun if_true1_conv ctxt ctrm =
  2351 ML{*fun if_true1_simple_conv ctxt ctrm =
  2305   case Thm.term_of ctrm of
  2352   case Thm.term_of ctrm of
  2306     Const (@{const_name If}, _) $ _ =>
  2353     Const (@{const_name If}, _) $ _ =>
  2307       Conv.arg_conv (all_true1_conv ctxt) ctrm
  2354       Conv.arg_conv (true_conj1_conv ctxt) ctrm
  2308   | _ $ _ => Conv.comb_conv (if_true1_conv ctxt) ctrm
  2355   | _ => Conv.no_conv ctrm 
  2309   | Abs _ => Conv.abs_conv (fn (_, ctxt) => if_true1_conv ctxt) ctxt ctrm
  2356 
  2310   | _ => Conv.all_conv ctrm *}
  2357 val if_true1_conv = More_Conv.top_sweep_conv if_true1_simple_conv*}
  2311 
  2358 
  2312 text {*
  2359 text {*
  2313   Here is an example for this conversion:
  2360   In the first function we only treat the top-most redex and also only the
       
  2361   success case and otherwise return @{ML no_conv in Conv}.  To apply this
       
  2362   conversion inside a term, we use in the last line the combinator @{ML_ind
       
  2363   top_sweep_conv in More_Conv}, which traverses the term starting from the
       
  2364   root and applies it to all the redexes on the way, but stops in a branch as
       
  2365   soon as it found one redex.\footnote{\bf Fixme: Check with Sascha whether
       
  2366   the conversion stops after a single success case, or stops in each branch
       
  2367   after a success case.} Here is an example for this conversion:
       
  2368 
  2314 
  2369 
  2315   @{ML_response_fake [display,gray]
  2370   @{ML_response_fake [display,gray]
  2316 "let
  2371 "let
  2317   val conv = if_true1_conv @{context}
  2372   val ctrm = @{cterm \"if P (True \<and> 1 \<noteq> (2::nat)) 
  2318   val ctrm = 
  2373                         then True \<and> True else True \<and> False\"}
  2319        @{cterm \"if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False\"}
       
  2320 in
  2374 in
  2321   conv ctrm
  2375   if_true1_conv @{context} ctrm
  2322 end"
  2376 end"
  2323 "if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False 
  2377 "if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False 
  2324 \<equiv> if P (1 \<noteq> 2) then True \<and> True else True \<and> False"}
  2378 \<equiv> if P (1 \<noteq> 2) then True \<and> True else True \<and> False"}
  2325 *}
  2379 *}
  2326 
  2380 
  2327 text {*
  2381 text {*
  2328   So far we only applied conversions to @{ML_type cterm}s. Conversions can, however, 
  2382   So far we only applied conversions to @{ML_type cterm}s. Conversions can, however, 
  2329   also work on theorems using the function @{ML_ind  fconv_rule in Conv}. As an example, 
  2383   also work on theorems using the function @{ML_ind  fconv_rule in Conv}. As an example, 
  2330   consider the conversion @{ML all_true1_conv} and the lemma:
  2384   consider the conversion @{ML true_conj1_conv} and the lemma:
  2331 *}
  2385 *}
  2332 
  2386 
  2333 lemma foo_test: 
  2387 lemma foo_test: 
  2334   shows "P \<or> (True \<and> \<not>P)" by simp
  2388   shows "P \<or> (True \<and> \<not>P)" by simp
  2335 
  2389 
  2336 text {*
  2390 text {*
  2337   Using the conversion @{ML all_true1_conv} you can transform this theorem into a 
  2391   Using the conversion @{ML true_conj1_conv} you can transform this theorem into a 
  2338   new theorem as follows
  2392   new theorem as follows
  2339 
  2393 
  2340   @{ML_response_fake [display,gray]
  2394   @{ML_response_fake [display,gray]
  2341 "let
  2395 "let
  2342   val conv = Conv.fconv_rule (all_true1_conv @{context}) 
  2396   val conv = Conv.fconv_rule (true_conj1_conv @{context}) 
  2343   val thm = @{thm foo_test}
  2397   val thm = @{thm foo_test}
  2344 in
  2398 in
  2345   conv thm
  2399   conv thm
  2346 end" 
  2400 end" 
  2347   "?P \<or> \<not> ?P"}
  2401   "?P \<or> \<not> ?P"}
  2361 
  2415 
  2362   \item @{ML_ind concl_conv in Conv} for applying a conversion to the
  2416   \item @{ML_ind concl_conv in Conv} for applying a conversion to the
  2363   conclusion of a goal.
  2417   conclusion of a goal.
  2364   \end{itemize}
  2418   \end{itemize}
  2365 
  2419 
  2366   Assume we want to apply @{ML all_true1_conv} only in the conclusion
  2420   Assume we want to apply @{ML true_conj1_conv} only in the conclusion
  2367   of the goal, and @{ML if_true1_conv} should only apply to the premises.
  2421   of the goal, and @{ML if_true1_conv} should only apply to the premises.
  2368   Here is a tactic doing exactly that:
  2422   Here is a tactic doing exactly that:
  2369 *}
  2423 *}
  2370 
  2424 
  2371 ML{*fun true1_tac ctxt =
  2425 ML{*fun true1_tac ctxt =
  2372   CONVERSION 
  2426   CONVERSION 
  2373     (Conv.params_conv ~1 (fn ctxt =>
  2427     (Conv.params_conv ~1 (fn ctxt =>
  2374        (Conv.prems_conv ~1 (if_true1_conv ctxt) then_conv
  2428        (Conv.prems_conv ~1 (if_true1_conv ctxt) then_conv
  2375           Conv.concl_conv ~1 (all_true1_conv ctxt))) ctxt)*}
  2429           Conv.concl_conv ~1 (true_conj1_conv ctxt))) ctxt)*}
  2376 
  2430 
  2377 text {* 
  2431 text {* 
  2378   We call the conversions with the argument @{ML "~1"}. This is to 
  2432   We call the conversions with the argument @{ML "~1"}. This is to 
  2379   analyse all parameters, premises and conclusions. If we call them with 
  2433   analyse all parameters, premises and conclusions. If we call them with 
  2380   a non-negative number, say @{text n}, then these conversions will 
  2434   a non-negative number, say @{text n}, then these conversions will 
  2392   \end{isabelle}*}
  2446   \end{isabelle}*}
  2393 (*<*)oops(*>*)
  2447 (*<*)oops(*>*)
  2394 
  2448 
  2395 text {*
  2449 text {*
  2396   As you can see, the premises are rewritten according to @{ML if_true1_conv}, while
  2450   As you can see, the premises are rewritten according to @{ML if_true1_conv}, while
  2397   the conclusion according to @{ML all_true1_conv}.
  2451   the conclusion according to @{ML true_conj1_conv}.
  2398 
  2452 
  2399   Conversions can also be helpful for constructing meta-equality theorems.
  2453   Conversions can also be helpful for constructing meta-equality theorems.
  2400   Such theorems are often definitions. As an example consider the following
  2454   Such theorems are often definitions. As an example consider the following
  2401   two ways of defining the identity function in Isabelle. 
  2455   two ways of defining the identity function in Isabelle. 
  2402 *}
  2456 *}