section‹Basic Monad Theory for Sequential Computations› theory
Monads imports
Main begin
subsection‹General Framework for Monad-based Sequence-Test› text‹
As such, Higher-order Logic as a purely functional specification formalism has no built-in
mechanism for state and state-transitions. Forms of testing involving state require therefore
explicit mechanisms for their treatment inside the logic; a well-known technique to model
states inside purely functional languages are \emph{monads} made popular by Wadler and Moggi
and extensively used in Haskell. \HOL is powerful enough to represent the most important
standard monads; however, it is not possible to represent monads as such due to well-known
limitations of the Hindley-Milner type-system.
Here is a variant for state-exception monads, that models precisely transition functions with
preconditions. Next, we declare the state-backtrack-monad. In all of them, our concept of
i/o-stepping functions can be formulated; these are functions mapping input to a given monad.
Later on, we will build the usual concepts of: \begin{enumerate} \item deterministic i/o automata, \item non-deterministic i/o automata, and \item labelled transition systems (LTS) \end{enumerate} ›
definition bind_SE :: "('o,'σ)MONSE==> ('o ==> ('o','σ)MONSE) ==> ('o','σ)MONSE" where"bind_SE f g = (λσ. case f σ of None ==> None | Some (out, σ') ==> g out σ')"
definition assert_SE :: "('σ ==> bool) ==> (bool, 'σ)MONSE" where"assert_SE P = (λσ. if P σ then Some(True,σ) else None)" notation assert_SE (‹assertSE›)
definition assume_SE :: "('σ ==> bool) ==> (unit, 'σ)MONSE" where"assume_SE P = (λσ. if ∃σ . P σ then Some((), SOME σ . P σ) else None)" notation assume_SE (‹assumeSE›)
definition if_SE :: "['σ ==> bool, ('α, 'σ)MONSE, ('α, 'σ)MONSE] ==> ('α, 'σ)MONSE" where"if_SE c E F = (λσ. if c σ then E σ else F σ)" notation if_SE (‹ifSE›)
text‹
The standard monad theorems about unit and associativity: ›
text‹
In order to express test-sequences also on the object-level and to make our theory amenable to
formal reasoning over test-sequences, we represent them as lists of input and generalize the
bind-operator of the state-exception monad accordingly. The approach is straightforward, but
comes with a price: we have to encapsulate all input and output data into one type. Assume that
we have a typed interface to a module with the operations $op_1$, $op_2$, \ldots, $op_n$ with
the inputs $\iota_1$, $\iota_2$, \ldots, $\iota_n$ (outputs are treated analogously). Then we
can encode for this interface the general input - type: \begin{displaymath} \texttt{datatype}\ \texttt{in}\ =\ op_1\ ::\ \iota_1\ |\ ...\ |\ \iota_n \end{displaymath}
Obviously, we loose some type-safety in this approach; we have to express that in traces only \emph{corresponding} input and output belonging to the same operation will occur; this form
of side-conditions have to be expressed inside \HOL. From the user perspective, this will not
make much difference, since junk-data resulting from too weak typing can be ruled out by adopted
front-ends. ›
text‹
In order to express test-sequences also on the object-level and to make our theory amenable to
formal reasoning over test-sequences, we represent them as lists of input and generalize the
bind-operator of the state-exception monad accordingly. Thus, the notion of test-sequence
is mapped to the notion of a \emph{computation}, a semantic notion; at times we will use
reifications of computations, \ie{} a data-type in order to make computation amenable to
case-splitting and meta-theoretic reasoning. To this end, we have to encapsulate all input
and output data into one type. Assume that we have a typed interface to a module with
the operations $op_1$, $op_2$, \ldots, $op_n$ with the inputs $\iota_1$, $\iota_2$, \ldots,
$\iota_n$ (outputs are treated analogously).
Then we can encode for this interface the general input - type: \begin{displaymath} \texttt{datatype}\ \texttt{in}\ =\ op_1\ ::\ \iota_1\ |\ ...\ |\ \iota_n \end{displaymath}
Obviously, we loose some type-safety in this approach; we have to express
that in traces only \emph{corresponding} input and output belonging to the
same operation will occur; this form of side-conditions have to be expressed
inside \HOL. From the user perspective, this will not make much difference,
since junk-data resulting from too weak typing can be ruled out by adopted
front-ends.›
text‹Note that the subsequent notion of a test-sequence allows the io stepping
(and the special case of a program under test) to stop execution
emph{within} the sequence; such premature terminations are characterized by an
list which is shorter than the input list. Note that our primary
of multiple execution ignores failure and reports failure
only by missing results ...›
fun mbind :: "'ι list ==> ('ι ==> ('o,'σ) MONSE) ==> ('o list,'σ) MONSE" where"mbind [] iostep σ = Some([], σ)" | "mbind (a#H) iostep σ = (case iostep a σ of None ==> Some([], σ) | Some (out, σ') ==> (case mbind H iostep σ' of None ==> Some([out],σ') | Some(outs,σ'') ==> Some(out#outs,σ'')))"
text‹As mentioned, this definition is fail-safe; in case of an exception,
current state is maintained, no result is reported.
alternative is the fail-strict variant ‹mbind'› defined below.›
lemma mbind_nofailure [simp]: "mbind S f σ ≠ None" apply (rule_tac x=σ in spec) apply (induct S) using mbind.simps(1) apply force apply(simp add:unit_SE_def) apply(safe)[1]
subgoal for a S x apply (case_tac "f a x") apply(simp) apply(safe)[1]
subgoal for aa b apply (erule_tac x="b"in allE) apply (erule exE)+ apply (simp) done done done
text‹The fail-strict version of ‹mbind'› looks as follows:› fun mbind' :: "'ι list ==> ('ι ==> ('o,'σ) MONSE) ==> ('o list,'σ) MONSE" where"mbind' [] iostep σ = Some([], σ)" | "mbind' (a#H) iostep σ = (case iostep a σ of None ==> None | Some (out, σ') ==> (case mbind H iostep σ' of None ==> None ― ‹fail-strict› | Some(outs,σ'') ==> Some(out#outs,σ'')))"
text‹
mbind' as failure strict operator can be seen as a foldr on bind---if the types would
match \ldots ›
definition try_SE :: "('o,'σ) MONSE==> ('o option,'σ) MONSE" where"try_SE ioprog = (λσ. case ioprog σ of None ==> Some(None, σ) | Some(outs, σ') ==> Some(Some outs, σ'))" text‹In contrast @{term mbind} as a failure safe operator can roughly be seen
as a @{term foldr} on bind - try: ‹m1 ; try m2 ; try m3; ...›. Note, that the rough equivalence only holds for
certain predicates in the sequence - length equivalence modulo None,
for example. However, if a conditional is added, the equivalence
can be made precise:›
lemma mbind_try: "(x ← mbind (a#S) F; M x) = (a' ← try_SE(F a); if a' = None then (M []) else (x ← mbind S F; M (the a' # x)))" apply (rule ext) apply (simp add: bind_SE_def try_SE_def)
subgoal for x apply (case_tac "F a x") apply(simp) apply (safe)[1] apply (simp add: bind_SE_def try_SE_def)
subgoal for aa b apply (case_tac "mbind S F b") apply (auto) done done done
text‹On this basis, a symbolic evaluation scheme can be established
that reduces @{term mbind}-code to @{term try_SE}-code and If-cascades.›
definition alt_SE :: "[('o, 'σ)MONSE, ('o, 'σ)MONSE] ==> ('o, 'σ)MONSE" (infixl‹⊓SE›10) where"(f ⊓SE g) = (λ σ. case f σ of None ==> g σ | Some H ==> Some H)"
definition malt_SE :: "('o, 'σ)MONSE list ==> ('o, 'σ)MONSE" where"malt_SE S = foldr alt_SE S failSE" notation malt_SE (‹⊓SE›)
lemma malt_SE_cons [simp]: "⊓SE (a # S) = (a ⊓SE (⊓SE S))" by(simp add: malt_SE_def)
subsubsection‹State-Backtrack Monads› text‹This subsection is still rudimentary and as such an interesting
formal analogue to the previous monad definitions. It is doubtful that it is
interesting for testing and as a computational structure at all.
Clearly more relevant is ``sequence'' instead of ``set,'' which would
rephrase Isabelle's internal tactic concept. ›
subsubsection‹State Backtrack Exception Monad› text‹
The following combination of the previous two Monad-Constructions allows for the semantic
foundation of a simple generic assertion language in the style of Schirmer's Simpl-Language or
Rustan Leino's Boogie-PL language. The key is to use the exceptional element None for violations
of the assert-statement. › type_synonym ('o, 'σ) MONSBE = "'σ ==> (('o × 'σ) set) option"
definition bind_SBE :: "('o,'σ)MONSBE==> ('o ==> ('o','σ)MONSBE) ==> ('o','σ)MONSBE" where"bind_SBE f g = (λσ. case f σ of None ==> None | Some S ==> (let S' = (λ(out, σ'). g out σ') ` S in if None ∈ S' then None else Some(∪ (the ` S'))))"
definition assert_SBE :: "('σ ==> bool) ==> (unit, 'σ)MONSBE" where"assert_SBE e = (λσ. if e σ then Some({((),σ)}) else None)" notation assert_SBE (‹assertSBE›)
definition assume_SBE :: "('σ ==> bool) ==> (unit, 'σ)MONSBE" where"assume_SBE e = (λσ. if e σ then Some({((),σ)}) else Some {})" notation assume_SBE (‹assumeSBE›)
lemmas aux = trans[OF HOL.neq_commute,OF Option.not_None_eq]
lemma bind_assoc_SBE: "(y :≡ (x :≡ m; k); h) = (x :≡ m; (y :≡ k; h))" proof (rule ext, simp add: unit_SBE_def bind_SBE_def, rename_tac x,
case_tac "m x", simp_all add: Let_def Set.image_iff, safe,goal_cases) case (1 x a aa b ab ba a b) thenshow ?caseby(rule_tac x="(a, b)"in bexI, simp_all) next case (2 x a aa b ab ba) thenshow ?case apply (rule_tac x="(aa, b)"in bexI, simp_all add:split_def) apply (erule_tac x="(aa,b)"in ballE) apply (auto simp: aux image_def split_def intro!: rev_bexI) done next case (3 x a a b) thenshow ?caseby(rule_tac x="(a, b)"in bexI, simp_all) next case (4 x a aa b) thenshow ?case apply (erule_tac Q="None = X"for X in contrapos_pp) apply (erule_tac x="(aa,b)"and P="λ x. None ≠ case_prod (λout. k) x"in ballE) apply (auto simp: aux image_def split_def intro!: rev_bexI) done next case (5 x a aa b ab ba a b) thenshow ?caseapply simp apply ((erule_tac x="(ab,ba)"in ballE)+) apply (simp_all add: aux, (erule exE)+, simp add:split_def) apply (erule rev_bexI, case_tac "None∈(λp. h(snd p))`y",auto simp:split_def) done
next case (6 x a aa b a b) thenshow ?caseapply simp apply ((erule_tac x="(a,b)"in ballE)+) apply (simp_all add: aux, (erule exE)+, simp add:split_def) apply (erule rev_bexI, case_tac "None∈(λp. h(snd p))`y",auto simp:split_def) done qed
subsection‹Valid Test Sequences in the State Exception Monad› text‹
This is still an unstructured merge of executable monad concepts and specification oriented
high-level properties initiating test procedures. ›
definition valid_SE :: "'σ ==> (bool,'σ) MONSE==> bool" (infix‹⊨›15) where"(σ ⊨ m) = (m σ ≠ None ∧ fst(the (m σ)))" text‹
This notation consideres failures as valid---a definition inspired by I/O conformance.
Note that it is not possible to define this concept once and for all in a Hindley-Milner
type-system. For the moment, we present it only for the state-exception monad, although for
the same definition, this notion is applicable to other monads as well. ›
text‹
These two rule prove that the SE Monad in connection with the notion of valid sequence is
actually sufficient for a representation of a Boogie-like language. The SBE monad with explicit
sets of states---to be shown below---is strictly speaking not necessary (and will therefore
be discontinued in the development). ›
subsection‹Valid Test Sequences in the State Exception Backtrack Monad› text‹
This is still an unstructured merge of executable monad concepts and specification oriented
high-level properties initiating test procedures. ›
definition valid_SBE :: "'σ ==> ('a,'σ) MONSBE==> bool" (infix‹⊨SBE›15) where"σ ⊨SBE m ≡ (m σ ≠ None)" text‹
This notation considers all non-failures as valid. ›
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.