SoP1: Assignment
This post discusses the syntax and semantics of a simple imperative language with simultaneous assignment.
module SoP1 where
open import Data.Bool using(if_then_else_ ; true ; false) renaming (Bool to πΉ )
open import MyPrelude hiding (_β¨Ύ_ ; swap) renaming (_Γ_ to _AND_)
The last import just brings into context the usual notions of propositional equality, sums, products, function operations, and a small theory of natural number arithmetic.
DataTypes
We want to describe sets of states of program variables and to write assertions about them. For simplicity, in our very elementary language (for now!), we only consider π©
atural numbers and π«
ropositions.
data Type : Set where
π© : Type
π« : Type
Which are then interpreted as elements of type Setβ
:
-- subscript t, for "t"ypes
β¦_β§β : Type β Setβ
β¦ π© β§β = Lift β
β¦ π« β§β = Set
Notice that we interpret propositions constructively, since weβre in Agda after-all. Had we interpreted them as Booleans, reasoning would be a bit tricky since Booleans are not proof-carrying data. Alternatively: a proposition describes the set of states in which it is true and in Agda these notions coincide via Curry-Howard.
The Lift
data type behaves, roughly, as follows: β (i j : Level) β i β€ j β Set i β Set j
. That is, it lets us embed a small type into a larger type. The βequivalenceβ A β
Lift A
is witnessed by the constructor lift
and inversely by the eliminator lower
.
Finally, without any surprise, we can decide when the types coincide or not:
_ββ_ : Decidable {A = Type} _β‘_
π© ββ π© = yes β‘-refl
π© ββ π« = no (Ξ» ())
π« ββ π© = no (Ξ» ())
π« ββ π« = yes β‘-refl
It feels like we ought to be able to mechanically derive equality as is done in Haskell; and indeed there is discussion on how to achieve this.
Variables
To make assertions of program variables, we need to have variables in the first place.
We use an explicitly dedicated datatype for formal variables.
data Variable : Type β Set where
x y z : β {t} β Variable t
_βα΅₯_ : β {t s} β Variable t β Variable s β πΉ
x βα΅₯ x = true
x βα΅₯ _ = false
y βα΅₯ y = true
y βα΅₯ _ = false
z βα΅₯ z = true
z βα΅₯ _ = false
A more generic approach would be to parameterise the module by a type for variables that is endowed with decidable equality βa setoid.
Note that we cannot consider using de Bruijn indices, as in Agda by Example: Ξ»-calculus, since our variables are not bound to any quantifier. βperhaps I will switch to begin-end blocks introducing/quantifying over variables.
Expressions
With types and variables in-hand, we can discuss how to form terms and how to assign meaning to them.
Syntax
A term is defined to be a constant symbol c
, a variable v
, or a function/relation symbol f
applied to terms:
term β·= constant c β£ variable v β£ application f(tβ, β¦, tβ)
For now, we take our function and relation symbols to be addition, ordering, equality, and conjunction.
data Expr : Type β Setβ where
-- varaibles
Var : β {t} (v : Variable t) β Expr t
-- constant constructors
π© : β β Expr π©
π« : Set β Expr π«
-- function/relation symbol application
_+_ : (m n : Expr π©) β Expr π©
_β_ _β€_ : (e f : Expr π©) β Expr π«
_β§_ : (p q : Expr π«) β Expr π«
infix 11 _+_
infix 10 _β_ _β€_
infix 8 _β§_
Notice that we make a natural constant by using π©
and the type of the resulting expression is of type π©
, with this overloading, we can loosely say π©
is the βconstructorβ for the βtypeβ π©
. Likewise for π«
.
Another candidate for constant constructor is,
π : β {t} β β¦ t β§β β Expr t
syntax π {t} c = c βΆ t -- ghost colon
However, I prefer the pair π©, π«
as they clearly denote the type of variable Iβm working with; moreover, I cannot make them synonyms for _ βΆ π© , _ βΆ π«
since π© , π«
are already defined and Agda only permits constructor overloading.
Also, I thought about using π±
instead of Var
but the latter seems clearer and the former clashes with the aforementioned convention of the curly-letters denoting the type of the associated constant.
Semantics
An expression like Var x + π© 5
can appear in a program; when encountered, it is βevaluatedβ in the current machine βstateβ to produce another natural number. What is this state? It is an association of identifiers to values.
State : Setβ
State = β {t : Type} β Variable t β β¦ t β§β
It is common to denote the interpretation of an expression e
in state Ο
by β¦ e β§ Ο
where the βsemantics-bracketsβ are defined by
β¦ c β§ Ο = c ; β¦ v β§ Ο = Ο v ; β¦ f(tβ, β¦, tβ) β§ Ο = β¦ f β§( β¦ tβ β§ Ο , β¦, β¦ tβ β§ Ο)
Formally,
-- subscript e, for "e"xpressions
β¦_β§β : β {t} β Expr t β State β β¦ t β§β
β¦_β§β (Var v) Ο = Ο v
β¦_β§β (π© q) Ο = lift q
β¦_β§β (π« p) Ο = p
β¦_β§β (e + eβ) Ο = lift $ lower (β¦ e β§β Ο) +β lower (β¦ eβ β§β Ο)
β¦_β§β (e β eβ) Ο = lift (lower (β¦ e β§β Ο)) β‘ lift (lower (β¦ eβ β§β Ο)) -- propositional equality
β¦_β§β (e β€ eβ) Ο = lower (β¦ e β§β Ο) β€β lower (β¦ eβ β§β Ο)
β¦_β§β (e β§ eβ) Ο = β¦ e β§β Ο AND β¦ eβ β§β Ο -- Cartesian product
The addition case works as follows: β¦ e + eβ β§ Ο
is obtained by recursively obtaining the semantics of the arguments, then using lower
on the results to obtain the underlying naturals (of the lifted number type), then adding them, then packing up the whole thing into the lifted number type again. Similarly for the other cases.
Sometimes we want to update the state for a variable, say when it is reassigned, and we accomplish this by βfunction patchingβ:
f [z β¦ y] = Ξ» x β If x β z Then y Else f x
Formally,
_[_β¦_] : State β β {t} β Variable t β β¦ t β§β β State
_[_β¦_] Ο {t} v e {s} vβΆs with t ββ s
_[_β¦_] Ο v e vβΆs β£ yes β‘-refl = if v βα΅₯ vβΆs then e else Ο vβΆs
_[_β¦_] Ο v e vβΆs β£ no Β¬p = Ο vβΆs
Reasoning about Boolean expressions
Say proposition Q
is weaker than P
if P β Q
; alternatively, P
is stronger than Q
. βA stronger proposition makes more restrictions on the combinations of values its identifiers can be associated with, a weaker proposition makes fewer.β That is, Q
is βless restrictiveβ than P
.
infix 2 _β_
_β_ : Expr π« β Expr π« β Setβ
P β Q = β (Ο : State) β β¦ P β§β Ο β β¦ Q β§β Ο
The weakest proposition is true
, or any tautology, because it represents the set of all states βie it is always inhabited.
True : Expr π«
True = π« β€
everything-implies-truth : β {R} β R β True
everything-implies-truth = Ξ» _ _ β tt
The strongest proposition is false
, because it represents the set of no states βie it is never inhabited.
False : Expr π«
False = π« β₯
false-is-strongest : β {Q} β False β Q
false-is-strongest Ο ()
Before we move on, let us remark that _β_
admits familiar properties, such as
β-refl : β {P} β P β P
β-refl Ο pf = pf
-- C-c C-a
β-trans : β {P Q R} β P β Q β Q β R β P β R
β-trans = Ξ» {P} {Q} {R} zβ zβ st zβ β zβ st (zβ st zβ)
β§-sym : β {P Q} β P β§ Q β Q β§ P
β§-sym st (p , q) = q , p
Using β-refl
without mentioning the implicit parameter yields yellow since Agda for some reason cannot infer them! However, if we comment out the definition of β¦_β§β
and postulate it instead, then no yellow :/ Send help!
Textual Substitution
Any expression E
can be considered as a function E(vβ, β¦, vβ)
of itβs free-variables, say vβ, β¦, vβ
, then we can form a new expression E(eβ, β¦, eβ)
for any expressions eα΅’
. However, this is cumbersome since the order of variables must be specified and also troublesome when I only want to substitute a few of the free variables. This is remedied by naming the variables to be replaced (irrespective of whether they are even in the given expression) and indicating its replacement as well βa sort of βnamed argumentsβ approach.
This βtextual substitutionβ of variables v
by expressions e
is defined, informally, by three rules:
c [ e / v ] = e
u [ e / v ] = IF variable u is one of the vα΅’ THEN eα΅’ ELSE u
f(tβ, β¦, tβ) [ e / v ] = f (tβ [ e / v ], β¦, tβ [ e / v ])
The new term to be introduced occurs above the fraction symbol, expression [ new_term / old_variable]
, and we read E [t / x]
as βE with t replacing xβ. As such, some would also write E [x β² t]
; other notations include E[x β t]
and EΛ£β
.
Warning! Simultaneous is not iteration! In general, E [ u , v / x , y] β (E [ u / x])[ v / y]
. For example,
(x + y)[ x + y , z / x , y] = (x + y) + z β (x + z) + z = (x + y)[x + y / x][z / y]
Anyhow, we implement the case n = 1
and leave the case n = 2
as an exercise for the reader. At the moment, I do not foresee the need for the general case; nor do I see a way to make it (quickly) syntactically appealing in Agda!
_[_/_] : β {s t} (E : Expr t) (e : Expr s) (v : Variable s) β Expr t
_[_/_] {s} {t} (Var v) u e with s ββ t
Var v [ e / u ] β£ yes β‘-refl = if v βα΅₯ u then e else Var v
Var v [ e / u ] β£ no Β¬p = Var v
π© n [ e / v ] = π© n
π« p [ e / v ] = π« p
(E + F) [ e / v ] = E [ e / v ] + F [ e / v ]
(E β F) [ e / v ] = E [ e / v ] β F [ e / v ]
(E β€ F) [ e / v ] = (E [ e / v ]) β€ (F [ e / v ])
(E β§ F) [ e / v ] = (E [ e / v ]) β§ (F [ e / v ])
Observe,
For the variable case, we substitute precisely when the types match; otherwise we cannot even define it.
If the types do match, then we pattern-match on the proof to obtain
β‘-refl
so that Agda unifies them, thereby making the given conditional well-typed.If the types do not match, irrespective of whether the variables are identical or not, the only thing we can do, while meeting the type requirements, is to leave them alone. This subtlety leads to a bit of poor items such as
Var {π©} x [ e / x (of type π«) ] = Var {π©} x
, or formally:subtle : β {e : Expr π«} β (Var {π©} x)[ e / x ] β‘ Var {π©} x subtle = β‘-refl
Note that because of the substitution, the second instance of
x
has to have the same type ase
, which isπ«
. Since the two occurrences ofx
on the left have different types, the substitution does nothing.The last few cases are identical; we intend to remedy this identical coding in the next post by using βinterpreted function symbolsβ.
The following is an exercise for the reader, where we want them to implement the function so that E [e, d / v,v] = E [d / v]
.
_[_,_/β_,_] : β {sβ sβ t} (E : Expr t) (eβ : Expr sβ) (eβ : Expr sβ) (vβ : Variable sβ) (vβ : Variable sβ) β Expr t
E [ eβ , eβ /β vβ , vβ ] = {! exercise !}
To close we give some lemmas dealing with textual substitution. Their proofs would be by induction on expressions and using the definitions of substitution; all-in-all quite a bore and so not proven here. From Chapter 4 of SoP:
Ο [ v β¦ Ο v ] = Ο
: if we update at positionv
by doing the same thing we currently do there, then thatβs the same thing as no update at all.postulate patching-id : β {t} {Ο : State} {v : Variable t} β β {u : Variable t} β (Ο [ v β¦ Ο v ]) u β‘ Ο u
Substituting an expression
e
forv
inE
and then evaluating in Ο yields the same result as substituting the value ofe
in Ο forv
and the evaluating.
-- constant helper
π : β {t} β β¦ t β§β β Expr t
π {π©} c = π© (lower c)
π {π«} c = π« c
postulate eval-over-sub : β {t s} {v : Variable t} {Ο : State} {e : Expr t} {E : Expr s}
β β¦ E [ e / v ] β§β Ο β‘ β¦ E [ π (β¦ e β§β Ο) / v ] β§β Ο
- Evaluating
E[x / e]
in state Ο is the same as evaluatingE
in Ο withx
updated to evaluation ofe
.
postulate sub-to-state : β {t} {v : Variable t} {e : Expr t} {s} {E : Expr s} {Ο : State}
β β¦ E [ e / v ] β§β Ο β‘ β¦ E β§β (Ο [ v β¦ β¦ e β§β Ο ])
That is, evaluating a substituted expression is the same as evaluating the original expression in an updated state. Notice that this law allows us to make substitutions part of the syntax and the law is then how to evaluate such syntax! Is that a better approach?!
postulate iterated-substitution : β {t} {v : Variable t} {e d : Expr t} {s} {E : Expr s}
β (E [ e / v ]) [ d / v ] β‘ E [ e [ d / v ] / v ]
Substitution of a non-occurring variable:
(E[x / u])[y / v] = E [x / u[y / v] ]
, ify
is not free inE
. (SoP, 4.4.8)Provided
x = (xβ, β¦, xβ)
are distinct identifiers andu = (uβ, β¦, uβ)
are fresh, distinct identifiers, we haveE[x / u][u / x] = E
.
For the curious, here is a very uninsightful proof of one of the above lemmas:
patching-id-pf : β {t} {Ο : State} (v : Variable t) β β (u : Variable t) β (Ο [ v β¦ Ο v ]) u β‘ Ο u
patching-id-pf {t} {Ο} v u with t ββ t
patching-id-pf v u β£ no Β¬p = β‘-refl
patching-id-pf x x β£ yes β‘-refl = β‘-refl
patching-id-pf x y β£ yes β‘-refl = β‘-refl
patching-id-pf x z β£ yes β‘-refl = β‘-refl
patching-id-pf y x β£ yes β‘-refl = β‘-refl
patching-id-pf y y β£ yes β‘-refl = β‘-refl
patching-id-pf y z β£ yes β‘-refl = β‘-refl
patching-id-pf z x β£ yes β‘-refl = β‘-refl
patching-id-pf z y β£ yes β‘-refl = β‘-refl
patching-id-pf z z β£ yes β‘-refl = β‘-refl
Admittedly, it could have been simplified had we implemented equality as a Dec
idable rather than just a Boolean.
To be fair, hereβs a another proof of another result: substitution is monotonic. Informally,
β¦ P [ v / e ] β§β Ο
β‘
β¦ P β§β (Ο [ v β¦ β¦ e β§β Ο ]) -- since sub-to-state
β
β¦ Q β§β (Ο [ v β¦ β¦ e β§β Ο ]) -- since P β Q
β‘
β¦ Q [ v / e ] β§β Ο -- since sub-to-state
Rather than create/import the needed syntacic sugar, letβs just βwing-itβ:
subst-over-β : β {P Q t} {v : Variable t} {e : Expr t} β P β Q β P [ e / v ] β Q [ e / v ]
subst-over-β {P} {Q} {t} {v} {e} PβQ Ο
rewrite sub-to-state {t} {v} {e} {π«} {P} {Ο} ` sub-to-state {t} {v} {e} {π«} {Q} {Ο} = PβQ _
Programming Language
The syntax of our miniature language is as follows,
data Program : Setβ where
skip : Program
abort : Program
_β¨Ύ_ : Program β Program β Program
_β_ : β {t} β Variable t β Expr t β Program
_,_ββ_,_ : β {s t} β Variable t β Variable s β Expr t β Expr s β Program
The subscript 2
in simultaneous assignment is needed, otherwise thereβll be parsing conflicts with the notation for elementary assignment.
As in usual programming languages, the precedence for the program constructors cannot be greater than that of expressions; otherwise, weβll need many parentheses.
infix 7 _β_
infixr 5 _β¨Ύ_
Now we quickly turn to the program semantics βwe do not give βoperational semanticsβ but instead use βHoare Triplesβ. The traditional notation for Hoare Triples is {Q} S {R}
, with assertions enclosed in braces. However, since braces are one of the few reserved symbols in Agda, we instead employ the semantics-brackets yet again. This is not too much of a leap since the conditions are seen to define the program and so assign it meaning βthe ideology mentioned in SoP is that one begins with the pre- and post-conditions then derives the program.
infixr 5 _β¨Ύβ¨_β©_
data β¦_β§_β¦_β§ : Expr π« β Program β Expr π« β Setβ where
_β¨Ύβ¨_β©_ : β {Q Sβ Pβ Pβ Sβ R} β β¦ Q β§ Sβ β¦ Pβ β§ β Pβ β Pβ β β¦ Pβ β§ Sβ β¦ R β§ β β¦ Q β§ Sβ β¨Ύ Sβ β¦ R β§
skip-rule : β {Q R} β Q β R β β¦ Q β§ skip β¦ R β§
absurd : β {Q R} β Q β False β β¦ Q β§ abort β¦ R β§
assignment : {t : Type} (v : Variable t) (e : Expr t) (Q R : Expr π«)
β Q β R [ e β/ v ] β β¦ Q β§ v β e β¦ R β§
simuAssign : {tβ tβ : Type} (vβ : Variable tβ) (vβ : Variable tβ) (eβ : Expr tβ) (eβ : Expr tβ)
(Q R : Expr π«) β Q β R [ eβ , eβ /β vβ , vβ ] β β¦ Q β§ vβ , vβ ββ eβ , eβ β¦ R β§
Those who know me know that I simply adore types and so might be wondering why I considered raw untyped programs, Program
, then correspondingly adorned each constructor with a type by considering programs typed by their specification. I do this so that
given an arbitrary program, we can show it satisfies a specification
typed-programs can have more than one solution and so we might want to indicate which solution we are interested in.
Anyhow, some remarks are in order:
sequential composition is executed by doing the first part then the second part: if we want to establish
R
fromQ
by executingSβ β¨Ύ Sβ
then necessarily we establish an intermediateP
fromQ
by executingSβ
and from that we reachR
viaSβ
. Does composition order matter? Nope, we have(Sβ β¨Ύ Sβ) β¨Ύ Sβ = Sβ β¨Ύ (Sβ β¨Ύ Sβ)
βjust as(m Γ n) Γ k = m Γ (n Γ k)
.ββ Be aware of the role of the semicolon; it is used to combine adjacent, independent commands into a single command, much the same way it is used in English to combine independent clauses. (For an example of its use in English, see the previous sentence.)ββ
In particular, the semicolon is not a statement terminator; which is great since we havenβt even defined a notion of statement!
execution of
skip
does nothing; some languages denote it by whitespace of by;
. It is an explicit way to say nothing is done βwhich is just its definition: givenQ
we wish to establishR
by doing nothing, and this is only possible ifQ β R
. Interestingly, just as1 Γ n = n Γ 1 = n
we haveskip β¨Ύ S = S β¨Ύ skip = S
.abort
should never be executed, since it can only be executed in a state satisfyingFalse
and no state satisfiesFalse
! If it is ever executed, then the program (and its proof) is in error and abortion is called for. Which fits in nicely with the lawabort β¨Ύ S = S β¨Ύ abort = abort
βsimilar to0 Γ n = n Γ 0 = 0
.After execution of
x β e
we have thatx
will contain the valuee
and soR
can be established precisely ifR
, with the value ofx
replaced by the value ofe
can be established before the execution. That is, if we can show that the condition with updated variables can be established, then making the update establishes the condition.Assignment ββ
x
becomese
ββ should really have proof obligationQ β ( domain e cand RΛ£β )
wheredomain
is a predicate that describes the set of all states in whiche
is well-defined and so may be evaluated, andcand
is just lazy-conjunction:x cand y = if x then y else false
.
As mentioned above, in a future post, I intend to replace the explicit constructors by what interpreted function symbols, but it seems that I need them to be augmented with a βdomainβ operation. For example, the symbol Γ·
can be interpreted as integral division with domain condition being the second argument is non-zero. For now, all of our expressions are well-defined. In the future, we intend to use βinterpreted function symbols with domainβ.
In practice, the domain
condition is omitted altogether since assignments should be always written in contexts in which the expressions can be properly evaluated. If we are deriving our program and know not the context, then the full definition is quiet useful.
We now introduce some combinators to make our annotated programs look more like those presented in the literature.
-- for easy copy/paste: β¦ ? β§ ? β ? since ? β¦ ? β§
syntax assignment v e Q R QβRβΛ£ = β¦ Q β§ v β e since QβRβΛ£ β¦ R β§
syntax simuAssign vβ vβ eβ eβ Q R QβRβΛ£ = β¦ Q β§ vβ , vβ ββ eβ , eβ since QβRβΛ£ β¦ R β§
syntax skip-rule {Q} {R} QβR = β¦ Q β§β¨ QβR β©β¦ R β§ -- β¦ Q β§ skip-since QβR β¦ R β§
For example, βExample 1, page 118 of SoPβ
progβ : β¦ π© 5 β π© 5 β§ x β π© 5 β¦ Var x β π© 5 β§
progβ = assignment _ _ _ _ (Ξ» _ _ β β‘-refl)
Now with out handy-dandy syntax:
progβ : β¦ π© 5 β π© 5 β§ x β π© 5 β¦ Var x β π© 5 β§
progβ = β¦ π© 5 β π© 5 β§ x β π© 5 since (Ξ» st β β-refl {π© 5 β π© 5} st) β¦ Var x β π© 5 β§
Before we move on, we know from propostional logic that all theorems can be replaced with true
. In particular, reflexitvity of equality does not give us much (in our toy language) and so we can replace it with true which was defined earlier as the unit type.
progβ : β¦ True β§ x β π© 5 β¦ Var x β π© 5 β§
progβ = β¦ True β§
x β π© 5 since (Ξ» _ _ β β‘-refl)
β¦ Var x β π© 5 β§
Examples
We now port over some simple programs from Griesβ text and show that readability is still maintained! We begin with the most complicated one, that is expressible in the language as described so far.
swap : β {X Y : β} β β¦ Var x β π© X β§ Var y β π© Y β§
z β Var {π©} x
β¨Ύ x β Var {π©} y
β¨Ύ y β Var {π©} z
β¦ Var x β π© Y β§ Var y β π© X β§
swap {X} {Y} =
β¦ Var x β π© X β§ Var y β π© Y β§
z β Var x since β§-sym {Var x β π© X} {Var y β π© Y}
β¦ Var y β π© Y β§ Var z β π© X β§
β¨Ύβ¨ (Ξ» st zβ β zβ) β©
β¦ Var y β π© Y β§ Var z β π© X β§
x β Var y since (Ξ» st zβ β zβ)
β¦ Var x β π© Y β§ Var z β π© X β§
β¨Ύβ¨ (Ξ» st zβ β zβ) β©
β¦ Var x β π© Y β§ Var z β π© X β§
y β Var z since (Ξ» st zβ β zβ)
β¦ Var x β π© Y β§ Var y β π© X β§
Some things to note:
We needed to mention the type π© since the variables are in-fact always independent; eg, the first
x
is not at all related to any second occurrence ofx
. This is like the numerals: one 4 does not affect another 4.The proof was really just
C-c C-a/r
, auto and refinements! What I did manually was replace theassignment
constructor with the preferred syntax.finally, equality syntax, as defined so far, is only well-defined for π© and so we could not prove
swap
for all types.
Swapping becomes a one-liner using simultaneous assignment.
swap2 : β {X Y : β} β β¦ Var x β π© X β§ Var y β π© Y β§ x , y ββ Var {π©} y , Var {π©} x β¦ Var x β π© Y β§ Var y β π© X β§
swap2 {X} {Y} = β¦ Var x β π© X β§ Var y β π© Y β§
x , y ββ Var y , Var x since β§-sym {Var x β π© X} {Var y β π© Y}
β¦ Var x β π© Y β§ Var y β π© X β§
One popular language that offers such a command is Python.
Another example of sequence βmany C-c C-r!
progβ : β¦ True β§ skip β¨Ύ x β π© 5 β¦ Var x β π© 5 β§
progβ = β¦ True β§β¨ β-refl {True}
β©β¦ True β§
β¨Ύβ¨ (Ξ» st xβ β xβ) β©
β¦ True β§
x β π© 5 since (Ξ» _ _ β β‘-refl)
β¦ Var x β π© 5 β§
Multiple assignments to the same variable mean only the last assignment is realized.
progβ : β¦ True β§ x β π© 5 β¨Ύ x β π© 3 β¦ Var x β π© 3 β§
progβ = β¦ True β§
x β π© 5 since (Ξ» _ _ β β‘-refl)
β¦ π© 3 β π© 3 β§
β¨Ύβ¨ β-refl {π© 3 β π© 3} β©
β¦ π© 3 β π© 3 β§
x β π© 3 since β-refl {π© 3 β π© 3}
β¦ Var x β π© 3 β§
Awesome! Looks like a paper-and-pencil annotation (with proofs)!
If the post condition does not involve the variable assigned, then we can ignore it: x not free in P yields β¦ P β§ x β e β¦ P β§
.
Here is a particular case, obtained by C-c C-a then using syntactic sugar βpage 119β,
constantEx : β {e : Expr π©} {c : β} β β¦ Var y β π© c β§ x β e β¦ Var y β π© c β§
constantEx {e} {c} = β¦ Var y β π© c β§
x β e since (Ξ» st pf β pf)
β¦ Var y β π© c β§
Sometimes weβll need lemmas in our annotations. For example, If I want x
to be at least 10 after increasing it by 3, then I necessairly need x
to be at least 7 before the increment. [Page 118, but no negative numbers.]
leq : β¦ π© 7 β€ Var x β§ x β Var x + π© 3 β¦ π© 10 β€ Var x β§
leq = β¦ π© 7 β€ Var x β§
x β Var x + π© 3 since lemma1
β¦ π© 10 β€ Var x β§
where
lemma1 : π© 7 β€ Var x β π© 10 β€ Var x + π© 3
lemma1 Ο 7β€Οx = 7β€Οx +-mono β€β-refl {3}
where we have used the fact that addition preserves order, _+-mono_ : β {x y u v : β} β x β€β y β u β€β v β x +β u β€ y +β v
.
Program Equality
Earlier, when introducing the programming language semantics, we mentioned certain equalities; such as, skip
is identity of composition: skip β¨Ύ S = S β¨Ύ skip = S
. We never defined program equality! Letβs fix that.
Two programs are considered equal precisely when they satisfy the same specifications,
infix 4 _ββ_
_ββ_ : Program β Program β Setβ
S ββ T = β {Q R} β β¦ Q β§ S β¦ R β§ β β¦ Q β§ T β¦ R β§
Where x β y
just means a pair of functions x β y
and y β x
.
Let us prove the aforementioned unity law. We begin with an expected result, then the needed pieces.
strengthen : β Q {P R S} β Q β P β β¦ P β§ S β¦ R β§ β β¦ Q β§ S β¦ R β§
strengthen = {! exercise in C-c C-c and C-c C-a ^_^ !}
What about? weaken : β {Q P} R {S} β P β R β β¦ Q β§ S β¦ P β§ β β¦ Q β§ S β¦ R β§
?
Letβs add some syntacic sugar βgood olβ sweet stuff ^_^
syntax strengthen Q QβP β¦Pβ§Sβ¦Rβ§ = β¦ Q β§ββ¨ QβP β© β¦Pβ§Sβ¦Rβ§
tada : β {S Q R} β β¦ Q β§ skip β¨Ύ S β¦ R β§ β β¦ Q β§ S β¦ R β§
tada {S} {Q} {R} (skip-rule QβPβ β¨Ύβ¨ PββPβ β© β¦Pββ§Sβ¦Rβ§) =
β¦ Q β§ββ¨ (Ξ» Ο zβ β PββPβ Ο (QβPβ Ο zβ))β© β¦Pββ§Sβ¦Rβ§
tadaΛ : β {S} {Q R} β β¦ Q β§ S β¦ R β§ β β¦ Q β§ skip β¨Ύ S β¦ R β§
tadaΛ {S} {Q} {R} β¦Qβ§Sβ¦Rβ§ =
β¦ Q β§β¨ β-refl {Q}
β©β¦ Q β§
β¨Ύβ¨ β-refl {Q} β©
β¦Qβ§Sβ¦Rβ§
skip-left-unit : β {S} β skip β¨Ύ S ββ S
skip-left-unit = tada , tadaΛ
These abstract-proofs are not as clear as the examples, but thatβs to be expected since the syntax was made with concrete code annotation in mind.
Closing
It seems that weβve managed to get a lot done βup to chapter 9 of SoP excluding arrays, quantification, and a few other function and relation symbols. We intend to remedy the situation in future posts! Hope youβve enjoyed the article!