(* A partial mechanisation of Gibbons/Hutton "Proof methods for co-recursive programs" 2005. I began with trying to understand how the Bird/Wadler-popularised "take lemma" works on infinite lists, and found this paper easy to follow. (In brief: there's no mystery, continuity does the job. The lemmata are cute as they reflect the information order of the data type into the term language of the logic. This would have been quite helpful in the LCF days.) Note that the HOLCF domain package generates an approx-style lemma for datatypes introduced that way. (C)opyright Peter Gammie, peteg42 at gmail.com, commenced December 2007. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. (This licence is intended to be in the spirit of the BSD licence.) *) theory approx_lemma imports HOLCF begin (* The fully-lazy list type. *) domain 'a llist = lnil | lcons (lazy lhead::"'a") (lazy ltail::"'a llist") (* The "map/iterate property" example from Gibbons/Hutton. *) consts inf_iterate :: "('a \ 'a) \ 'a \ 'a llist" fixrec "inf_iterate\f\x = lcons\x\(inf_iterate\f\(f\x))" declare inf_iterate_simps[simp del] consts lmap :: "('a \ 'b) \ 'a llist \ 'b llist" fixrec "lmap\f\(lnil) = lnil" "lmap\f\(lcons\x\xs) = lcons\(f\x)\(lmap\f\xs)" fixpat lmap_strict[simp]: "lmap\f\\" declare lmap_simps(2)[simp del] declare lmap_simps(2)[symmetric, simp add] definition mapiterate :: "('a \ 'a) \ 'a \ bool" where mapiterate_def[simp]: "mapiterate f x \ lmap\f\(inf_iterate\f\x) = inf_iterate\f\(f\x)" (* Fixpoint induction. thm fix_ind: \adm ?P; ?P \; \x. ?P x \ ?P (?F\x)\ \ ?P (fix\?F) *) lemma fixes f :: "'a \ 'a" fixes x :: "'a" shows "mapiterate f x" proof - let ?F = "\ inf_iterate (f :: 'a \ 'a) x . lcons\x\(inf_iterate\f\(f\x))" let ?P = "\g . \ (f :: 'a \ 'a) (x :: 'a) . lmap\f\(g\f\x) = g\f\(f\x)" let ?h = "\ g (f :: 'a \ 'a) (x :: 'a) . lcons\x\(g\f\(f\x))" have "adm ?P" by simp moreover have "?P \" by simp moreover { fix g assume pg: "?P g" { fix f x from pg have "lmap\f\(g\f\(f\x)) = g\f\(f\(f\x))" by simp hence "lcons\(f\x)\(lmap\f\(g\f\(f\x))) = lcons\(f\x)\(g\f\(f\(f\x)))" by simp hence "lmap\f\(lcons\x\(g\f\(f\x))) = lcons\(f\x)\(g\f\(f\(f\x)))" by (simp only: lmap_simps(2)) hence "lmap\f\(?h\g\f\x) = ?h\g\f\(f\x)" by simp } hence "?P (?h\g)" by blast } ultimately have "?P (fix\?F)" by (rule fix_ind[where ?F="?F" and ?P="?P"]) thus ?thesis by (simp add: inf_iterate_def) qed (* Using the take lemma HOLCF derived for llist. *) (* take_def: "llist_take \ \n. iterate n llist_copy \" *) (* take_lemmas: "(\n. llist_take n\?x = llist_take n\?x') \ ?x = ?x'" *) (* take_rews: *) (* "llist_take ?n\\ = \" *) (* "llist_take 0\?x = \" *) (* "llist_take (Suc ?n)\lnil = lnil" *) (* "llist_take (Suc ?n)\(lcons\?a\?l) = lcons\?a\(llist_take ?n\?l)" *) lemma fixes f :: "'a \ 'a" fixes x :: "'a" shows "mapiterate f x" proof - { fix n have "\x. llist_take n\(lmap\f\(inf_iterate\f\x)) = llist_take n\(inf_iterate\f\(f\x))" proof(induct n) case 0 show ?case by (simp add: take_rews) next case (Suc i) have "llist_take (Suc i)\(lmap\f\(inf_iterate\f\x)) = llist_take (Suc i)\(lcons\(f\x)\(lmap\f\(inf_iterate\f\(f\x))))" by (simp add: inf_iterate_simps[symmetric]) also have "\ = lcons\(f\x)\(llist_take i\(lmap\f\(inf_iterate\f\(f\x))))" by (simp only: llist.rews) also have "\ = lcons\(f\x)\(llist_take i\(inf_iterate\f\(f\(f\x))))" using Suc by simp also have "\ = llist_take (Suc i)\(lcons\(f\x)\(inf_iterate\f\(f\(f\x))))" by (simp add: llist.rews) also have "\ = llist_take (Suc i)\(inf_iterate\f\(f\x))" by (simp add: inf_iterate_simps[symmetric]) finally show ?case . qed } thus ?thesis by - (unfold mapiterate_def, rule take_lemmas) qed (* Using the coinduction rule HOLCF derived for llist. (* bisim_def: *) (* "llist_bisim \ *) (* \R. \x x'. *) (* R x x' \ *) (* x = \ \ x' = \ \ *) (* x = lnil \ x' = lnil \ *) (* (\a l l'. R l l' \ x = lcons\a\l \ x' = lcons\a\l')" *) (* casedist: *) (* "\?x = \ \ ?P; ?x = lnil \ ?P; \a l. ?x = lcons\a\l \ ?P\ \ ?P" *) (* coind: "\llist_bisim ?R; ?R ?x ?x'\ \ ?x = ?x'" *) *) lemma fixes f :: "'a \ 'a" fixes x :: "'a" shows "mapiterate f x" proof - let ?R = "\ xs ys. \ (f :: 'a \ 'a) (x :: 'a). xs = lmap\f\(inf_iterate\f\x) \ ys = inf_iterate\f\(f\x)" have "llist_bisim ?R" proof - { fix as bs assume rasbs: "?R as bs" then obtain f :: "'a \ 'a" and x :: "'a" where lhs: "as = lmap\f\(inf_iterate\f\x)" and rhs: "bs = inf_iterate\f\(f\x)" by blast from lhs have "as = lmap\f\(lcons\x\(inf_iterate\f\(f\x)))" by (simp add: inf_iterate_simps[symmetric]) also have "\ = lcons\(f\x)\(lmap\f\(inf_iterate\f\(f\x)))" by simp finally have as': "as = lcons\(f\x)\(lmap\f\(inf_iterate\f\(f\x)))" . from rhs have bs': "bs = lcons\(f\x)\(inf_iterate\f\(f\(f\ x)))" by (simp add: inf_iterate_simps[symmetric]) have rtails: "?R (lmap\f\(inf_iterate\f\(f\x))) (inf_iterate\f\(f\(f\x)))" by blast from rtails as' bs' have "\a l l'. ?R l l' \ as = lcons\a\l \ bs = lcons\a\l'" by blast } thus ?thesis by (unfold bisim_def, blast) qed moreover have "?R (lmap\f\(inf_iterate\f\x)) (inf_iterate\f\(f\x))" by blast ultimately show ?thesis by (unfold mapiterate_def, blast dest: llist.coind) qed (* Meta-theory *) (* "approx" is strict and total in it's first argument, and we need to do induction over it. Let's use HOL's @{typ "nat"}. *) consts approx :: "nat \ 'a llist \ 'a llist" primrec "approx 0 = \" "approx (Suc n) = (\ xxs. case xxs of lnil \ lnil | lcons\x\xs \ lcons\x\(approx n\xs))" lemma approx_strict[simp]: "approx x\\ = \" by (cases x, simp_all add: llist.rews) lemma approx_lnil[simp]: "approx (Suc x)\lnil = lnil" by (simp add: llist.rews) lemma approx_lnil_le[simp]: "approx x\lnil \ lnil" by (cases x, simp_all add: llist.rews) (* Left-to-right. *) lemma approx_chain: "chain approx" proof(rule chainI) fix n show "(approx n :: 'a llist \ 'a llist) \ approx (Suc n)" proof(induct n) case (Suc n) { fix xs :: "'a llist" have "approx (Suc n)\xs \ approx (Suc (Suc n))\xs" proof(cases xs rule: llist.casedist) case (3 y ys) from Suc have "approx n\ys \ approx (Suc n)\ys" by - (rule monofun_cfun_fun) hence "lcons\y\(approx n\ys) \ lcons\y\(approx (Suc n)\ys)" by (simp add: monofun_cfun) hence "approx (Suc n)\(lcons\y\ys) \ approx (Suc (Suc n))\(lcons\y\ys)" by (simp add: llist.rews) with 3 show ?thesis by simp qed (simp_all add: llist.rews) } thus ?case by (rule less_cfun_ext) qed simp qed lemma lub_shift: assumes chainF: "chain (F :: nat \ 'a::cpo)" shows "(\ i. F i) = (\ i. F (i + j))" (is "?lhs = ?rhs") proof - have "range F <<| ?lhs" using thelubE[OF chainF] by simp hence "range (\i. F (i + j)) <<| ?lhs" using is_lub_range_shift[OF chainF, where j=j] by simp thus "?lhs = ?rhs" using thelubI[where l="?lhs" and M="range (\i. F (i + j))"] by simp qed lemma approx_limit: "(\i. approx i) = ID" proof(rule ext_cfun) fix xs show "(\i. approx i)\xs = ID\xs" proof(induct xs rule: llist.ind) case 2 show ?case using contlub_cfun_fun[OF approx_chain, where x="\"] by simp next case 3 have "(\ i\nat. approx i)\lnil = (\ i\nat. approx i\lnil)" using contlub_cfun_fun[OF approx_chain, where x="lnil"] by simp also have "\ = lnil" by (rule lub_chain_maxelem[where i=1], simp_all add: llist.rews) finally show ?case by simp next case (4 y ys) have "(\ i\nat. approx i)\(lcons\y\ys) = (\ i\nat. approx i\(lcons\y\ys))" using contlub_cfun_fun[OF approx_chain, where x="lcons\y\ys"] by simp also have "\ = (\ i. approx (Suc i)\(lcons\y\ys))" using lub_shift[OF ch2ch_Rep_CFunL[OF approx_chain], where j=1] by simp also have "\ = (\ i. lcons\y\(approx i\ys))" by (simp add: llist.rews) also have "\ = lcons\y\(\ i. approx i\ys)" using contlub_cfun_arg[where f="lcons\y", OF ch2ch_Rep_CFunL[OF approx_chain]] by simp also have "\ = lcons\y\((\ i. approx i)\ys)" using contlub_cfun_fun[OF approx_chain, where x="ys"] by simp also from 4 have "\ = lcons\y\ys" by simp finally show ?case by simp qed simp qed lemma approx_lemma: "(\n. approx n\xs = approx n\ys) = (xs = ys)" (is "?lhs = ?rhs") proof assume lhs: "?lhs" hence "(\i. approx i\xs) = (\i. approx i\ys)" by simp hence "((\i. approx i)\xs) = ((\i. approx i)\ys)" by (simp add: contlub_cfun_fun [OF approx_chain]) hence "ID\xs = ID\ys" by (simp add: approx_limit) thus "xs = ys" by simp qed simp end