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 |
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}, |