From 8e81962b476cf7f313194c24f7bf736020364bef Mon Sep 17 00:00:00 2001 From: Gerwin Klein Date: Mon, 30 Jan 2023 09:48:04 +1100 Subject: [PATCH] lib/monads: refactor + cleanup in No_Fail - factor out valid_NF definition and lemmas into NonDetMonad_Total - apply modern style and (very) occasional proof contraction in both Signed-off-by: Gerwin Klein --- lib/Monads/Monad_Equations.thy | 12 + lib/Monads/No_Fail.thy | 560 ++++--------------------------- lib/Monads/NonDetMonad_Total.thy | 349 +++++++++++++++++++ 3 files changed, 424 insertions(+), 497 deletions(-) create mode 100644 lib/Monads/NonDetMonad_Total.thy diff --git a/lib/Monads/Monad_Equations.thy b/lib/Monads/Monad_Equations.thy index bbd3479da..f85ee9859 100644 --- a/lib/Monads/Monad_Equations.thy +++ b/lib/Monads/Monad_Equations.thy @@ -552,4 +552,16 @@ lemma bind_known_operation_eq: apply (fastforce simp: valid_def empty_fail_def) done +lemma assert_opt_If: + "assert_opt v = If (v = None) fail (return (the v))" + by (simp add: assert_opt_def split: option.split) + +lemma if_to_top_of_bind: + "(bind (If P x y) z) = If P (bind x z) (bind y z)" + by (simp split: if_split) + +lemma if_to_top_of_bindE: + "(bindE (If P x y) z) = If P (bindE x z) (bindE y z)" + by (simp split: if_split) + end \ No newline at end of file diff --git a/lib/Monads/No_Fail.thy b/lib/Monads/No_Fail.thy index f4126ae94..9736a0874 100644 --- a/lib/Monads/No_Fail.thy +++ b/lib/Monads/No_Fail.thy @@ -17,58 +17,15 @@ begin subsection "Non-Failure" text \ - With the failure flag, we can formulate non-failure separately - from validity. A monad @{text m} does not fail under precondition - @{text P}, if for no start state in that precondition it sets - the failure flag. + With the failure flag, we can formulate non-failure separately from validity. + A monad @{text m} does not fail under precondition @{text P}, if for no start + state that satisfies the precondition it sets the failure flag. \ -definition - no_fail :: "('s \ bool) \ ('s,'a) nondet_monad \ bool" -where - "no_fail P m \ \s. P s \ \ (snd (m s))" +definition no_fail :: "('s \ bool) \ ('s,'a) nondet_monad \ bool" where + "no_fail P m \ \s. P s \ \snd (m s)" -text \ - It is often desired to prove non-failure and a Hoare triple - simultaneously, as the reasoning is often similar. The following - definitions allow such reasoning to take place. -\ - -definition - validNF ::"('s \ bool) \ ('s,'a) nondet_monad \ ('a \ 's \ bool) \ bool" - ("\_\/ _ /\_\!") -where - "validNF P f Q \ valid P f Q \ no_fail P f" - -definition - validE_NF :: "('s \ bool) \ ('s, 'a + 'b) nondet_monad \ - ('b \ 's \ bool) \ - ('a \ 's \ bool) \ bool" - ("\_\/ _ /(\_\,/ \_\!)") -where - "validE_NF P f Q E \ validE P f Q E \ no_fail P f" - -lemma validE_NF_alt_def: - "\ P \ B \ Q \,\ E \! = \ P \ B \ \v s. case v of Inl e \ E e s | Inr r \ Q r s \!" - by (clarsimp simp: validE_NF_def validE_def validNF_def) - -lemma validNF_make_schematic_post: - "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \!) \ - \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') \ f \ Q' \!" - by (auto simp add: valid_def validNF_def no_fail_def split: prod.splits) - -lemma validE_NF_make_schematic_post: - "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \, \ \rv s. E s0 rv s \!) \ - \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') - \ (\rv s'. E s0 rv s' \ E' rv s') \ f \ Q' \, \ E' \!" - by (auto simp add: validE_NF_def validE_def valid_def no_fail_def split: prod.splits sum.splits) - -lemma validNF_conjD1: "\ P \ f \ \rv s. Q rv s \ Q' rv s \! \ \ P \ f \ Q \!" - by (fastforce simp: validNF_def valid_def no_fail_def) - -lemma validNF_conjD2: "\ P \ f \ \rv s. Q rv s \ Q' rv s \! \ \ P \ f \ Q' \!" - by (fastforce simp: validNF_def valid_def no_fail_def) - +subsection \@{method wpc} setup\ lemma no_fail_pre[wp_pre]: "\ no_fail P f; \s. Q s \ P s\ \ no_fail Q f" @@ -78,29 +35,26 @@ lemma wpc_helper_no_fail_final: "no_fail Q f \ wpc_helper (P, P') (Q, Q') (no_fail P f)" by (clarsimp simp: wpc_helper_def elim!: no_fail_pre) -lemma wpc_helper_validNF: - "\Q\ g \S\! \ wpc_helper (P, P') (Q, Q') \P\ g \S\!" - apply (clarsimp simp: wpc_helper_def) - by (metis hoare_vcg_precond_imp no_fail_pre validNF_def) - wpc_setup "\m. no_fail P m" wpc_helper_no_fail_final -wpc_setup "\m. \P\ m \Q\!" wpc_helper_validNF +subsection \Bundles\ + bundle no_pre = hoare_pre [wp_pre del] no_fail_pre [wp_pre del] -bundle classic_wp_pre = hoare_pre [wp_pre del] - all_classic_wp_combs[wp_comb del] all_classic_wp_combs[wp_comb] +bundle classic_wp_pre = + hoare_pre [wp_pre del] + all_classic_wp_combs[wp_comb del] + all_classic_wp_combs[wp_comb] - -subsection "Non-Failure" +subsection \Lemmas\ lemma no_failD: "\ no_fail P m; P s \ \ \(snd (m s))" by (simp add: no_fail_def) -lemma no_fail_modify [wp,simp]: +lemma no_fail_modify[wp,simp]: "no_fail \ (modify f)" by (simp add: no_fail_def modify_def get_def put_def bind_def) @@ -109,106 +63,92 @@ lemma no_fail_gets_simp[simp]: unfolding no_fail_def gets_def get_def return_def bind_def by simp -lemma no_fail_gets: +lemma no_fail_gets[wp]: "no_fail \ (gets f)" by simp -lemma no_fail_select [simp]: +lemma no_fail_select[simp]: "no_fail \ (select S)" by (simp add: no_fail_def select_def) -lemma no_fail_alt [wp]: +lemma no_fail_alt[wp]: "\ no_fail P f; no_fail Q g \ \ no_fail (P and Q) (f \ g)" by (simp add: no_fail_def alternative_def) -lemma no_fail_return [simp, wp]: +lemma no_fail_return[simp, wp]: "no_fail \ (return x)" by (simp add: return_def no_fail_def) -lemma no_fail_get [simp, wp]: +lemma no_fail_get[simp, wp]: "no_fail \ get" by (simp add: get_def no_fail_def) -lemma no_fail_put [simp, wp]: +lemma no_fail_put[simp, wp]: "no_fail \ (put s)" by (simp add: put_def no_fail_def) -lemma no_fail_when [wp]: +lemma no_fail_when[wp]: "(P \ no_fail Q f) \ no_fail (if P then Q else \) (when P f)" by (simp add: when_def) -lemma no_fail_unless [wp]: +lemma no_fail_unless[wp]: "(\P \ no_fail Q f) \ no_fail (if P then \ else Q) (unless P f)" by (simp add: unless_def when_def) -lemma no_fail_fail [simp, wp]: +lemma no_fail_fail[simp, wp]: "no_fail \ fail" by (simp add: fail_def no_fail_def) -lemmas [wp] = no_fail_gets - -lemma no_fail_assert [simp, wp]: +lemma no_fail_assert[simp, wp]: "no_fail (\_. P) (assert P)" by (simp add: assert_def) -lemma no_fail_assert_opt [simp, wp]: +lemma no_fail_assert_opt[simp, wp]: "no_fail (\_. P \ None) (assert_opt P)" by (simp add: assert_opt_def split: option.splits) -lemma no_fail_case_option [wp]: +lemma no_fail_case_option[wp]: assumes f: "no_fail P f" assumes g: "\x. no_fail (Q x) (g x)" shows "no_fail (if x = None then P else Q (the x)) (case_option f g x)" by (clarsimp simp add: f g) -lemma no_fail_if [wp]: - "\ P \ no_fail Q f; \P \ no_fail R g \ \ - no_fail (if P then Q else R) (if P then f else g)" +lemma no_fail_if[wp]: + "\ P \ no_fail Q f; \P \ no_fail R g \ \ no_fail (if P then Q else R) (if P then f else g)" by simp -lemma no_fail_apply [wp]: +lemma no_fail_apply[wp]: "no_fail P (f (g x)) \ no_fail P (f $ g x)" by simp -lemma no_fail_undefined [simp, wp]: +lemma no_fail_undefined[simp, wp]: "no_fail \ undefined" by (simp add: no_fail_def) -lemma no_fail_returnOK [simp, wp]: +lemma no_fail_returnOK[simp, wp]: "no_fail \ (returnOk x)" by (simp add: returnOk_def) -lemma no_fail_bind [wp]: - assumes f: "no_fail P f" - assumes g: "\rv. no_fail (R rv) (g rv)" - assumes v: "\Q\ f \R\" - shows "no_fail (P and Q) (f >>= (\rv. g rv))" - apply (clarsimp simp: no_fail_def bind_def) - apply (rule conjI) - prefer 2 - apply (erule no_failD [OF f]) - apply clarsimp - apply (drule (1) use_valid [OF _ v]) - apply (drule no_failD [OF g]) - apply simp - done - +lemma no_fail_bind[wp]: + "\ no_fail P f; \rv. no_fail (R rv) (g rv); \Q\ f \R\ \ \ no_fail (P and Q) (f >>= (\rv. g rv))" + unfolding no_fail_def bind_def + using post_by_hoare by fastforce lemma no_fail_assume_pre: "(\s. P s \ no_fail P f) \ no_fail P f" by (simp add: no_fail_def) -lemma no_fail_liftM_eq [simp]: +lemma no_fail_liftM_eq[simp]: "no_fail P (liftM f m) = no_fail P m" by (auto simp: liftM_def no_fail_def bind_def return_def) -lemma no_fail_select_f [wp]: +lemma no_fail_select_f[wp]: "no_fail (\s. \snd S) (select_f S)" by (simp add: select_f_def no_fail_def) -lemma no_fail_liftM [wp]: +lemma no_fail_liftM[wp]: "no_fail P m \ no_fail P (liftM f m)" - by (simp) + by simp lemma no_fail_pre_and: "no_fail P f \ no_fail (P and Q) f" @@ -218,7 +158,7 @@ lemma no_fail_spec: "\ \s. no_fail (((=) s) and P) f \ \ no_fail P f" by (simp add: no_fail_def) -lemma no_fail_assertE [wp]: +lemma no_fail_assertE[wp]: "no_fail (\_. P) (assertE P)" by (simp add: assertE_def split: if_split) @@ -226,77 +166,44 @@ lemma no_fail_spec_pre: "\ no_fail (((=) s) and P') f; \s. P s \ P' s \ \ no_fail (((=) s) and P) f" by (erule no_fail_pre, simp) -lemma no_fail_whenE [wp]: +lemma no_fail_whenE[wp]: "\ G \ no_fail P f \ \ no_fail (\s. G \ P s) (whenE G f)" by (simp add: whenE_def split: if_split) -lemma no_fail_unlessE [wp]: +lemma no_fail_unlessE[wp]: "\ \ G \ no_fail P f \ \ no_fail (\s. \ G \ P s) (unlessE G f)" by (simp add: unlessE_def split: if_split) -lemma no_fail_throwError [wp]: +lemma no_fail_throwError[wp]: "no_fail \ (throwError e)" by (simp add: throwError_def) -lemma no_fail_liftE [wp]: +lemma no_fail_liftE[wp]: "no_fail P f \ no_fail P (liftE f)" unfolding liftE_def by wpsimp -lemma no_fail_gets_the [wp]: +lemma no_fail_gets_the[wp]: "no_fail (\s. f s \ None) (gets_the f)" - apply (simp add: gets_the_def) - apply (rule no_fail_pre, wp) - apply simp - done + unfolding gets_the_def + by wpsimp -lemma assert_opt_If: - "assert_opt v = If (v = None) fail (return (the v))" - by (simp_all add: assert_opt_def split: option.split) +lemma no_fail_lift: + "(\y. x = Inr y \ no_fail P (f y)) \ no_fail (\s. \isl x \ P s) (lift f x)" + unfolding lift_def + by (wpsimp wp: no_fail_throwError split: sum.splits | assumption)+ -lemma if_to_top_of_bind: - "(bind (If P x y) z) = If P (bind x z) (bind y z)" - by (simp split: if_split) +lemma validE_R_valid_eq: + "\Q\ f \R\, - = \Q\ f \\rv s. \ isl rv \ R (projr rv) s\" + unfolding validE_R_def validE_def valid_def + by (fastforce split: sum.splits prod.split) -lemma if_to_top_of_bindE: - "(bindE (If P x y) z) = If P (bindE x z) (bindE y z)" - by (simp split: if_split) - -lemma alternative_bind: - "((a \ b) >>= c) = ((a >>= c) \ (b >>= c))" - apply (rule ext, simp add: alternative_def bind_def split_def) - apply blast - done - -lemma alternative_refl: - "(a \ a) = a" - by (rule ext, simp add: alternative_def) - -lemma alternative_com: - "(f \ g) = (g \ f)" - apply (rule ext) - apply (auto simp: alternative_def) - done - -lemma liftE_alternative: - "liftE (a \ b) = (liftE a \ liftE b)" - by (simp add: liftE_def alternative_bind) - -lemma no_fail_bindE [wp]: +lemma no_fail_bindE[wp]: "\ no_fail P f; \rv. no_fail (R rv) (g rv); \Q\ f \R\,- \ \ no_fail (P and Q) (f >>=E g)" - apply (simp add: bindE_def) - apply (erule no_fail_bind) - apply (simp add: lift_def) - apply wpc - apply (simp add: throwError_def) - apply wp - apply assumption - apply (simp add: validE_R_def validE_def) - apply (erule hoare_strengthen_post) - apply clarsimp - done + unfolding bindE_def + by (wpsimp wp: no_fail_lift simp: validE_R_valid_eq | assumption)+ -lemma no_fail_False [simp]: +lemma no_fail_False[simp]: "no_fail (\_. False) X" by (clarsimp simp: no_fail_def) @@ -313,350 +220,9 @@ lemma no_fail_state_assert[wp]: unfolding state_assert_def by wpsimp -section "validNF Rules" - -subsection "Basic validNF theorems" - -lemma validNF [intro?]: - "\ \ P \ f \ Q \; no_fail P f \ \ \ P \ f \ Q \!" - by (clarsimp simp: validNF_def) - -lemma validNF_valid: "\ \ P \ f \ Q \! \ \ \ P \ f \ Q \" - by (clarsimp simp: validNF_def) - -lemma validNF_no_fail: "\ \ P \ f \ Q \! \ \ no_fail P f" - by (clarsimp simp: validNF_def) - -lemma snd_validNF: - "\ \ P \ f \ Q \!; P s \ \ \ snd (f s)" - by (clarsimp simp: validNF_def no_fail_def) - -lemma use_validNF: - "\ (r', s') \ fst (f s); \ P \ f \ Q \!; P s \ \ Q r' s'" - by (fastforce simp: validNF_def valid_def) - -subsection "validNF weakest pre-condition rules" - -lemma validNF_return [wp]: - "\ P x \ return x \ P \!" - by (wp validNF)+ - -lemma validNF_get [wp]: - "\ \s. P s s \ get \ P \!" - by (wp validNF)+ - -lemma validNF_put [wp]: - "\ \s. P () x \ put x \ P \!" - by (wp validNF)+ - -lemma validNF_K_bind [wp]: - "\ P \ x \ Q \! \ \ P \ K_bind x f \ Q \!" - by simp - -lemma validNF_fail [wp]: - "\ \s. False \ fail \ Q \!" - by (clarsimp simp: validNF_def fail_def no_fail_def) - -lemma validNF_prop [wp_unsafe]: - "\ no_fail (\s. P) f \ \ \ \s. P \ f \ \rv s. P \!" - by (wp validNF)+ - -lemma validNF_post_conj [intro!]: - "\ \ P \ a \ Q \!; \ P \ a \ R \! \ \ \ P \ a \ Q and R \!" - by (auto simp: validNF_def) - -lemma validNF_pre_disj [intro!]: - "\ \ P \ a \ R \!; \ Q \ a \ R \! \ \ \ P or Q \ a \ R \!" - by (rule validNF) (auto dest: validNF_valid validNF_no_fail intro: no_fail_or) - -(* - * Set up combination rules for WP, which also requires - * a "wp_trip" rule for validNF. - *) - -definition "validNF_property Q s b \ \ snd (b s) \ (\(r', s') \ fst (b s). Q r' s')" - -lemma validNF_is_triple [wp_trip]: - "validNF P f Q = triple_judgement P f (validNF_property Q)" - apply (clarsimp simp: validNF_def triple_judgement_def validNF_property_def) - apply (auto simp: no_fail_def valid_def) - done - -lemma validNF_weaken_pre[wp_pre]: - "\\Q\ a \R\!; \s. P s \ Q s\ \ \P\ a \R\!" - by (metis hoare_pre_imp no_fail_pre validNF_def) - -lemma validNF_post_comb_imp_conj: - "\ \P'\ f \Q\!; \P\ f \Q'\!; \s. P s \ P' s \ \ \P\ f \\rv s. Q rv s \ Q' rv s\!" - by (fastforce simp: validNF_def valid_def) - -lemma validNF_post_comb_conj_L: - "\ \P'\ f \Q\!; \P\ f \Q'\ \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\!" - apply (clarsimp simp: validNF_def valid_def no_fail_def) - apply force - done - -lemma validNF_post_comb_conj_R: - "\ \P'\ f \Q\; \P\ f \Q'\! \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\!" - apply (clarsimp simp: validNF_def valid_def no_fail_def) - apply force - done - -lemma validNF_post_comb_conj: - "\ \P'\ f \Q\!; \P\ f \Q'\! \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\!" - apply (clarsimp simp: validNF_def valid_def no_fail_def) - apply force - done - -lemma validNF_if_split [wp_split]: - "\P \ \Q\ f \S\!; \ P \ \R\ g \S\!\ \ \\s. (P \ Q s) \ (\ P \ R s)\ if P then f else g \S\!" - by simp - -lemma validNF_vcg_conj_lift: - "\ \P\ f \Q\!; \P'\ f \Q'\! \ \ - \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\!" - apply (subst pred_conj_def[symmetric], subst pred_conj_def[symmetric], rule validNF_post_conj) - apply (erule validNF_weaken_pre, fastforce) - apply (erule validNF_weaken_pre, fastforce) - done - -lemma validNF_vcg_disj_lift: - "\ \P\ f \Q\!; \P'\ f \Q'\! \ \ - \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\!" - apply (clarsimp simp: validNF_def) - apply safe - apply (auto intro!: hoare_vcg_disj_lift)[1] - apply (clarsimp simp: no_fail_def) - done - -lemma validNF_vcg_all_lift [wp]: - "\ \x. \P x\ f \Q x\! \ \ \\s. \x. P x s\ f \\rv s. \x. Q x rv s\!" - apply atomize - apply (rule validNF) - apply (clarsimp simp: validNF_def) - apply (rule hoare_vcg_all_lift) - apply force - apply (clarsimp simp: no_fail_def validNF_def) - done - -lemma validNF_bind [wp_split]: - "\ \x. \B x\ g x \C\!; \A\ f \B\! \ \ - \A\ do x \ f; g x od \C\!" - apply (rule validNF) - apply (metis validNF_valid hoare_seq_ext) - apply (clarsimp simp: no_fail_def validNF_def bind_def' valid_def) - apply blast - done - -lemmas validNF_seq_ext = validNF_bind - -subsection "validNF compound rules" - -lemma validNF_state_assert [wp]: - "\ \s. P () s \ G s \ state_assert G \ P \!" - apply (rule validNF) - apply wpsimp - apply (clarsimp simp: no_fail_def state_assert_def - bind_def' assert_def return_def get_def) - done - -lemma validNF_modify [wp]: - "\ \s. P () (f s) \ modify f \ P \!" - apply (clarsimp simp: modify_def) - apply wp - done - -lemma validNF_gets [wp]: - "\\s. P (f s) s\ gets f \P\!" - apply (clarsimp simp: gets_def) - apply wp - done - -lemma validNF_condition [wp]: - "\ \ Q \ A \P\!; \ R \ B \P\!\ \ \\s. if C s then Q s else R s\ condition C A B \P\!" - apply rule - apply (drule validNF_valid)+ - apply (erule (1) condition_wp) - apply (drule validNF_no_fail)+ - apply (clarsimp simp: no_fail_def condition_def) - done - -lemma validNF_alt_def: - "validNF P m Q = (\s. P s \ ((\(r', s') \ fst (m s). Q r' s') \ \ snd (m s)))" - by (fastforce simp: validNF_def valid_def no_fail_def) - -lemma validNF_assert [wp]: - "\ (\s. P) and (R ()) \ assert P \ R \!" - apply (rule validNF) - apply (clarsimp simp: valid_def in_return) - apply (clarsimp simp: no_fail_def return_def) - done - -lemma validNF_false_pre: - "\ \_. False \ P \ Q \!" - by (clarsimp simp: validNF_def no_fail_def) - -lemma validNF_chain: - "\\P'\ a \R'\!; \s. P s \ P' s; \r s. R' r s \ R r s\ \ \P\ a \R\!" - by (fastforce simp: validNF_def valid_def no_fail_def Ball_def) - -lemma validNF_case_prod [wp]: - "\ \x y. validNF (P x y) (B x y) Q \ \ validNF (case_prod P v) (case_prod (\x y. B x y) v) Q" - by (metis prod.exhaust split_conv) - -lemma validE_NF_case_prod [wp]: - "\ \a b. \P a b\ f a b \Q\, \E\! \ \ - \case x of (a, b) \ P a b\ case x of (a, b) \ f a b \Q\, \E\!" - apply (clarsimp simp: validE_NF_alt_def) - apply (erule validNF_case_prod) - done - -lemma no_fail_is_validNF_True: "no_fail P s = (\ P \ s \ \_ _. True \!)" - by (clarsimp simp: no_fail_def validNF_def valid_def) - -subsection "validNF reasoning in the exception monad" - -lemma validE_NF [intro?]: - "\ \ P \ f \ Q \,\ E \; no_fail P f \ \ \ P \ f \ Q \,\ E \!" - apply (clarsimp simp: validE_NF_def) - done - -lemma validE_NF_valid: - "\ \ P \ f \ Q \,\ E \! \ \ \ P \ f \ Q \,\ E \" - apply (clarsimp simp: validE_NF_def) - done - -lemma validE_NF_no_fail: - "\ \ P \ f \ Q \,\ E \! \ \ no_fail P f" - apply (clarsimp simp: validE_NF_def) - done - -lemma validE_NF_weaken_pre[wp_pre]: - "\\Q\ a \R\,\E\!; \s. P s \ Q s\ \ \P\ a \R\,\E\!" - apply (clarsimp simp: validE_NF_alt_def) - apply (erule validNF_weaken_pre) - apply simp - done - -lemma validE_NF_post_comb_conj_L: - "\ \P\ f \Q\, \ E \!; \P'\ f \Q'\, \ \_ _. True \ \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\, \ E \!" - apply (clarsimp simp: validE_NF_alt_def validE_def validNF_def - valid_def no_fail_def split: sum.splits) - apply force - done - -lemma validE_NF_post_comb_conj_R: - "\ \P\ f \Q\, \ \_ _. True \; \P'\ f \Q'\, \ E \! \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\, \ E \!" - apply (clarsimp simp: validE_NF_alt_def validE_def validNF_def - valid_def no_fail_def split: sum.splits) - apply force - done - -lemma validE_NF_post_comb_conj: - "\ \P\ f \Q\, \ E \!; \P'\ f \Q'\, \ E \! \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\, \ E \!" - apply (clarsimp simp: validE_NF_alt_def validE_def validNF_def - valid_def no_fail_def split: sum.splits) - apply force - done - -lemma validE_NF_chain: - "\\P'\ a \R'\,\E'\!; - \s. P s \ P' s; - \r' s'. R' r' s' \ R r' s'; - \r'' s''. E' r'' s'' \ E r'' s''\ \ - \\s. P s \ a \\r' s'. R r' s'\,\\r'' s''. E r'' s''\!" - by (fastforce simp: validE_NF_def validE_def2 no_fail_def Ball_def split: sum.splits) - -lemma validE_NF_bind_wp [wp]: - "\\x. \B x\ g x \C\, \E\!; \A\ f \B\, \E\!\ \ \A\ f >>=E (\x. g x) \C\, \E\!" - apply (unfold validE_NF_alt_def bindE_def) - apply (rule validNF_bind [rotated]) - apply assumption - apply (clarsimp simp: lift_def throwError_def split: sum.splits) - apply wpsimp - done - -lemma validNF_catch [wp]: - "\\x. \E x\ handler x \Q\!; \P\ f \Q\, \E\!\ \ \P\ f (\x. handler x) \Q\!" - apply (unfold validE_NF_alt_def catch_def) - apply (rule validNF_bind [rotated]) - apply assumption - apply (clarsimp simp: lift_def throwError_def split: sum.splits) - apply wp - done - -lemma validNF_throwError [wp]: - "\E e\ throwError e \P\, \E\!" - by (unfold validE_NF_alt_def throwError_def o_def) wpsimp - -lemma validNF_returnOk [wp]: - "\P e\ returnOk e \P\, \E\!" - by (clarsimp simp: validE_NF_alt_def returnOk_def) wpsimp - -lemma validNF_whenE [wp]: - "(P \ \Q\ f \R\, \E\!) \ \if P then Q else R ()\ whenE P f \R\, \E\!" - unfolding whenE_def by clarsimp wp - -lemma validNF_nobindE [wp]: - "\ \B\ g \C\,\E\!; - \A\ f \\r s. B s\,\E\! \ \ - \A\ doE f; g odE \C\,\E\!" - by clarsimp wp - -text \ -Setup triple rules for @{term validE_NF} so that we can use -wp combinator rules. -\ - -definition "validE_NF_property Q E s b \ \ snd (b s) - \ (\(r', s') \ fst (b s). case r' of Inl x \ E x s' | Inr x \ Q x s')" - -lemma validE_NF_is_triple [wp_trip]: - "validE_NF P f Q E = triple_judgement P f (validE_NF_property Q E)" - apply (clarsimp simp: validE_NF_def validE_def2 no_fail_def triple_judgement_def - validE_NF_property_def split: sum.splits) - apply blast - done - -lemma validNF_cong: - "\ \s. P s = P' s; \s. P s \ m s = m' s; - \r' s' s. \ P s; (r', s') \ fst (m s) \ \ Q r' s' = Q' r' s' \ \ - (\ P \ m \ Q \!) = (\ P' \ m' \ Q' \!)" - by (fastforce simp: validNF_alt_def) - -lemma validE_NF_liftE [wp]: - "\P\ f \Q\! \ \P\ liftE f \Q\,\E\!" - by (wpsimp simp: validE_NF_alt_def liftE_def) - -lemma validE_NF_handleE' [wp]: - "\ \x. \F x\ handler x \Q\,\E\!; \P\ f \Q\,\F\! \ \ - \P\ f (\x. handler x) \Q\,\E\!" - apply (unfold validE_NF_alt_def handleE'_def) - apply (rule validNF_bind [rotated]) - apply assumption - apply (clarsimp split: sum.splits) - apply wpsimp - done - -lemma validE_NF_handleE [wp]: - "\ \x. \F x\ handler x \Q\,\E\!; \P\ f \Q\,\F\! \ \ - \P\ f handler \Q\,\E\!" - apply (unfold handleE_def) - apply (metis validE_NF_handleE') - done - -lemma validE_NF_condition [wp]: - "\ \ Q \ A \P\,\ E \!; \ R \ B \P\,\ E \!\ - \ \\s. if C s then Q s else R s\ condition C A B \P\,\ E \!" - apply rule - apply (drule validE_NF_valid)+ - apply wp - apply (drule validE_NF_no_fail)+ - apply (clarsimp simp: no_fail_def condition_def) - done - -lemma hoare_assume_preNF: - "(\s. P s \ \P\ f \Q\!) \ \P\ f \Q\!" - by (metis validNF_alt_def) +lemma no_fail_condition: + "\no_fail Q A; no_fail R B\ \ no_fail (\s. (C s \ Q s) \ (\ C s \ R s)) (condition C A B)" + unfolding condition_def no_fail_def + by clarsimp end diff --git a/lib/Monads/NonDetMonad_Total.thy b/lib/Monads/NonDetMonad_Total.thy new file mode 100644 index 000000000..7ad256f5a --- /dev/null +++ b/lib/Monads/NonDetMonad_Total.thy @@ -0,0 +1,349 @@ +(* + * Copyright 2023, Proofcraft Pty Ltd + * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230) + * + * SPDX-License-Identifier: BSD-2-Clause + *) + +(* Total correctness Hoare logic for the NonDetMonad (= valid + no_fail) *) + +theory NonDetMonad_Total + imports No_Fail +begin + +section \Total correctness for NonDetMonad and NonDetMonad with exceptions\ + +subsection Definitions + +text \ + It is often desired to prove non-failure and a Hoare triple simultaneously, as the reasoning + is often similar. The following definitions allow such reasoning to take place.\ + +definition validNF :: + "('s \ bool) \ ('s,'a) nondet_monad \ ('a \ 's \ bool) \ bool" ("\_\/ _ /\_\!") where + "\P\ f \Q\! \ \P\ f \Q\ \ no_fail P f" + +lemma validNF_alt_def: + "\P\ f \Q\! = (\s. P s \ ((\(r', s') \ fst (f s). Q r' s') \ \ snd (f s)))" + by (fastforce simp: validNF_def valid_def no_fail_def) + +definition validE_NF :: + "('s \ bool) \ ('s, 'a + 'b) nondet_monad \ ('b \ 's \ bool) \ ('a \ 's \ bool) \ bool" + ("\_\/ _ /(\_\,/ \_\!)") where + "\P\ f \Q\, \E\! \ \P\ f \Q\, \E\ \ no_fail P f" + +lemma validE_NF_alt_def: + "\P\ f \Q\, \E\! = \P\ f \\v s. case v of Inl e \ E e s | Inr r \ Q r s\!" + by (clarsimp simp: validE_NF_def validE_def validNF_def) + + +subsection \@{method wpc} setup\ + +lemma wpc_helper_validNF: + "\Q\ g \S\! \ wpc_helper (P, P') (Q, Q') \P\ g \S\!" + unfolding wpc_helper_def + by clarsimp (metis hoare_vcg_precond_imp no_fail_pre validNF_def) + +wpc_setup "\m. \P\ m \Q\!" wpc_helper_validNF + + +subsection \Basic @{const validNF} theorems\ + +lemma validNF_make_schematic_post: + "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \!) \ + \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') \ f \ Q' \!" + by (auto simp add: valid_def validNF_def no_fail_def split: prod.splits) + +lemma validE_NF_make_schematic_post: + "(\s0. \ \s. P s0 s \ f \ \rv s. Q s0 rv s \, \ \rv s. E s0 rv s \!) \ + \ \s. \s0. P s0 s \ (\rv s'. Q s0 rv s' \ Q' rv s') + \ (\rv s'. E s0 rv s' \ E' rv s') \ f \ Q' \, \ E' \!" + by (auto simp add: validE_NF_def validE_def valid_def no_fail_def split: prod.splits sum.splits) + +lemma validNF_conjD1: + "\ P \ f \ \rv s. Q rv s \ Q' rv s \! \ \ P \ f \ Q \!" + by (fastforce simp: validNF_def valid_def no_fail_def) + +lemma validNF_conjD2: + "\ P \ f \ \rv s. Q rv s \ Q' rv s \! \ \ P \ f \ Q' \!" + by (fastforce simp: validNF_def valid_def no_fail_def) + +lemma validNF[intro?]: (* FIXME lib: should be validNFI *) + "\ \ P \ f \ Q \; no_fail P f \ \ \ P \ f \ Q \!" + by (clarsimp simp: validNF_def) + +lemma validNFE: + "\ \ P \ f \ Q \!; \ \ P \ f \ Q \; no_fail P f \ \ R \ \ R" + by (clarsimp simp: validNF_def) + +lemma validNF_valid: + "\ \ P \ f \ Q \! \ \ \ P \ f \ Q \" + by (erule validNFE) + +lemma validNF_no_fail: + "\ \ P \ f \ Q \! \ \ no_fail P f" + by (erule validNFE) + +lemma snd_validNF: + "\ \ P \ f \ Q \!; P s \ \ \ snd (f s)" + by (clarsimp simp: validNF_def no_fail_def) + +lemma use_validNF: + "\ (r', s') \ fst (f s); \ P \ f \ Q \!; P s \ \ Q r' s'" + by (fastforce simp: validNF_def valid_def) + + +subsection \@{const validNF} weakest precondition rules\ + +lemma validNF_return[wp]: + "\ P x \ return x \ P \!" + by (wp validNF)+ + +lemma validNF_get[wp]: + "\ \s. P s s \ get \ P \!" + by (wp validNF)+ + +lemma validNF_put[wp]: + "\ \s. P () x \ put x \ P \!" + by (wp validNF)+ + +lemma validNF_K_bind[wp]: + "\ P \ x \ Q \! \ \ P \ K_bind x f \ Q \!" + by simp + +lemma validNF_fail[wp]: + "\ \s. False \ fail \ Q \!" + by (clarsimp simp: validNF_def fail_def no_fail_def) + +lemma validNF_prop[wp_unsafe]: + "\ no_fail (\s. P) f \ \ \ \s. P \ f \ \rv s. P \!" + by (wp validNF)+ + +lemma validNF_post_conj[intro!]: + "\ \ P \ a \ Q \!; \ P \ a \ R \! \ \ \ P \ a \ Q and R \!" + by (auto simp: validNF_def) + +lemma validNF_pre_disj[intro!]: + "\ \ P \ a \ R \!; \ Q \ a \ R \! \ \ \ P or Q \ a \ R \!" + by (rule validNF) (auto dest: validNF_valid validNF_no_fail intro: no_fail_or) + +text \ + Set up combination rules for @{method wp}, which also requires a @{text wp_trip} rule for + @{const validNF}.\ +definition validNF_property :: "('a \ 's \ bool) \ 's \ ('s,'a) nondet_monad \ bool" where + "validNF_property Q s b \ \ snd (b s) \ (\(r', s') \ fst (b s). Q r' s')" + +lemma validNF_is_triple[wp_trip]: + "validNF P f Q = triple_judgement P f (validNF_property Q)" + by (auto simp: validNF_def triple_judgement_def validNF_property_def no_fail_def valid_def) + +lemma validNF_weaken_pre[wp_pre]: + "\\Q\ a \R\!; \s. P s \ Q s\ \ \P\ a \R\!" + by (metis hoare_pre_imp no_fail_pre validNF_def) + +lemma validNF_post_comb_imp_conj: + "\ \P'\ f \Q\!; \P\ f \Q'\!; \s. P s \ P' s \ \ \P\ f \\rv s. Q rv s \ Q' rv s\!" + by (fastforce simp: validNF_def valid_def) + +lemma validNF_post_comb_conj_L: + "\ \P'\ f \Q\!; \P\ f \Q'\ \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\!" + by (fastforce simp: validNF_def valid_def no_fail_def) + +lemma validNF_post_comb_conj_R: + "\ \P'\ f \Q\; \P\ f \Q'\! \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\!" + by (fastforce simp: validNF_def valid_def no_fail_def) + +lemma validNF_post_comb_conj: + "\ \P'\ f \Q\!; \P\ f \Q'\! \ \ \\s. P s \ P' s \ f \\rv s. Q rv s \ Q' rv s\!" + by (fastforce simp: validNF_def valid_def no_fail_def) + +lemma validNF_if_split[wp_split]: + "\P \ \Q\ f \S\!; \ P \ \R\ g \S\!\ \ + \\s. (P \ Q s) \ (\ P \ R s)\ if P then f else g \S\!" + by simp + +lemma validNF_vcg_conj_lift: + "\ \P\ f \Q\!; \P'\ f \Q'\! \ \ \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\!" + by (fastforce intro!: validNF_post_conj[unfolded pred_conj_def] intro: validNF_weaken_pre) + +lemma validNF_vcg_disj_lift: + "\ \P\ f \Q\!; \P'\ f \Q'\! \ \ \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\!" + by (auto simp: validNF_def no_fail_def intro!: hoare_vcg_disj_lift) + +lemma validNF_vcg_all_lift[wp]: + "\ \x. \P x\ f \Q x\! \ \ \\s. \x. P x s\ f \\rv s. \x. Q x rv s\!" + by (auto simp: validNF_def no_fail_def intro!: hoare_vcg_all_lift) + +lemma validNF_bind[wp_split]: + "\ \x. \B x\ g x \C\!; \A\ f \B\! \ \ \A\ do x \ f; g x od \C\!" + unfolding validNF_def + by (auto intro: hoare_seq_ext no_fail_bind[where P=Q and Q=Q for Q, simplified]) + +lemmas validNF_seq_ext = validNF_bind + + +subsection "validNF compound rules" + +lemma validNF_state_assert[wp]: + "\ \s. P () s \ G s \ state_assert G \ P \!" + by (rule validNF; wpsimp) + +lemma validNF_modify[wp]: + "\ \s. P () (f s) \ modify f \ P \!" + by (rule validNF; wpsimp) + +lemma validNF_gets[wp]: + "\\s. P (f s) s\ gets f \P\!" + by (rule validNF; wpsimp) + +lemma validNF_condition[wp]: + "\ \ Q \ A \P\!; \ R \ B \P\!\ \ \\s. if C s then Q s else R s\ condition C A B \P\!" + by (erule validNFE)+ + (rule validNF; wpsimp wp: no_fail_condition) + +lemma validNF_assert[wp]: + "\ (\s. P) and (R ()) \ assert P \ R \!" + by (rule validNF; wpsimp) + +lemma validNF_false_pre: + "\ \_. False \ P \ Q \!" + by (rule validNF; wpsimp) + +lemma validNF_chain: + "\\P'\ a \R'\!; \s. P s \ P' s; \r s. R' r s \ R r s\ \ \P\ a \R\!" + by (fastforce simp: validNF_def valid_def no_fail_def Ball_def) + +lemma validNF_case_prod[wp]: + "(\x y. \P x y\ B x y \Q\!) \ \case v of (x, y) \ P x y\ case v of (x, y) \ B x y \Q\!" + by (metis prod.exhaust split_conv) + +lemma validE_NF_case_prod[wp]: + "\ \a b. \P a b\ f a b \Q\, \E\! \ \ + \case x of (a, b) \ P a b\ case x of (a, b) \ f a b \Q\, \E\!" + unfolding validE_NF_alt_def + by (erule validNF_case_prod) + +lemma no_fail_is_validNF_True: + "no_fail P s = (\ P \ s \ \_ _. True \!)" + by (clarsimp simp: no_fail_def validNF_def valid_def) + + +subsection \@{const validNF} reasoning in the exception monad\ + +lemma validE_NF[intro?]: (* FIXME lib: should be validE_NFI *) + "\ \ P \ f \ Q \,\ E \; no_fail P f \ \ \ P \ f \ Q \,\ E \!" + by (clarsimp simp: validE_NF_def) + +lemma validE_NFE: + "\ \ P \ f \ Q \,\ E \!; \ \ P \ f \ Q \,\ E \; no_fail P f \ \ R \ \ R" + by (clarsimp simp: validE_NF_def) + +lemma validE_NF_valid: + "\ \ P \ f \ Q \,\ E \! \ \ \ P \ f \ Q \,\ E \" + by (rule validE_NFE) + +lemma validE_NF_no_fail: + "\ \ P \ f \ Q \,\ E \! \ \ no_fail P f" + by (rule validE_NFE) + +lemma validE_NF_weaken_pre[wp_pre]: + "\\Q\ a \R\,\E\!; \s. P s \ Q s\ \ \P\ a \R\,\E\!" + by (simp add: validE_NF_alt_def validNF_weaken_pre) + +lemma validE_NF_post_comb_conj_L: + "\ \P\ f \Q\, \E\!; \P'\ f \Q'\, \\_ _. True\ \ \ + \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\, \E\!" + unfolding validE_NF_alt_def + by (fastforce simp: validE_def validNF_def valid_def no_fail_def split: sum.splits) + +lemma validE_NF_post_comb_conj_R: + "\ \P\ f \Q\, \\_ _. True\; \P'\ f \Q'\, \E\! \ \ + \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\, \E\!" + unfolding validE_NF_alt_def validE_def validNF_def valid_def no_fail_def + by (fastforce split: sum.splits) + +lemma validE_NF_post_comb_conj: + "\ \P\ f \Q\, \E\!; \P'\ f \Q'\, \E\! \ \ \\s. P s \ P' s\ f \\rv s. Q rv s \ Q' rv s\, \E\!" + unfolding validE_NF_alt_def validE_def validNF_def valid_def no_fail_def + by (fastforce split: sum.splits) + +lemma validE_NF_chain: + "\ \P'\ a \R'\,\E'\!; \s. P s \ P' s; \r' s'. R' r' s' \ R r' s'; + \r'' s''. E' r'' s'' \ E r'' s''\ \ + \\s. P s \ a \\r' s'. R r' s'\,\\r'' s''. E r'' s''\!" + by (fastforce simp: validE_NF_def validE_def2 no_fail_def Ball_def split: sum.splits) + +lemma validE_NF_bind_wp[wp]: + "\\x. \B x\ g x \C\, \E\!; \A\ f \B\, \E\!\ \ \A\ f >>=E (\x. g x) \C\, \E\!" + by (blast intro: validE_NF hoare_vcg_seqE no_fail_pre no_fail_bindE validE_validE_R validE_weaken + elim!: validE_NFE) + +lemma validNF_catch[wp]: + "\\x. \E x\ handler x \Q\!; \P\ f \Q\, \E\!\ \ \P\ f (\x. handler x) \Q\!" + unfolding validE_NF_alt_def catch_def lift_def throwError_def + by (clarsimp simp: validNF_return split: sum.splits elim!: validNF_bind[rotated]) + +lemma validNF_throwError[wp]: + "\E e\ throwError e \P\, \E\!" + by (unfold validE_NF_alt_def throwError_def o_def) wpsimp + +lemma validNF_returnOk[wp]: + "\P e\ returnOk e \P\, \E\!" + by (clarsimp simp: validE_NF_alt_def returnOk_def) wpsimp + +lemma validNF_whenE[wp]: + "(P \ \Q\ f \R\, \E\!) \ \if P then Q else R ()\ whenE P f \R\, \E\!" + unfolding whenE_def by wpsimp + +lemma validNF_nobindE[wp]: + "\ \B\ g \C\,\E\!; \A\ f \\r s. B s\,\E\! \ \ \A\ doE f; g odE \C\,\E\!" + by wpsimp + +text \ + Set up triple rules for @{term validE_NF} so that we can use @{method wp} combinator rules.\ +definition validE_NF_property :: + "('a \ 's \ bool) \ ('c \ 's \ bool) \ 's \ ('s, 'c+'a) nondet_monad \ bool" where + "validE_NF_property Q E s b \ + \ snd (b s) \ (\(r', s') \ fst (b s). case r' of Inl x \ E x s' | Inr x \ Q x s')" + +lemma validE_NF_is_triple[wp_trip]: + "validE_NF P f Q E = triple_judgement P f (validE_NF_property Q E)" + by (fastforce simp: validE_NF_def validE_def2 no_fail_def triple_judgement_def + validE_NF_property_def + split: sum.splits) + +lemma validNF_cong: + "\ \s. P s = P' s; \s. P s \ m s = m' s; + \r' s' s. \ P s; (r', s') \ fst (m s) \ \ Q r' s' = Q' r' s' \ \ + (\P\ m \Q\!) = (\P'\ m' \Q'\!)" + by (fastforce simp: validNF_alt_def) + +lemma validE_NF_liftE[wp]: + "\P\ f \Q\! \ \P\ liftE f \Q\,\E\!" + by (wpsimp simp: validE_NF_alt_def liftE_def) + +lemma validE_NF_handleE'[wp]: + "\ \x. \F x\ handler x \Q\,\E\!; \P\ f \Q\,\F\! \ \ + \P\ f (\x. handler x) \Q\,\E\!" + unfolding validE_NF_alt_def handleE'_def + apply (erule validNF_bind[rotated]) + apply (clarsimp split: sum.splits) + apply wpsimp + done + +lemma validE_NF_handleE[wp]: + "\ \x. \F x\ handler x \Q\,\E\!; \P\ f \Q\,\F\! \ \ + \P\ f handler \Q\,\E\!" + unfolding handleE_def + by (metis validE_NF_handleE') + +lemma validE_NF_condition[wp]: + "\ \ Q \ A \P\,\ E \!; \ R \ B \P\,\ E \! \ \ + \\s. if C s then Q s else R s\ condition C A B \P\,\ E \!" + by (erule validE_NFE)+ (wpsimp wp: no_fail_condition validE_NF) + +lemma hoare_assume_preNF: + "(\s. P s \ \P\ f \Q\!) \ \P\ f \Q\!" + by (metis validNF_alt_def) + +end \ No newline at end of file