Theory Combinators
theory Combinators
imports
Programs
begin
section‹ More combinators\label{sec:combinators} ›
text‹
Extra combinators:
▪ ‹prog.select› shows how we can handle arbitrary choice
▪ ‹prog.while› combinator expresses all tail-recursive computations. Its condition is a pure value.
›
setup ‹Sign.mandatory_path "prog"›
definition select :: "'v set ⇒ ('s, 'v) prog" where
"select X = (⨆x∈X. prog.return x)"
context
notes [[function_internals]]
begin
partial_function (lfp) while :: "('k ⇒ ('s, 'k + 'v) prog) ⇒ 'k ⇒ ('s, 'v) prog" where
"while c k = c k ⤜ (λrv. case rv of Inl k' ⇒ while c k' | Inr v ⇒ prog.return v)"
end
abbreviation loop :: "('s, unit) prog ⇒ ('s, 'w) prog" where
"loop P ≡ prog.while (λ(). P ⪢ prog.return (Inl ())) ()"
abbreviation guardM :: "bool ⇒ ('s, unit) prog" where
"guardM b ≡ if b then ⊥ else prog.return ()"
abbreviation unlessM :: "bool ⇒ ('s, unit) prog ⇒ ('s, unit) prog" where
"unlessM b c ≡ if b then prog.return () else c"
abbreviation whenM :: "bool ⇒ ('s, unit) prog ⇒ ('s, unit) prog" where
"whenM b c ≡ if b then c else prog.return ()"
definition app :: "('a ⇒ ('s, unit) prog) ⇒ 'a list ⇒ ('s, unit) prog" where
"app f xs = foldr (λx m. f x ⪢ m) xs (prog.return ())"
definition set_app :: "('a ⇒ ('s, unit) prog) ⇒ 'a set ⇒ ('s, unit) prog" where
"set_app f =
prog.while (λX. if X = {} then prog.return (Inr ())
else prog.select X ⤜ (λx. f x ⪢ prog.return (Inl (X - {x}))))"
primrec foldM :: "('b ⇒ 'a ⇒ ('s, 'b) prog) ⇒ 'b ⇒ 'a list ⇒ ('s, 'b) prog" where
"foldM f b [] = prog.return b"
| "foldM f b (x # xs) = do {
b' ← f b x;
foldM f b' xs
}"
primrec fold_mapM :: "('a ⇒ ('s, 'b) prog) ⇒ 'a list ⇒ ('s, 'b list) prog" where
"fold_mapM f [] = prog.return []"
| "fold_mapM f (x # xs) = do {
y ← f x;
ys ← fold_mapM f xs;
prog.return (y # ys)
}"
setup ‹Sign.mandatory_path "select"›
lemma empty:
shows "prog.select {} = ⊥"
by (simp add: prog.select_def)
lemma singleton:
shows "prog.select {x} = prog.return x"
by (simp add: prog.select_def)
lemma monotone:
shows "mono prog.select"
by (simp add: monoI prog.select_def SUP_subset_mono)
lemmas strengthen[strg] = st_monotone[OF prog.select.monotone]
lemmas mono = monotoneD[OF prog.select.monotone, of P Q for P Q]
lemmas mono2mono[cont_intro, partial_function_mono] = monotone2monotone[OF prog.select.monotone, simplified, of orda P for orda P]
lemma Sup:
shows "prog.select (⋃X) = (⨆x∈X. prog.select x)"
by (simp add: prog.select_def flip: SUP_UNION)
lemma mcont:
shows "mcont ⋃ (⊆) Sup (≤) prog.select"
by (simp add: mcontI contI prog.select.monotone prog.select.Sup)
lemmas mcont2mcont[cont_intro] = mcont2mcont[OF prog.select.mcont, of supa orda P for supa orda P]
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "return"›
lemma select_le:
assumes "x ∈ X"
shows "prog.return x ≤ prog.select X"
by (simp add: assms prog.select_def SUP_upper)
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "bind"›
lemma selectL:
shows "prog.select X ⤜ g = (⨆x∈X. g x)"
by (simp add: prog.select_def prog.bind.SUPL prog.bind.returnL)
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "while"›
lemma bot:
shows "prog.while ⊥ = ⊥"
by (simp add: fun_eq_iff prog.while.simps prog.bind.botL)
lemma monotone:
shows "mono (λP. prog.while P s)"
by (rule monoI)
(induct arbitrary: s rule: prog.while.fixp_induct; simp add: prog.bind.mono le_funD split: sum.split)
lemmas strengthen[strg] = st_monotone[OF prog.while.monotone]
lemmas mono' = monotoneD[OF prog.while.monotone, of P Q for P Q]
lemmas mono2mono[cont_intro, partial_function_mono] = monotone2monotone[OF prog.while.monotone, simplified, of orda P for orda P]
lemma Sup_le:
shows "(⨆P∈X. prog.while P s) ≤ prog.while (⨆X) s"
by (simp add: SUP_le_iff SupI prog.while.mono')
lemma Inf_le:
shows "prog.while (⨅X) s ≤ (⨅P∈X. prog.while P s)"
by (simp add: le_INF_iff Inf_lower prog.while.mono')
lemma True_skip_eq_bot:
shows "prog.while ⟨prog.return (Inl x)⟩ s = ⊥"
by (induct arbitrary: s rule: prog.while.fixp_induct) (simp_all add: prog.bind.returnL)
lemma Inr_eq_return:
shows "prog.while ⟨prog.return (Inr v)⟩ s = prog.return v"
by (subst prog.while.simps) (simp add: prog.bind.returnL)
lemma kleene_star:
shows "prog.kleene.star P
= prog.while (λ_. (P ⪢ prog.return (Inl ())) ⊔ prog.return (Inr ())) ()" (is "?lhs = ?rhs")
proof(rule antisym)
show "?lhs ≤ ?rhs"
proof(induct rule: prog.kleene.star.fixp_induct[case_names adm bot step])
case (step P) then show ?case
by (subst prog.while.simps) (simp add: prog.bind.supL prog.bind.bind prog.bind.mono sup.coboundedI1 prog.bind.returnL)
qed simp_all
show "?rhs ≤ ?lhs"
proof(induct rule: prog.while.fixp_induct[case_names adm bot step])
case (step k) then show ?case
by (subst prog.kleene.star.simps) (simp add: prog.bind.supL prog.bind.bind prog.bind.mono prog.bind.returnL le_supI1)
qed simp_all
qed
lemma invmap_le:
fixes sf :: "'s ⇒ 't"
fixes vf :: "'v ⇒ 'w"
shows "prog.while (λk. prog.invmap sf (map_sum id vf) (c k)) k
≤ prog.invmap sf vf (prog.while c k)" (is "?lhs prog.while k ≤ ?rhs k")
proof(rule spec[where x=k],
induct rule: prog.while.fixp_induct[where P="λR. ∀k. ?lhs R k ≤ ?rhs k", case_names adm bot step])
case (step k) show ?case
apply (subst prog.while.simps)
apply (strengthen ord_to_strengthen(1)[OF step[rule_format]])
apply (auto intro!: SUPE prog.bind.mono[OF order.refl]
split: sum.splits
simp: prog.invmap.bind prog.invmap.return
prog.invmap.split_vinvmap[where sf=sf and vf="map_sum id vf"]
prog.bind.bind prog.bind.return prog.bind.SUPL
SUP_upper
order.trans[OF _ prog.bind.mono[OF prog.return.rel_le order.refl]])
done
qed (simp_all add: prog.invmap.bot)
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "loop"›
lemma bindL:
fixes P :: "('s, unit) prog"
fixes Q :: "('s, 'w) prog"
shows "prog.loop P ⪢ Q = prog.loop P" (is "?lhs = ?rhs")
proof(rule antisym)
show "?lhs ≤ ?rhs"
by (rule prog.while.fixp_induct[where P="λR. R (λ(). P ⪢ prog.return (Inl ())) () ⪢ Q ≤ ?rhs"]; simp add: prog.bind.botL)
(subst prog.while.simps; simp add: prog.bind.bind prog.bind.mono lambda_unit_futzery prog.bind.returnL)
show "?rhs ≤ ?lhs"
by (rule prog.while.fixp_induct[where P="λR. R (λ(). P ⪢ prog.return (Inl ())) () ≤ ?lhs"]; simp)
(subst prog.while.simps; simp add: prog.bind.bind prog.bind.mono lambda_unit_futzery prog.bind.returnL)
qed
lemma parallel_le:
shows "prog.loop P ≤ lfp (λR. P ∥ R)"
proof(induct rule: prog.while.fixp_induct[case_names adm bot step])
case (step k) show ?case
apply (subst lfp_unfold, simp)
apply (strengthen ord_to_strengthen[OF prog.bind.parallel_le])
apply (simp add: prog.bind.bind prog.bind.mono prog.bind.returnL step)
done
qed simp_all
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "foldM"›
lemma append:
shows "prog.foldM f b (xs @ ys) = prog.foldM f b xs ⤜ (λb'. prog.foldM f b' ys)"
by (induct xs arbitrary: b) (simp_all add: prog.bind.returnL prog.bind.bind)
setup ‹Sign.parent_path›
lemma foldM_alt_def:
shows "prog.foldM f b xs = foldr (λx m. prog.bind m (λb. f b x)) (rev xs) (prog.return b)"
by (induct xs arbitrary: b rule: rev_induct) (simp_all add: prog.foldM.append prog.bind.returnR)
setup ‹Sign.mandatory_path "fold_mapM"›
lemma bot:
shows "prog.fold_mapM ⊥ = (λxs. case xs of [] ⇒ prog.return [] | _ ⇒ ⊥)"
by (simp add: fun_eq_iff prog.bind.botL split: list.split)
lemma append:
shows "prog.fold_mapM f (xs @ ys)
= prog.fold_mapM f xs ⤜ (λxs. prog.fold_mapM f ys ⤜ (λys. prog.return (xs @ ys)))"
by (induct xs) (simp_all add: prog.bind.bind prog.bind.returnL prog.bind.returnR)
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "app"›
lemma bot:
shows "prog.app ⊥ = (λxs. case xs of [] ⇒ prog.return () | _ ⇒ ⊥)"
and "prog.app (λ_. ⊥) = (λxs. case xs of [] ⇒ prog.return () | _ ⇒ ⊥)"
by (simp_all add: fun_eq_iff prog.app_def prog.bind.botL split: list.split)
lemma Nil:
shows "prog.app f [] = prog.return ()"
by (simp add: prog.app_def)
lemma Cons:
shows "prog.app f (x # xs) = f x ⪢ prog.app f xs"
by (simp add: prog.app_def)
lemmas simps =
prog.app.bot
prog.app.Nil
prog.app.Cons
lemma append:
shows "prog.app f (xs @ ys) = prog.app f xs ⪢ prog.app f ys"
by (induct xs arbitrary: ys) (simp_all add: prog.app.simps prog.bind.returnL prog.bind.bind)
lemma monotone:
shows "mono (λf. prog.app f xs)"
by (induct xs) (simp_all add: prog.app.simps le_fun_def monotone_on_def prog.bind.mono)
lemmas strengthen[strg] = st_monotone[OF prog.app.monotone]
lemmas mono = monotoneD[OF prog.app.monotone]
lemmas mono2mono[cont_intro, partial_function_mono] = monotone2monotone[OF prog.app.monotone, simplified, of orda P for orda P]
lemma Sup_le:
shows "(⨆f∈X. prog.app f xs) ≤ prog.app (⨆X) xs"
by (simp add: SUP_le_iff SupI prog.app.mono)
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "invmap"›
lemma app:
fixes sf :: "'s ⇒ 't"
fixes vf :: "'v ⇒ unit"
shows "prog.invmap sf vf (prog.app f xs)
= prog.app (λx. prog.sinvmap sf (f x)) xs ⪢ prog.invmap sf vf (prog.return ())"
by (induct xs)
(simp_all add: prog.app.simps prog.bind.return prog.invmap.bind prog.bind.bind id_def)
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "sinvmap"›
lemma app_le:
fixes sf :: "'s ⇒ 't"
fixes vf :: "'v ⇒ unit"
shows "prog.app (λx. prog.sinvmap sf (f x)) xs ≤ prog.sinvmap sf (prog.app f xs)"
by (simp add: prog.invmap.app prog.invmap.return prog.bind.return
order.trans[OF _ prog.bind.mono[OF order.refl prog.return.rel_le]])
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "set_app"›
lemma bot:
shows "X ≠ {} ⟹ prog.set_app ⊥ X = ⊥"
and "X ≠ {} ⟹ prog.set_app (λ_. ⊥) X = ⊥"
by (simp_all add: prog.set_app_def prog.while.simps prog.bind.bind prog.bind.botL prog.bind.selectL)
lemma empty:
shows "prog.set_app f {} = prog.return ()"
by (simp add: prog.set_app_def prog.while.simps prog.bind.returnL)
lemma not_empty:
assumes "X ≠ {}"
shows "prog.set_app f X = prog.select X ⤜ (λx. f x ⪢ prog.set_app f (X - {x}))"
using assms by (simp add: prog.set_app_def prog.while.simps prog.bind.returnL prog.bind.bind)
lemmas simps =
prog.set_app.bot
prog.set_app.empty
prog.set_app.not_empty
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "app"›
lemma set_app_le:
assumes "X = set xs"
assumes "distinct xs"
shows "prog.app f xs ≤ prog.set_app f X"
using assms
proof(induct xs arbitrary: X)
case (Cons x xs) then show ?case
apply (simp add: prog.set_app.simps prog.app.simps)
apply (strengthen ord_to_strengthen(2)[OF prog.return.select_le[of x]], blast)
apply (simp add: prog.bind.returnL prog.bind.mono)
done
qed (simp add: prog.app.simps prog.set_app.simps)
setup ‹Sign.parent_path›
lemma set_app_alt_def:
assumes "finite X"
shows "prog.set_app f X = (⨆xs∈{ys. set ys = X ∧ distinct ys}. prog.app f xs)" (is "?lhs = ?rhs")
proof(rule antisym)
from assms show "?lhs ≤ ?rhs"
proof(induct rule: finite_remove_induct)
case (remove X)
from ‹finite X› ‹X ≠ {}› have *: "{ys. set ys = X - {x} ∧ distinct ys} ≠ {}" for x
by (simp add: finite_distinct_list)
from ‹X ≠ {}› show ?case
apply (clarsimp simp: prog.set_app.simps prog.bind.selectL)
apply (strengthen ord_to_strengthen[OF remove.hyps(4)], blast)
apply (fastforce simp: prog.app.simps prog.bind.SUPR_not_empty[OF *] Sup_le_iff
intro: rev_SUPI[where x="x # xs" for x xs])
done
qed (simp add: prog.set_app.simps prog.app.simps)
show "?rhs ≤ ?lhs"
by (simp add: Sup_le_iff prog.app.set_app_le)
qed
setup ‹Sign.parent_path›
setup ‹Sign.parent_path›
setup ‹Sign.mandatory_path "ag.prog"›
lemma select_sp:
assumes "⋀s x. ⟦P s; x ∈ X⟧ ⟹ Q x s"
assumes "⋀v. stable A (P ❙∧ Q v)"
shows "prog.p2s (prog.select X) ≤ ⦃P⦄, A ⊢ G, ⦃λv. P ❙∧ Q v⦄"
by (clarsimp simp: prog.select_def prog.p2s.Sup spec.interference.cl.bot ag.spec.term.none_inteference)
(rule ag.pre[OF ag.prog.return[OF assms(2)]]; blast intro: assms(1))
lemma while:
fixes c :: "'k ⇒ ('s, 'k + 'v) prog"
assumes c: "⋀k. prog.p2s (c k) ≤ ⦃P k⦄, A ⊢ G, ⦃case_sum I Q⦄"
assumes IP: "⋀s v. I v s ⟹ P v s"
assumes sQ: "⋀v. stable A (Q v)"
shows "prog.p2s (prog.while c k) ≤ ⦃I k⦄, A ⊢ G, ⦃Q⦄"
proof -
have "prog.p2s (prog.while c k) ≤ ⦃P k⦄, A ⊢ G, ⦃Q⦄"
proof(induct arbitrary: k rule: prog.while.fixp_induct[case_names adm bot step])
case (step k) show ?case
apply (rule ag.prog.bind[OF _ c])
apply (rule ag.pre_pre[OF ag.prog.case_sum[OF step ag.prog.return[OF sQ]]])
apply (simp add: IP split: sum.splits)
done
qed (simp_all add: ag.prog.galois)
then show ?thesis
by (meson IP ag.pre_pre)
qed
lemma app:
fixes xs :: "'a list"
fixes f :: "'a ⇒ ('s, unit) prog"
fixes P :: "'a list ⇒ 's pred"
assumes "⋀x ys zs. xs = ys @ x # zs ⟹ prog.p2s (f x) ≤ ⦃P ys⦄, A ⊢ G, ⦃λ_. P (ys @ [x])⦄"
assumes "⋀ys. prefix ys xs ⟹ stable A (P ys)"
shows "prog.p2s (prog.app f xs) ≤ ⦃P []⦄, A ⊢ G, ⦃λ_. P xs⦄"
using assms
by (induct xs rule: rev_induct;
fastforce intro: ag.prog.bind ag.prog.return
simp: prog.app.append prog.bind.returnR prog.app.simps)
lemma app_set:
fixes X :: "'a set"
fixes f :: "'a ⇒ ('s, unit) prog"
fixes P :: "'a set ⇒ 's pred"
assumes "⋀Y x. ⟦Y ⊆ X; x ∈ X - Y⟧ ⟹ prog.p2s (f x) ≤ ⦃P Y⦄, A ⊢ G, ⦃λ_. P (insert x Y)⦄"
assumes "⋀Y. Y ⊆ X ⟹ Stability.stable A (P Y)"
shows "prog.p2s (prog.set_app f X) ≤ ⦃P {}⦄, A ⊢ G, ⦃λ_. P X⦄"
proof -
have *: "X - (Y - {x}) = insert x (X - Y)" if "Y ⊆ X" and "x ∈ Y" for x and X Y :: "'a set"
using that by blast
show ?thesis
unfolding prog.set_app_def
apply (rule ag.prog.while[where I="λY s. Y ⊆ X ∧ P (X - Y) s" and Q="⟨P X⟩" and k="X", simplified])
apply (rename_tac k)
apply (rule ag.prog.if)
apply (rule ag.prog.return)
apply (simp add: assms; fail)
apply (rule_tac P="λs. k ⊆ X ∧ P (X - k) s" in ag.prog.bind[rotated])
apply (rule_tac Q="λx s. x ∈ k" in ag.prog.select_sp, assumption)
apply (simp add: assms(2) stable.conjI stable.const; fail)
apply (intro ag.gen_asm)
apply (rule ag.prog.bind[rotated])
apply (rule assms(1); force)
apply (rule ag.pre_pre[OF ag.prog.return])
apply (simp add: assms(2) stable.conjI stable.const; fail)
using * apply fastforce
apply force
apply (simp add: assms(2))
done
qed
lemma foldM:
fixes xs :: "'a list"
fixes f :: "'b ⇒ 'a ⇒ ('s, 'b) prog"
fixes I :: "'b ⇒ 'a ⇒ 's pred"
fixes P :: "'b ⇒ 's pred"
assumes f: "⋀b x. x ∈ set xs ⟹ prog.p2s (f b x) ≤ ⦃I b x⦄, A ⊢ G, ⦃P⦄"
assumes P: "⋀b x s. ⟦P b s; x ∈ set xs⟧ ⟹ I b x s"
assumes sP: "⋀b. stable A (P b)"
shows "prog.p2s (prog.foldM f b xs) ≤ ⦃P b⦄, A ⊢ G, ⦃P⦄"
using f P by (induct xs arbitrary: b) (fastforce intro!: ag.prog.bind intro: ag.pre_pre ag.prog.return[OF sP])+
setup ‹Sign.parent_path›
end