Fundamentals of Perfect Developer: A One-Day Hands-On Tutorial
Fundamentals of Perfect Developer: A One-Day Hands-On Tutorial
Developer
A one-day hands-on tutorial
www.escherte
Outline
Session 1: Getting Started
Configuring Perfect Developer and creating a project
Expressions, types, constants, functions, properties
Classes, data, invariants, functions, schemas, constructors
Session 4: Inheritance
Derived and deferred classes
Defining and redefining inherited methods
www.escherte
Configuration
Configure Perfect Developer to use the chosen
editor
www.escherte
Creating a project
Click on the New toolbar icon
Browse to a writable folder, enter a project name,
click Save and then OK
Create a new file called Examples
Add text:
www.escherte
bool
set of X
char
bag of X
int
seq of X
real
map of (X -> Y)
Expressions
All the usual arithmetic operators
a < b < c means what you would expect it to
a / b (integer) and a % b have precondition b > 0
a / b (integer) rounds towards -
a .. b yields the sequence {a, a+1, a+2 b}
#c yields the cardinality or length of the collection c
a # c yields the number of occurrences of a in c
www.escherte
<==
<==>
Conditional expression:
( [a > 0]: a, [b > 0]: b, []: 0)
Let-declaration:
( let t ^= a - b; t * t )
Embedded assertion:
( assert a > 0, b > 0; a / b )
www.escherte
Constructor calls
A constructor call yields a value of the specified type
Without parameters:
seq of int{}
MyClass{}
With parameters:
seq of int{a, b, c}
MyClass{42}
www.escherte
Quantifiers etc.
If c is of type set of T, bag of T or seq of T,
and p(x) is any Boolean expression involving x:
Expression
Return type
www.escherte
Declaring a function:
www.escherte
Declaring properties
Use property declarations to express theorems:
property
assert half(four) = two;
Implicit universal quantification over the
parameters
property (x: int)
Givens to be
pre x > 0,
assumed
x % 2 = 0
assert half(x) < x,
(let h ^= half(x); h + h = x);
Consequences to be
proved
www.escherte
Declaring enumerations
class Currency ^= enum
unspecified, euro, pound, USdollar
end;
const localCurrency ^= pound@Currency;
localCurrency.toString
www.escherte
Declaring a Class
class Money ^=
abstract variables,
invariants,
methods,
constructors
interna
l
confined
interface
end;
www.escherte
No () if no
parameters
Return type
Result expression
Optional
precondition
Optional
postassertion
Operator
declarations are the
same as function
declarations apart
from the header
www.escherte
Declaring Schemas
No self object
No () if no
parameters
Parameter is modified
Postcondition
includes
frame
This one
modifies
instance
variables
www.escherte
Declaring Constructors
Note parameter list in
{}
build{a: int, c: Currency}
pre a > 0
post amt! = a, ccy! = c;
build{!amt: int}
post ccy! = euro@Currency;
build{}
^= Money
{0,
unspecified@Currency};
This constructor is
defined in terms of
another one
www.escherte
Postcondition must
initialise all instance
variables*
Initialise instance
variable directly from
the parameter
Short for:
change amt
satisfy amt= a
We do use {} if
no parameters
*except for variables whose
when-guards are false
makes v1 readable
makes v2 readable and writable
www.escherte
Exercise 2: Specification
(followed by coffee break)
You have been provided with a Perfect specification
of class Money
Try to verify it (there will be 3 verification errors)
Fix the specification to remove the verification errors
Verify that multiplying a Money object by 2 is
equivalent to adding it to itself
Declare a + operator that works like the plus
function except that if the amount of either operand
is zero, we dont care what the corresponding
currency is
www.escherte
Perfect back-end
constructor calls
when pressed
calls
when pressed
calls
class Application
^=
interface
build{}
function f ()
schema ! s ()
www.escherte
www.escherte
[tips follow]
www.escherte
Tips
To use constants and functions from
Examples.pd:
Add file Examples.pd to the project
Add to Application.pd: import "Examples.pd";
www.escherte
www.escherte
www.escherte
Recursive call
Recursion variants
General form is:
decrease e1, e2, e3
Exercise 4: Sequences
Specify the following functions:
numLeadingSpaces(s: string): nat
returns the index of the first character in s that is not
a space, or the length of s if it is all spaces
www.escherte
Lunch break!
www.escherte
Refinement
There are three types of refinement in Perfect:
Refining result expressions to statement lists
Refining postconditions to statement lists
Refining abstract data to implementation data
www.escherte
Refining specifications
Specification refinement serves these purposes:
To implement a specification where Perfect Developer
fails to
To implement a specification more efficiently
To take account of data refinement in affected methods
www.escherte
value statement
returns a value from
the via..end
Semicolon separates
and sequences the
statements
A postcondition can
be used as a
statement
Nested Refinements
You can refine not just method specifications but
also:
Postcondition statements
Let-declarations in statement lists
function fourth(x: int): int
^= x^4
via
let x2 ^= x^2
via
value x*x
end;
value x2*x2
end;
www.escherte
value yielded by
the inner via..end
value yielded by
the outer via..end
Types of Statement
Let-declaration
Assertion
pass statement
Does nothing
If-statement
value and done statements
Loop statement
Block statement
www.escherte
If-statement
if
[c in `a`..`z`]:
isAletter! = true;
valid! = true;
Guard
Statement list
[c in `0`..`9`]:
isAletter! = false;
valid! = true;
[]:
valid! = false
fi
www.escherte
value statement
function max(a,b,c: class X): X
satisfy result >= a
& result >= b
& result >= c
& (result=a | result=b |
result=c)
via
if
[a > b]:
value max(a, c);
[]:
value max(b, c)
fi
end;
www.escherte
Every path in
an expression
refinement
must end at a
value
statement
done statement
schema max(a!,b,c: class X)
post change a
satisfy a >= a
& a >= b
& a >= c
& (a= a | a= b | a= c)
via
if
[a > b]:
a!= max(a, c);
done;
[] fi;
a!= max(b, c)
end;
www.escherte
A postcondition
refinement may
contain one or
more done
statements
Implicit done
statement
here
Loop statement
// Calculate a^b
var rslt: int! = 1;
loop
var j: nat! = 0;
change rslt
keep rslt = a^j
until j= b
decrease b - j;
rslt! * b,
j! + 1
end;
Start of loop
statement
Loop variable
declaration
List of what the loop can
change
Loop invariant list
Termination
condition
Loop variant
Loop body
End of loop
statement
www.escherte
Loop statement
loop
local variable declarations (optional)
change list (optional)
invariant
termination condition (optional)
variant
body statements
end
www.escherte
www.escherte
www.escherte
www.escherte
Problem! These
expressions are
not universally
well-formed
www.escherte
www.escherte
www.escherte
www.escherte
www.escherte
Loops: a summary
Getting the invariant correct is critical
It must describe the relationships between all variables
changed by the loop (including the local loop variables)
www.escherte
y: int): int
<= x,
<= y,
= x | result = y;
www.escherte
Data refinement
When designing a class, we should always use
the simplest possible abstract data model
Avoid redundancy!
Dont be concerned with efficiency at this stage!
www.escherte
www.escherte
www.escherte
www.escherte
www.escherte
www.escherte
www.escherte
www.escherte
Inheritance
When declaring a class you can inherit another class
Declare class UniversityMember
Then
class Student ^= inherits UniversityMember
And
class StaffMember ^= inherits UniversityMember
And
class Professor ^= inherits StaffMember
www.escherte
www.escherte
Redefining methods
Functions, selectors, schemas and operators that
are inherited from a parent class may be
redefined
This must be indicated using the redefine
keyword
The parameter and result types in the redefinition
must be identical to those in the overridden
function
www.escherte
Example of overriding
class UniversityMember ^=
abstract
var firstNames: seq of string, lastName: string;
interface
function getSalary: Money
^= 0;
end;
class StaffMember ^= inherits UniversityMember
abstract
var salary: Money;
interface
redefine function getSalary: Money
^= salary;
end;
www.escherte
www.escherte
Deferred methods
You can also declare a method in a class deferred
The method is left undefined in that class
This avoids the risk of classes inheriting what may
be an unsuitable definition
The class itself must also be flagged deferred
and may not be instantiated
Descendent classes may define the method using
the define keyword
Any descendent class that does not define it is
also a deferred class
www.escherte
end;
class StaffMember ^= inherits UniversityMember
abstract
var salary: Money;
interface
define function getSalary: Money
^= salary;
end;
www.escherte
www.escherte
Some consequences
If D is a deferred class:
var x: D is illegal
But you can use var x: from D
If F is a final class:
var x: from F is illegal
But you can use: var x: F
www.escherte
www.escherte
end;
class StaffMember ^= inherits UniversityMember
abstract
var salary: int;
interface
define function isPaid: bool
^= true;
define function getSalary: Money
^= salary;
www.escherte
www.escherte
end;
class Student ^= inherits UniversityMember
interface
define function isPaid: bool
^= false;
absurd function getSalary;
www.escherte
Absurd methods
Declaring a method absurd means that its
precondition is always false
Repeat the parameter list of the method but not its
return type
An absurd method declaration has these
consequences:
The method is defined or redefined such that calling it will
raise an exception
A verification condition is generated (i.e. that the
precondition is always false)
It avoids the Given false, so proof is trivial warnings you
will otherwise see
www.escherte
www.escherte
Inheritance tips
When using inheritance, declare methods final
where possible
This is not necessary in leaf classes which are declared
final
Inheritance exercises
Design a UniversityMember or Employee class
hierarchy that reflects the properties and
privileges of members of your organisation
Specify a family of shopping scanners, as outlined
at the end of the Shopping Scanner worked
example at:
http://www.eschertech.com/teaching/scanner_example.pdf
www.escherte
after expressions
over expressions
Guarded variable declarations
Selectors
Members of classes set and bag
Declaring templated classes
Other library classes, e.g. map of (X -> Y)
How to solve verification problems
Serialization
Declaring axioms
www.escherte
Further Reading
Perfect Developer 3.0 Language Reference
Manual
Start -> Programs -> Perfect Developer ->
Documentation
Or click on the book tool in the Project Manager
Also available at www.eschertech.com
Online tutorial
via Support -> Self-help section of the web site
Teaching materials
via Support -> Self-help -> Teaching materials
www.escherte
www.escherte