Theory General_Remarks
chapter ‹General Remarks›
theory %invisible General_Remarks
imports Main
begin
text ‹\label{chap:general}›
text ‹The following remarks apply to pop-refinement in general,
beyond the examples in \chapref{chap:exampleI} and \chapref{chap:exampleII}.›
section ‹Program-Level Requirements›
text ‹\label{sec:progreq}›
text ‹By predicating directly over programs,
a pop-refinement specification
(like @{term spec⇩0}
in \secref{sec:specificationI} and \secref{sec:specificationII})
can express program-level requirements
that are defined in terms of the vocabulary of the target language,
e.g.\
constraints on memory footprint
(important for embedded software),
restrictions on calls to system libraries to avoid or limit information leaks
(important for security),
conformance to coding standards
(important for certain certifications),
and use or provision of interfaces
(important for integration with existing code).
Simple examples are
@{term "wfp p"} in \secref{sec:specificationI} and \secref{sec:specificationII},
@{term "para p = [''x'', ''y'']"} in \secref{sec:specificationI},
and @{term "iovars p"} in \secref{sec:specificationII}.›
section ‹Non-Functional Requirements›
text ‹\label{sec:nonfuncreq}›
text ‹Besides functional requirements,
a pop-refinement specification can express non-functional requirements,
e.g.\ constraints on
computational complexity,
timing,
power consumption,
etc.%
\footnote{In order to express these requirements,
the formalized semantics of the target language
must suitably include non-functional aspects,
as in the simple model in \secref{sec:nonfunc}.}
A simple example is
@{term "costp p ≤ (3::nat)"} in \secref{sec:specificationI}.›
section ‹Links with High-Level Requirements›
text ‹\label{sec:hilevreq}›
text ‹A pop-refinement specification can explicate links
between high-level requirements and target programs.›
text ‹For example,
@{term "∀x y. exec p [x, y] = Some (f x y)"}
in @{term spec⇩0} in \secref{sec:specificationI}
links the high-level functional requirement expressed by @{term f}
to the target program @{term p}.%
\footnote{Similarly, the functional requirements in \secref{sec:specificationII}
could be expressed abstractly
in terms of mappings between low and high inputs and outputs
(without reference to program variables and executions)
and linked to program variables and executions.
But \secref{sec:specificationII} expresses such functional requirements
directly in terms of programs
to keep the example (whose focus is on hyperproperties) simpler.}›
text ‹As another example,
a function ‹sort :: nat list ⇒ nat list›,
defined to map each list of natural numbers to its sorted permutation,
expresses a high-level functional requirement
that can be realized in different ways.
An option is a procedure that destructively sorts an array in place.
Another option is a procedure that returns a newly created sorted linked list
from a linked list passed as argument and left unmodified.
A pop-refinement specification can pin down the choice,
which matters to external code that uses the procedure.›
text ‹As a third example,
a high-level model of a video game or physical simulator
could use real numbers and differential equations.
A pop-refinement specification could
state required bounds on how
the idealized model is approximated by an implementation
that uses floating point numbers and difference equations.›
text ‹Different pop-refinement specifications
could use the same high-level requirements
to constrain programs in different target languages or in different ways,
as in the @{term sort} example above.
As another example,
the high-level behavior of an operating system
could be described by a state transition system
that abstractly models internal states and system calls;
the same state transition system could be used
in a pop-refinement specification of a Haskell simulator that runs on a desktop,
as well as in a pop-refinement specification of a C/Assembly implementation
that runs on a specific hardware platform.›
section ‹Non-Determinism and Under-Specification›
text ‹\label{sec:nondet}›
text ‹The interaction of refinement
with non-determinism and under-specification
is delicate in general.
The one-to-many associations of a relational specification
(e.g.\ a state transition system
where the next-state relation may associate
multiple new states to each old state)
could be interpreted as
non-determinism
(i.e.\ different outcomes at different times, from the same state)
or under-specification
(i.e.\ any outcome is allowed, deterministically or non-deterministically).
Hyperproperties like GNI
are consistent with the interpretation as non-determinism,
because security depends on the ability to yield different outcomes,
e.g.\ generating a nonce in a cryptographic protocol.
The popular notion of refinement as inclusion of sets of traces
(e.g.~\<^cite>‹"AbadiLamportRefinement"›)
is consistent with the interpretation as under-specification,
because a refined specification is allowed to reduce the possible outcomes.
Thus, hyperproperties are not always preserved by
refinement as trace set inclusion~\<^cite>‹"ClarksonSchneiderHyperproperties"›.›
text ‹As exemplified in \secref{sec:specificationII},
a pop-refinement specification can explicitly distinguish
non-determinism and under-specification.
Each pop-refinement step preserves all the hyperproperties
expressed or implied by the requirement specification.%
\footnote{Besides security hyperproperties expressed in terms of non-determinism,
pop-refinement can handle more explicit security randomness properties.
The formalized semantics of a target language could
manipulate probability distributions over values (instead of just values),
with random number generation libraries
that return known distributions (e.g.\ uniform),
and with language operators that transform distributions.
A pop-refinement specification could include
randomness requirements on program outcomes expressed in terms of distributions,
and each pop-refinement step would preserve such requirements.}›
section ‹Specialized Formalisms›
text ‹\label{sec:specform}›
text ‹Specialized formalisms (e.g.\ state machines, temporal logic),
shallowly or deeply embedded into the logic of the theorem prover
(e.g.~\<^cite>‹"AFP-Statecharts" and "AFP_TLA"›),
can be used to express some of the requirements
of a pop-refinement specification.
The logic of the theorem prover provides semantic integration
of different specialized formalisms.›
section ‹Strict and Non-Strict Refinement Steps›
text ‹\label{sec:strictref}›
text ‹In a pop-refinement step from @{term spec⇩i} to ‹spec⇩i⇩+⇩1›,
the two predicates may be equivalent, i.e.\ ‹spec⇩i⇩+⇩1 = spec⇩i›.
But the formulation of ‹spec⇩i⇩+⇩1› should be ``closer''
to the implementation than the formulation of @{term spec⇩i}.
An example is in \secref{sec:refI:stepI}.›
text ‹When the implication ‹spec⇩i⇩+⇩1 p ⟹ spec⇩i p› is strict,
potential implementations are eliminated.
Since the final predicate of a pop-refinement derivation
must characterize a unique program,
some refinement steps must be strict---%
unless the initial predicate @{term spec⇩0}
is satisfiable by a unique program,
which is unlikely.›
text ‹A strict refinement step may lead to a blind alley
where ‹spec⇩i⇩+⇩1 = λp. False›,
which cannot lead to a final predicate that characterizes a unique program.
An example is discussed in \secref{sec:refI:stepIV}.›
section ‹Final Predicate›
text ‹\label{sec:lastpred}›
text ‹The predicate that concludes a pop-refinement derivation
must have the form @{term "spec⇩n p ≡ p = p⇩0"},
where @{term p⇩0} is the representation of a program's abstract syntax
in the theorem prover,
as in \secref{sec:refI:stepVII} and \secref{sec:refII:stepVI}.
This form guarantees that
the predicate characterizes exactly one program
and that the program is explicitly determined.
@{term p⇩0} witnesses the consistency of the requirements,
i.e.\ the fact that @{term spec⇩0} is not always false;
inconsistent requirements cannot lead to a predicate of this form.›
text ‹A predicate of the form @{term "spec⇩i ≡ p = p⇩0 ∧ Φ p"}
may not characterize a unique program:
if @{term "Φ p⇩0"} is false, @{term spec⇩i} is always false.
To conclude the derivation, @{term "Φ p⇩0"} must be proved.
But it may be easier to prove the constraints expressed by @{term Φ}
as @{term p⇩0} is constructed in the derivation.
For example,
deriving a program from @{term spec⇩0} in \secref{sec:specificationI}
based on the functional constraint and ignoring the cost constraint
would lead to a predicate @{term "spec⇩i ≡ p = p⇩0 ∧ costp p ≤ (3::nat)"},
where @{term "costp p⇩0 ≤ (3::nat)"} must be proved to conclude the derivation;
instead, the derivation in \secref{sec:refinementI}
proves the cost constraint as @{term p⇩0} is constructed.
Taking all constraints into account at each stage of the derivation can help
choose the next refinement step
and reduce the chance of blind alleys
(cf.\ \secref{sec:refI:stepIV}).›
text ‹The final predicate @{term spec⇩n} expresses
a purely syntactic requirement,
while the initial predicate @{term spec⇩0} usually includes
semantic requirements.
A pop-refinement derivation progressively turns
semantic requirements into syntactic requirements.
This may involve
rephrasing functional requirements
to use only operations supported by the target language
(e.g.\ lemma ‹f_rephrased› in \secref{sec:refI:stepIV}),
obtaining shallowly embedded program fragments,
and turning them into their deeply embedded counterparts
(e.g.\ \secref{sec:refI:stepV} and \secref{sec:refI:stepVI}).%
\footnote{In \secref{sec:refinementII},
program fragments are introduced directly,
without going through shallow embeddings.}›
section ‹Proof Coverage›
text ‹\label {sec:allproved}›
text ‹In a chain of predicate inclusions
as in \secref{sec:refinementI} and \secref{sec:refinementII},
the proofs checked by the theorem prover encompass the range
from the specified requirements to the implementation code.
No separate code generator is needed
to turn low-level specifications into code:
pop-refinement folds code generation into the refinement sequence,
providing fine-grained control on the implementation code.›
text ‹A purely syntactic pretty-printer is needed
to turn program abstract syntax,
as in \secref{sec:refI:stepVII} and \secref{sec:refII:stepVI},
to concrete syntax.
This pretty-printer can be eliminated
by formalizing in the theorem prover
the concrete syntax of the target language
and its relation to the abstract syntax,
and by defining the @{term spec⇩i} predicates over program concrete syntax---%
thus, folding pretty-printing into the refinement sequence.›
section ‹Generality and Flexibility›
text ‹\label{sec:genflex}›
text ‹Inclusion of predicates over programs
is a general and flexible notion of refinement.
More specialized notions of refinement
(e.g.~\<^cite>‹"HoareData" and "MilnerSimulation"›)
can be used for any auxiliary types, functions, etc.\
out of which the @{term spec⇩i} predicates may be constructed,
as long as the top-level implication
‹spec⇩i⇩+⇩1 p ⟹ spec⇩i p› holds at every step.›
end %invisible