ProgTutorial/Tactical.thy
changeset 291 077c764c8d8b
parent 289 08ffafe2585d
child 292 41a802bbb7df
equal deleted inserted replaced
290:6af069ab43d4 291:077c764c8d8b
  1842   @{ML_response_fake [display,gray]
  1842   @{ML_response_fake [display,gray]
  1843   "let
  1843   "let
  1844   val add = @{cterm \"\<lambda>x y. x + (y::nat)\"}
  1844   val add = @{cterm \"\<lambda>x y. x + (y::nat)\"}
  1845   val two = @{cterm \"2::nat\"}
  1845   val two = @{cterm \"2::nat\"}
  1846   val ten = @{cterm \"10::nat\"}
  1846   val ten = @{cterm \"10::nat\"}
       
  1847   val ctrm = Thm.capply (Thm.capply add two) ten
  1847 in
  1848 in
  1848   Thm.beta_conversion true (Thm.capply (Thm.capply add two) ten)
  1849   Thm.beta_conversion true ctrm
  1849 end"
  1850 end"
  1850   "((\<lambda>x y. x + y) 2) 10 \<equiv> 2 + 10"}
  1851   "((\<lambda>x y. x + y) 2) 10 \<equiv> 2 + 10"}
  1851 
  1852 
  1852   Note that the actual response in this example is @{term "2 + 10 \<equiv> 2 + (10::nat)"}, 
  1853   If you run this example, you will notice that the actual response is the 
  1853   since the pretty-printer for @{ML_type cterm}s eta-normalises terms.
  1854   seemingly nonsensical @{term
  1854   But how we constructed the term (using the function 
  1855   "2 + 10 \<equiv> 2 + (10::nat)"}. The reason is that the pretty-printer for
  1855   @{ML [index] capply in Thm}, which is the application @{ML $} for @{ML_type cterm}s)
  1856   @{ML_type cterm}s eta-normalises terms and therefore produces this output.
  1856   ensures that the left-hand side must contain beta-redexes. Indeed
  1857   If we get hold of the ``raw'' representation of the produced theorem, 
  1857   if we obtain the ``raw'' representation of the produced theorem, we
  1858   we obtain the expected result.
  1858   can see the difference:
  1859 
  1859 
  1860 
  1860   @{ML_response [display,gray]
  1861   @{ML_response [display,gray]
  1861 "let
  1862 "let
  1862   val add = @{cterm \"\<lambda>x y. x + (y::nat)\"}
  1863   val add = @{cterm \"\<lambda>x y. x + (y::nat)\"}
  1863   val two = @{cterm \"2::nat\"}
  1864   val two = @{cterm \"2::nat\"}
  1864   val ten = @{cterm \"10::nat\"}
  1865   val ten = @{cterm \"10::nat\"}
  1865   val thm = Thm.beta_conversion true (Thm.capply (Thm.capply add two) ten)
  1866   val ctrm = Thm.capply (Thm.capply add two) ten
  1866 in
  1867 in
  1867   Thm.prop_of thm
  1868   Thm.prop_of (Thm.beta_conversion true ctrm)
  1868 end"
  1869 end"
  1869 "Const (\"==\",\<dots>) $ 
  1870 "Const (\"==\",\<dots>) $ 
  1870   (Abs (\"x\",\<dots>,Abs (\"y\",\<dots>,\<dots>)) $\<dots>$\<dots>) $ 
  1871   (Abs (\"x\",\<dots>,Abs (\"y\",\<dots>,\<dots>)) $\<dots>$\<dots>) $ 
  1871     (Const (\"HOL.plus_class.plus\",\<dots>) $ \<dots> $ \<dots>)"} 
  1872     (Const (\"HOL.plus_class.plus\",\<dots>) $ \<dots> $ \<dots>)"} 
  1872 
  1873 
  1873   The argument @{ML true} in @{ML Thm.beta_conversion} indicates that 
  1874   The argument @{ML true} in @{ML beta_conversion in Thm} indicates that 
  1874   the right-hand side should be fully beta-normalised. If instead 
  1875   the right-hand side should be fully beta-normalised. If instead 
  1875   @{ML false} is given, then only a single beta-reduction is performed 
  1876   @{ML false} is given, then only a single beta-reduction is performed 
  1876   on the outer-most level. For example
  1877   on the outer-most level. 
  1877 
       
  1878   @{ML_response_fake [display,gray]
       
  1879   "let
       
  1880   val add = @{cterm \"\<lambda>x y. x + (y::nat)\"}
       
  1881   val two = @{cterm \"2::nat\"}
       
  1882 in
       
  1883   Thm.beta_conversion false (Thm.capply add two)
       
  1884 end"
       
  1885   "((\<lambda>x y. x + y) 2) \<equiv> \<lambda>y. 2 + y"} 
       
  1886 
       
  1887   Again, we actually see as output only the fully eta-normalised term.
       
  1888 
  1878 
  1889   The main point of conversions is that they can be used for rewriting
  1879   The main point of conversions is that they can be used for rewriting
  1890   @{ML_type cterm}s. To do this you can use the function @{ML
  1880   @{ML_type cterm}s. One example is the function 
  1891   "Conv.rewr_conv"}, which expects a meta-equation as an argument. Suppose we
  1881   @{ML [index] rewr_conv in Conv}, which expects a meta-equation as an 
  1892   want to rewrite a @{ML_type cterm} according to the meta-equation:
  1882   argument. Suppose the following meta-equation.
       
  1883   
  1893 *}
  1884 *}
  1894 
  1885 
  1895 lemma true_conj1: "True \<and> P \<equiv> P" by simp
  1886 lemma true_conj1: "True \<and> P \<equiv> P" by simp
  1896 
  1887 
  1897 text {* 
  1888 text {* 
  1898   You can see how this function works in the example rewriting 
  1889   It can be used for example to rewrite @{term "True \<and> (Foo \<longrightarrow> Bar)"} 
  1899   @{term "True \<and> (Foo \<longrightarrow> Bar)"} to @{term "Foo \<longrightarrow> Bar"}.
  1890   to @{term "Foo \<longrightarrow> Bar"}. The code is as follows.
  1900 
  1891 
  1901   @{ML_response_fake [display,gray]
  1892   @{ML_response_fake [display,gray]
  1902 "let 
  1893 "let 
  1903   val ctrm = @{cterm \"True \<and> (Foo \<longrightarrow> Bar)\"}
  1894   val ctrm = @{cterm \"True \<and> (Foo \<longrightarrow> Bar)\"}
  1904 in
  1895 in
  1907   "True \<and> (Foo \<longrightarrow> Bar) \<equiv> Foo \<longrightarrow> Bar"}
  1898   "True \<and> (Foo \<longrightarrow> Bar) \<equiv> Foo \<longrightarrow> Bar"}
  1908 
  1899 
  1909   Note, however, that the function @{ML [index] rewr_conv in Conv} only rewrites the 
  1900   Note, however, that the function @{ML [index] rewr_conv in Conv} only rewrites the 
  1910   outer-most level of the @{ML_type cterm}. If the given @{ML_type cterm} does not match 
  1901   outer-most level of the @{ML_type cterm}. If the given @{ML_type cterm} does not match 
  1911   exactly the 
  1902   exactly the 
  1912   left-hand side of the theorem, then  @{ML [index] rewr_conv in Conv} fails by raising 
  1903   left-hand side of the theorem, then  @{ML [index] rewr_conv in Conv} fails, raising 
  1913   the exception @{ML CTERM}.
  1904   the exception @{ML CTERM}.
  1914 
  1905 
  1915   This very primitive way of rewriting can be made more powerful by
  1906   This very primitive way of rewriting can be made more powerful by
  1916   combining several conversions into one. For this you can use conversion
  1907   combining several conversions into one. For this you can use conversion
  1917   combinators. The simplest conversion combinator is @{ML [index] then_conv}, 
  1908   combinators. The simplest conversion combinator is @{ML [index] then_conv}, 
  1926   (conv1 then_conv conv2) ctrm
  1917   (conv1 then_conv conv2) ctrm
  1927 end"
  1918 end"
  1928   "(\<lambda>x. x \<and> False) True \<equiv> False"}
  1919   "(\<lambda>x. x \<and> False) True \<equiv> False"}
  1929 
  1920 
  1930   where we first beta-reduce the term and then rewrite according to
  1921   where we first beta-reduce the term and then rewrite according to
  1931   @{thm [source] true_conj1}. (Recall the problem with the pretty-printer
  1922   @{thm [source] true_conj1}. (When running this example recall the 
  1932   normalising all terms.)
  1923   problem with the pretty-printer normalising all terms.)
  1933 
  1924 
  1934   The conversion combinator @{ML [index] else_conv} tries out the 
  1925   The conversion combinator @{ML [index] else_conv} tries out the 
  1935   first one, and if it does not apply, tries the second. For example 
  1926   first one, and if it does not apply, tries the second. For example 
  1936 
  1927 
  1937   @{ML_response_fake [display,gray]
  1928   @{ML_response_fake [display,gray]
  1952   The conversion combinator @{ML [index] try_conv in Conv} constructs a conversion 
  1943   The conversion combinator @{ML [index] try_conv in Conv} constructs a conversion 
  1953   which is tried out on a term, but in case of failure just does nothing.
  1944   which is tried out on a term, but in case of failure just does nothing.
  1954   For example
  1945   For example
  1955   
  1946   
  1956   @{ML_response_fake [display,gray]
  1947   @{ML_response_fake [display,gray]
  1957   "Conv.try_conv (Conv.rewr_conv @{thm true_conj1}) @{cterm \"True \<or> P\"}"
  1948 "let
       
  1949   val conv = Conv.try_conv (Conv.rewr_conv @{thm true_conj1})
       
  1950   val ctrm = @{cterm \"True \<or> P\"}
       
  1951 in
       
  1952   conv ctrm
       
  1953 end"
  1958   "True \<or> P \<equiv> True \<or> P"}
  1954   "True \<or> P \<equiv> True \<or> P"}
  1959 
  1955 
  1960   Apart from the function @{ML beta_conversion in Thm}, which is able to fully
  1956   Apart from the function @{ML beta_conversion in Thm}, which is able to fully
  1961   beta-normalise a term, the conversions so far are restricted in that they
  1957   beta-normalise a term, the conversions so far are restricted in that they
  1962   only apply to the outer-most level of a @{ML_type cterm}. In what follows we
  1958   only apply to the outer-most level of a @{ML_type cterm}. In what follows we
  1963   will lift this restriction. The combinator @{ML [index] arg_conv in Conv} will apply
  1959   will lift this restriction. The combinators @{ML [index] fun_conv in Conv} 
  1964   the conversion to the first argument of an application, that is the term
  1960   and @{ML [index] arg_conv in Conv} will apply
  1965   must be of the form @{ML "t1 $ t2" for t1 t2} and the conversion is applied
  1961   a conversion to the first, respectively second, argument of an application.  
  1966   to @{text t2}.  For example
  1962   For example
  1967 
  1963 
  1968   @{ML_response_fake [display,gray]
  1964   @{ML_response_fake [display,gray]
  1969 "let 
  1965 "let 
  1970   val conv = Conv.rewr_conv @{thm true_conj1}
  1966   val conv = Conv.arg_conv (Conv.rewr_conv @{thm true_conj1})
  1971   val ctrm = @{cterm \"P \<or> (True \<and> Q)\"}
  1967   val ctrm = @{cterm \"P \<or> (True \<and> Q)\"}
  1972 in
  1968 in
  1973   Conv.arg_conv conv ctrm
  1969   conv ctrm
  1974 end"
  1970 end"
  1975 "P \<or> (True \<and> Q) \<equiv> P \<or> Q"}
  1971 "P \<or> (True \<and> Q) \<equiv> P \<or> Q"}
  1976 
  1972 
  1977   The reason for this behaviour is that @{text "(op \<or>)"} expects two
  1973   The reason for this behaviour is that @{text "(op \<or>)"} expects two
  1978   arguments.  Therefore the term must be of the form @{text "(Const \<dots> $ t1) $ t2"}. The
  1974   arguments.  Therefore the term must be of the form @{text "(Const \<dots> $ t1) $ t2"}. The
  1979   conversion is then applied to @{text "t2"} which in the example above
  1975   conversion is then applied to @{text "t2"}, which in the example above
  1980   stands for @{term "True \<and> Q"}. The function @{ML [index] fun_conv in Conv} applies
  1976   stands for @{term "True \<and> Q"}. The function @{ML fun_conv in Conv} would apply
  1981   the conversion to the first argument of an application.
  1977   the conversion to the term @{text "(Const \<dots> $ t1)"}.
  1982 
  1978 
  1983   The function @{ML [index] abs_conv in Conv} applies a conversion under an abstraction.
  1979   The function @{ML [index] abs_conv in Conv} applies a conversion under an 
  1984   For example:
  1980   abstraction. For example:
  1985 
  1981 
  1986   @{ML_response_fake [display,gray]
  1982   @{ML_response_fake [display,gray]
  1987 "let 
  1983 "let 
  1988   val conv = Conv.rewr_conv @{thm true_conj1} 
  1984   val conv = Conv.rewr_conv @{thm true_conj1} 
  1989   val ctrm = @{cterm \"\<lambda>P. True \<and> P \<and> Foo\"}
  1985   val ctrm = @{cterm \"\<lambda>P. True \<and> (P \<and> Foo)\"}
  1990 in
  1986 in
  1991   Conv.abs_conv (K conv) @{context} ctrm
  1987   Conv.abs_conv (K conv) @{context} ctrm
  1992 end"
  1988 end"
  1993   "\<lambda>P. True \<and> P \<and> Foo \<equiv> \<lambda>P. P \<and> Foo"}
  1989   "\<lambda>P. True \<and> (P \<and> Foo) \<equiv> \<lambda>P. P \<and> Foo"}
  1994   
  1990   
  1995   Note that this conversion needs a context as an argument. The conversion that 
  1991   Note that this conversion needs a context as an argument. We also give the
  1996   goes under an application is @{ML [index] combination_conv in Conv}. It expects two conversions 
  1992   conversion as @{text "(K conv)"}, which is a function that ignores its
  1997   as arguments, each of which is applied to the corresponding ``branch'' of the
  1993   argument (the argument being a sufficiently freshened version of the
  1998   application. 
  1994   variable that is abstracted and a context). The conversion that goes under
       
  1995   an application is @{ML [index] combination_conv in Conv}. It expects two
       
  1996   conversions as arguments, each of which is applied to the corresponding
       
  1997   ``branch'' of the application.
  1999 
  1998 
  2000   We can now apply all these functions in a conversion that recursively
  1999   We can now apply all these functions in a conversion that recursively
  2001   descends a term and applies a ``@{thm [source] true_conj1}''-conversion 
  2000   descends a term and applies a ``@{thm [source] true_conj1}''-conversion 
  2002   in all possible positions.
  2001   in all possible positions.
  2003 *}
  2002 *}
  2011                (all_true1_conv ctxt) (all_true1_conv ctxt) ctrm
  2010                (all_true1_conv ctxt) (all_true1_conv ctxt) ctrm
  2012   | Abs _ => Conv.abs_conv (fn (_, ctxt) => all_true1_conv ctxt) ctxt ctrm
  2011   | Abs _ => Conv.abs_conv (fn (_, ctxt) => all_true1_conv ctxt) ctxt ctrm
  2013   | _ => Conv.all_conv ctrm*}
  2012   | _ => Conv.all_conv ctrm*}
  2014 
  2013 
  2015 text {*
  2014 text {*
  2016   This function ``fires'' if the terms is of the form @{text "True \<and> \<dots>"}; 
  2015   This function ``fires'' if the terms is of the form @{text "(True \<and> \<dots>)"}. 
  2017   it descends under applications (Line 6 and 7) and abstractions 
  2016   It descends under applications (Line 6 and 7) and abstractions 
  2018   (Line 8); otherwise it leaves the term unchanged (Line 9). In Line 2
  2017   (Line 8); otherwise it leaves the term unchanged (Line 9). In Line 2
  2019   we need to transform the @{ML_type cterm} into a @{ML_type term} in order
  2018   we need to transform the @{ML_type cterm} into a @{ML_type term} in order
  2020   to be able to pattern-match the term. To see this 
  2019   to be able to pattern-match the term. To see this 
  2021   conversion in action, consider the following example:
  2020   conversion in action, consider the following example:
  2022 
  2021 
  2023 @{ML_response_fake [display,gray]
  2022 @{ML_response_fake [display,gray]
  2024 "let
  2023 "let
  2025   val ctxt = @{context}
  2024   val conv = all_true1_conv @{context}
  2026   val ctrm = @{cterm \"distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x\"}
  2025   val ctrm = @{cterm \"distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x\"}
  2027 in
  2026 in
  2028   all_true1_conv ctxt ctrm
  2027   conv ctrm
  2029 end"
  2028 end"
  2030   "distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x \<equiv> distinct [1, x] \<longrightarrow> 1 \<noteq> x"}
  2029   "distinct [1, x] \<longrightarrow> True \<and> 1 \<noteq> x \<equiv> distinct [1, x] \<longrightarrow> 1 \<noteq> x"}
  2031 
  2030 
  2032   To see how much control you have about rewriting by using conversions, let us 
  2031   To see how much control you have about rewriting by using conversions, let us 
  2033   make the task a bit more complicated by rewriting according to the rule
  2032   make the task a bit more complicated by rewriting according to the rule
  2047 text {*
  2046 text {*
  2048   Here is an example for this conversion:
  2047   Here is an example for this conversion:
  2049 
  2048 
  2050   @{ML_response_fake [display,gray]
  2049   @{ML_response_fake [display,gray]
  2051 "let
  2050 "let
  2052   val ctxt = @{context}
  2051   val conv = if_true1_conv @{context}
  2053   val ctrm = 
  2052   val ctrm = 
  2054        @{cterm \"if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False\"}
  2053        @{cterm \"if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False\"}
  2055 in
  2054 in
  2056   if_true1_conv ctxt ctrm
  2055   conv ctrm
  2057 end"
  2056 end"
  2058 "if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False 
  2057 "if P (True \<and> 1 \<noteq> 2) then True \<and> True else True \<and> False 
  2059 \<equiv> if P (1 \<noteq> 2) then True \<and> True else True \<and> False"}
  2058 \<equiv> if P (1 \<noteq> 2) then True \<and> True else True \<and> False"}
  2060 *}
  2059 *}
  2061 
  2060 
  2066 *}
  2065 *}
  2067 
  2066 
  2068 lemma foo_test: "P \<or> (True \<and> \<not>P)" by simp
  2067 lemma foo_test: "P \<or> (True \<and> \<not>P)" by simp
  2069 
  2068 
  2070 text {*
  2069 text {*
  2071   Using the conversion you can transform this theorem into a new theorem
  2070   Using the conversion @{ML all_true1_conv} you can transform this theorem into a 
  2072   as follows
  2071   new theorem as follows
  2073 
  2072 
  2074   @{ML_response_fake [display,gray]
  2073   @{ML_response_fake [display,gray]
  2075   "Conv.fconv_rule (all_true1_conv @{context}) @{thm foo_test}" 
  2074 "let
       
  2075   val conv = Conv.fconv_rule (all_true1_conv @{context}) 
       
  2076   val thm = @{thm foo_test}
       
  2077 in
       
  2078   conv thm
       
  2079 end" 
  2076   "?P \<or> \<not> ?P"}
  2080   "?P \<or> \<not> ?P"}
  2077 
  2081 
  2078   Finally, conversions can also be turned into tactics and then applied to
  2082   Finally, conversions can also be turned into tactics and then applied to
  2079   goal states. This can be done with the help of the function @{ML [index] CONVERSION},
  2083   goal states. This can be done with the help of the function @{ML [index] CONVERSION},
  2080   and also some predefined conversion combinators that traverse a goal
  2084   and also some predefined conversion combinators that traverse a goal
  2081   state. The combinators for the goal state are: @{ML [index] params_conv in Conv} for
  2085   state. The combinators for the goal state are: 
  2082   converting under parameters (i.e.~where goals are of the form @{text "\<And>x. P \<Longrightarrow>
  2086 
  2083   Q"}); the function @{ML [index] prems_conv in Conv} for applying a conversion to all
  2087   \begin{itemize}
  2084   premises of a goal, and @{ML [index] concl_conv in Conv} for applying a conversion to
  2088   \item @{ML [index] params_conv in Conv} for converting under parameters
  2085   the conclusion of a goal.
  2089   (i.e.~where goals are of the form @{text "\<And>x. P x \<Longrightarrow> Q x"})
       
  2090 
       
  2091   \item @{ML [index] prems_conv in Conv} for applying a conversion to all
       
  2092   premises of a goal, and
       
  2093 
       
  2094   \item @{ML [index] concl_conv in Conv} for applying a conversion to the
       
  2095   conclusion of a goal.
       
  2096   \end{itemize}
  2086 
  2097 
  2087   Assume we want to apply @{ML all_true1_conv} only in the conclusion
  2098   Assume we want to apply @{ML all_true1_conv} only in the conclusion
  2088   of the goal, and @{ML if_true1_conv} should only apply to the premises.
  2099   of the goal, and @{ML if_true1_conv} should only apply to the premises.
  2089   Here is a tactic doing exactly that:
  2100   Here is a tactic doing exactly that:
  2090 *}
  2101 *}
  2123   often much less efficient than the simplifier. The advantage of conversions, 
  2134   often much less efficient than the simplifier. The advantage of conversions, 
  2124   however, that they provide much less room for non-termination.
  2135   however, that they provide much less room for non-termination.
  2125 
  2136 
  2126   \begin{exercise}\label{ex:addconversion}
  2137   \begin{exercise}\label{ex:addconversion}
  2127   Write a tactic that does the same as the simproc in exercise
  2138   Write a tactic that does the same as the simproc in exercise
  2128   \ref{ex:addsimproc}, but is based in conversions. That means replace terms
  2139   \ref{ex:addsimproc}, but is based on conversions. You can make
  2129   of the form @{term "t\<^isub>1 + t\<^isub>2"} by their result. You can make
       
  2130   the same assumptions as in \ref{ex:addsimproc}. 
  2140   the same assumptions as in \ref{ex:addsimproc}. 
  2131   \end{exercise}
  2141   \end{exercise}
  2132 
  2142 
  2133   \begin{exercise}\label{ex:compare}
  2143   \begin{exercise}\label{ex:compare}
  2134   Compare your solutions of Exercises~\ref{ex:addsimproc} and \ref{ex:addconversion},
  2144   Compare your solutions of Exercises~\ref{ex:addsimproc} and \ref{ex:addconversion},