.
 CLU proposal
.
.dv xgp
.fo 0 30vr
.fo 1 31vgb
.fo 2 30vri
.fo 3 37vrb
.fo 4 75vbee
.fo 5 30fg
.tr @ 
.nr chapter_starts_page 0
.nr reset_per_page 0
.nr both_sides 1
.ls 2
.sr left_heading
.sr right_heading
.sr figure_name Figure \
.sr table_name Table \
.nr immediate_figure 0
.nr tty_table_of_contents 1
.nr verbose 1
.sr list_left_margin 500m
.sr list_right_margin 500m
.so r/r.macros
.so clu/clukey.r
.
 number register for section references
.
.nr introduction 1
.nr example 2
.nr semantics 3
.nr more_abstraction 4
.nr library 5
.nr implementation 6
.nr efficiency 7
.nr discussion 8
.
.
.so r/ref3.rmac
.
.ref Atk76
Atkinson, R. R.
Optimization techniques for a structured programming language.
S.M. Thesis,
Dept. of Electrical Engineering and Computer Science,
M.@I.@T., Cambridge, Mass., June 1976.
.em
.ref Birkhoff70
Birkhoff, G. and Lipson, J. D.
Heterogeneous Algebras.
2Journal of Combinatorial Theory 8*, (1970), 115-133
.em
.ref Burstall
Burstall, R. M.
Some Techniques for Proving Correctness of Programs Which Alter
Data Structures.
2Machine Intelligence 7*, D. Mitchie (ed.), (1972),
American Elsevier, New York
.em
.ref Clint
Clint, H.
Program Proving: Coroutines.
2Acta Informatica 2*, (1973), 50-63
.em
.ref Dah70
Dahl, O. J., Myhrhaug, B., and Nygaard, K.
2The SIMULA 67 Common Base Language*.
Publication S-22, Norwegian Computing Center, Oslo, 1970.
.em
.ref Dij72
Dijkstra, E. W.  
Notes on structured programming.
2Structured Programming,
A.P.I.C. Studies in Data Processing No. 8,*,
Academic Press, New York 1972, 1-81.
.em
.ref Dij76
Dijkstra, E. W.
2A Discipline of Programming*, (1976)
Prentice-Hall Series in Automatic Computation,
Prentice Hall, Englewood Cliffs, New Jersey
.em
.ref HewGr
Greif, I. and Hewitt, C.
Actor Semantics of PLANNER-73.
2Proceedings of the ACM SIGPLAN/SIGACT Conference*,
Palo Alto, California, (1975)
.em
.ref Greif75
Greif, I.
2Semantics of Communicating Parallel Processes*, (1975)
Ph. D. Thesis, M. I. T., MAC TR-154
.em
.ref Guttag75
Guttag, J. V.
2The Specification and Application to Programming of
Abstract Data Types*, (1975)
Ph. D. Thesis, University of Toronto CSRG-59
.em
.ref Guttag76a
Guttag, J. V.
Abstract Data Types and the Development of Data Structures.
2Supplement to the Proceedings of the SIGPLAN/SIGMOD Conference
on Data: Abstraction, Definition, and Structure*, (1976), 37-46
.em
.ref Guttag76b
Guttag, J. V., Horowitz, E. and Musser, D. R.
Abstract Data Types and Software Validation.
2USC ISI/RR-76-48*, (1976)
.em
.ref Habermann
Habermann, A. N.
Path Expressions.
Dept. of Computer Science, Carnegie-Mellon University, (1975)
.em
.ref Hewitt
Hewitt, C. and Atkinson, R. R.
Parallelism and Synchronization in Actor Systems.
2ACM SIGACT/SIGPLAN Conference*, Los Angeles, (Jan. 1977)
to be published.
.em
.ref HoareAx
Hoare, C. A. R.
An axiomatic basis for computer programming.
2Comm. ACM 12*, 10 (October 1969), 576-583.
.em
.ref Hoare71
Hoare, C. A. R.
Procedures and Parameters - An Axiomatic Approach.
2Symposium on the Semantics of Algorithmic Languages*, (1971), 102-116,
E. Engeln (ed.)
Springer, Berlin - Heidelberg - New York
.em
.ref Hfor
Hoare, C. A. R.
A note on the for statement.
2BIT 12*, 1972, 334-341.
.em
.ref HoareCDR
Hoare, C. A. R.
Proof of Correctness of Data Representations.
2Acta Informatica 1*, 4, (1972), 271-281
.em
.ref HoareMON
Hoare, C. A. R.
Monitors: An Operating System Structuring Concept.
2Comm. ACM 17*, 10, (1974) 549-557
.em
.ref LCS75
2Laboratory for Computer Science Progress Report 1974-1975*,
Computation Structures Group.
Rep. PR-XII,
Laboratory for Computer Science, M.@I.@T.,
to be published.
.em
.ref Euclid
Lampson, B. W., Horning, J. J., London, R. L., Mitchell, J. G. and Popek, G. J.
Report on the Programming Language Euclid
(1976).
.em
.ref Laventhal
Lavanthal, M. S.
2Verification of Programs Operating on Structured Data*, (1974)
Master's Thesis, M. I. T. MAC TR-124
.em
.ref Lis74
Liskov, B. H., and Zilles, S. N.
Programming with abstract data types.
Proc. ACM SIGPLAN Conference on Very High Level Languages,
2SIGPLAN Notices 9*, 4 (April 1974), 50-59.
.em
.ref Lis75
Liskov, B. H., and Zilles, S. N.
Specification techniques for data abstractions.
2IEEE Trans. on Software Engineering*, 2SE-1*,
(1975), 7-19.
.em
.ref Lis76
Liskov, B. H., and Berzins, V.
An appraisal of program specifications.
Computation Structures Group Memo 141,
Laboratory for Computer Science,
M.@I.@T., Cambridge, Mass., July 1976.
.em
.ref CSG144
Liskov, B. H., Snyder, A., Atkinson, R. R. and Schaffert, J. C.
Abstraction Mechanisms in CLU.
Computation Structures Memo 144, (1976)
.em
.ref McC62
McCarthy, J., et al.
2LISP 1.5 Programmer's Manual*.  MIT Press, 1962.
.em
.ref McCcond
McCarthy, J.
A basis for a mathematical theory of computation.
2Computer Programming and Formal Systems*.
Braffort, Hirschberg, eds., North Holland Publishing Co.,
Amsterdam-London 1963, 33-70.
.em
.ref Mor73
Morris, J. H.
Protection in programming languages.
2Comm. ACM 16*, 1 (Jan 1973), 15-21.
.em
.ref Owicki
Owicki, S. S.
2Axiomatic Proof Techniques for Parallel Programs*, (1975)
Ph. D. Thesis, Cornell University, TR75-251
.em
.ref Parspec
Parnas, D. L.
A Technique for Software Specification with Examples.
2Comm. ACM 15*, (1972), 330-336
.em
.ref Parnas75
Parnas, D. L. and Handzel, G.
More on Specification Techniques for Software Modules.
Fachbereich Informatik Technische Hochschule Darmstadt, (1975)
.em
.ref SRI
Robinson, L., Levitt, K, Neumann, P. G. and Saxena, A. R.
On Attaining Reliable Software for a Secure Operating System.
2Proc. Interntional Conf. on Reliable Software*, (1975), 267-284
.em
.ref Schaffert
Schaffert, J. C.
2Specifying Meaning in Object Oriented Languages*, (1976)
forthcoming Master's Thesis, M. I. T.
.em
.ref Wulfit
Shaw, M., Wulf, W. A., and London, R. L.
2Abstraction and Verification in Alphard:  Iteration and Generators.*
Department of Computer Science Report, Carnegie-Mellon
University, August 1976.
.em
.ref Sites
Sites, R. L.
2Proving That Computer Programs Terminate Cleanly*.
Report STAN-CS-74-418, Stanford University, Computer Science Department,
1974.
.em
.ref Suzuki
Suzuki, N.
2Automatic Verification of Programs with Complex Data Structures*, (1976)
Ph. D. Thesis, Comp. Science Dept., Stanford University
.em
.ref Wir71a
Wirth, N.
Program development by stepwise refinement.
2Comm. ACM 14*, 4 (1971), 221-227.
.em
.ref Wul84
Wulf, W. A., London, R., and Shaw, M.
2Abstraction and Verification in Alphard: An Introduction.*
Department of Computer Science Report, Carnegie-Mellon
University, June 1976.
.em
.ref Zilles74
Zilles, S.
Algebraic Specification of Data Types.
2Project Mac Progress Report*, (1974) 52-58;
also Computation Structures Group Memo 119
.em
.bp
.
 string registers for italic variable names
.
.sr i 2i*
.sr s 2s*
.sr o 2o*
.sr c 2c*
.sr n 2n*
.sr t 2t*
.sr r 2r*
.sr x 2x*
.sr tr 2tr*
.sr w 2w*
.sr wb 2wb*
.sr total 2total*
.sr contents 2contents*
.sr count_words 2count_words*
.sr next_word 2next_word*
.sr wordbag 2wordbag*
.sr wordtree 2wordtree*
.sr wordbags 2wordbags*
.sr wordtrees 2wordtrees*
.sr insert 2insert*
.sr create 2create*
.sr print 2print*
.sr instream 2instream*
.sr instreams 2instreams*
.sr outstream 2outstream*
.sr outstreams 2outstreams*
.nd wordbag 3
.sr p 2p*
.sr q 2q*
.sr x 2x*
.sr y 2y*
.sr z 2z*
.sr a 2a*
.sr b 2b*
.sr insert 2insert*
.sr increment 2increment*
.
.sr wordbag 2wordbag*
.sr sorted_bag 2sorted_bag*
.sr sorted_bags 2sorted_bags*
.sr wordtree 2wordtree*
.sr tree 2tree*
.sr node 2node*
.sr r 2r*
.sr x 2x*
.sr t 2t*
.sr count_words 2count_words*
.sr count_numeric 2count_numeric*
.sr less_than 2less_than*
.sr equal 2equal*
.sr print 2print*
.sr string_chars 2string_chars*
.sr create 2create*
.sr insert 2insert*
.sr size 2size*
.sr increasing 2increasing*
.sr s 2s*
.sr n 2n*
.sr index 2index*
.sr limit 2limit*
.sr count 2count*
.sr next_word 2next_word*
.sr elements 2elements*
.sr reverse_elements 2reverse_elements*
.
.
.sr p 2p*
.sr q 2q*
.sr x 2x*
.sr y 2y*
.sr z 2z*
.sr a 2a*
.sr b 2b*
.sr insert 2insert*
.sr p 2p*
.sr q 2q*
.sr x 2x*
.sr y 2y*
.sr z 2z*
.sr a 2a*
.sr b 2b*
.sr insert 2insert*
 
 string registers for special symbols
 
.sr le 5*
 less-than-or-eq
.sr eq 5*
 equivalent
.sr not 5*
 not
.sr or 4*
 or
.sr all 5*
 for all
.sr ex 5*
 there exists
.sr u 5*
 union
.sr mem 5*
 member
.sr sub 5_*
 subset
.hx space 1
.de align
'be alignment
 
'nv indent (hpos-space)

.em
.de un_align
'en alignment
.em
3CONSTRUCTION AND VERIFICATION OF*
.br
3WELL-STRUCTURED PROGRAMS*
.sp 3
3Abstract*
	This research is concerned with the construction and
verification of well-structured programs.  Our approach derives
from a programming methodology in which the recognition and use
of abstractions plays a major role.  We have identified three kinds of
abstractions, procedural, data and control abstractions,
that are useful in the programming process.  Support for the
methodology involves both the development of linguistic
constructs to enable programming with abstractions, and 
the development of techniques for specifying the meaning
of abstractions and for
verifying the correctness of programs written in terms of
abstractions.
	During the present period of support, we have made
progress in both these areas.  We have designed the CLU
programming language and system, which supports the use of
control and data abstractions by means of novel linguistic
mechanisms.  In addition, we have developed specification
and verification techniques for data abstractions.
	We propose to continue our work on CLU and on
specification and verification.  We propose to extend
CLU to parallel programming and to modify it where necessary
to make CLU programs more amenable to verification.
We also propose to continue our study of specification and
verification of CLU programs by extending our current work
to programs that share changeable data bases and to parallel programs.
.bp
.chapter "Introduction"
	This research has as its objective the development of
tools and techniques to ease the production of software that is
reliable and relatively easy to understand, modify, and
maintain.  Our work is based on a programming methodology in
which programs are developed by means of problem decomposition
based on the recognition of abstractions.  A program is
constructed in many stages.  At each stage, the problem to
be solved is how to implement some abstraction (the initial
problem is to implement the abstract behavior required of
the entire program).  This is done by performing the
following four steps:
.ilist 3
1.  	Problem Decomposition.  The programmer envisions a
number of subsidiary abstractions which are useful in the
problem domain.
.next
2.  	Specification.  The behavior of each abstraction is specified
precisely.
.next
3.  	Implementation.  Once the behavior of these abstractions
is understood and specified, they can be used in a program
to implement the problem.
.next
4.  	Verification.  The programmer verifies that the program
correctly implements the problem, assuming that the abstractions are 
implemented correctly.
.end_list
As soon as step (2) has been performed, new problems exist
concerning how to implement the abstractions defined in step (2).
The programmer can choose to work on one of these problems
immediately, before steps (3) and (4) have been carried out
for the current stage.
The process terminates when all abstractions generated during
design are realized either by programs or by the programming
language in use.
	The above methodology is similar to that described
by Dijkstra [Dij72] and Wirth [Wir71a].  It is original in the
emphasis placed on specifications, and in the kinds of
abstractions that are used.
	To make effective use of this methodology,
it is necessary to understand the nature of the abstractions
that are useful in constructing programs; this includes what
is being abstracted, and what form the abstraction takes.
In studying this question, we identified three kinds of
useful abstractions: procedural, control and especially
data abstractions.  While the procedural abstraction 
(which performs a computation on a set of input
objects, and produces a set of output objects) has long been recognized
as useful, control and data
abstractions have been neglected in discussions of
programming methodology.
	A control abstraction defines a method of sequencing
arbitrary actions.  All languages provide built-in control abstractions;
examples are the if
statement and the while statement.  In addition, however,
it is helpful to allow user definitions of a simple kind of control abstraction, which is a generalization of the
repetition methods (in particular, the for
statement) available in many programming
languages.  Frequently the programmer desires to
perform the same action for all the objects in a
collection, such as all the
characters in a string or all items in a set.  The simple
control abstraction permits the action to be described
separately from the method of obtaining the objects in
the collection.
	A data abstraction is used to introduce a new
type of data object that is deemed useful
in the domain of the problem being solved.
At the level of use, the programmer is
concerned with the 2behavior* of these data objects,
what kinds of information can be stored in them and
obtained from them.  The programmer is 2not* concerned
with how the data objects are represented in storage,
nor with the algorithms used to store and access
information in them.  In fact, a data abstraction is
often introduced to delay such implementation
decisions until a later stage of design.
	The behavior of the data objects is expressed most
naturally in terms of a set of operations that are meaningful
for those objects.  This set will include operations
to create objects, to obtain information from them,
and possibly to modify them.
For example,
push and pop are among the meaningful operations for stacks,
while meaningful operations for integers include the usual
arithmetic operations.
	Thus, a data abstraction consists of a
set of objects and a set of operations
which characterize the behavior of the
objects.  To ensure that a data abstraction can be understood
at an abstract level, we require that the operations 2completely*
determine the behavior of the data objects.  This property can
be achieved by making the operations the 2only direct means*
of creating and manipulating the objects.
	There are two main areas of research involved in
supporting the above methodology:
.ilist 3
1.  	Linguistic support for programming with abstractions.  Data
and control abstractions are not supported well by conventional languages.
.next
2.  	Development of techniques for specifying the meaning of
abstractions, and for verifying the correctness of programs
written in terms of abstractions.
.end_list
	During the present period of support we have been
working in both these areas.  We discuss some of our
accomplishments in Sections 2 and 3.  We have designed the CLU
programming language and system; this is discussed in Section 2.
We have studied specification and verification techniques
for data abstractions; this is discussed in Section 3.
	We propose to continue our work on CLU and on
specification and verification.  We propose to extend CLU
to parallel programming and to modify it where necessary
to make CLU programs more amenable to verification.
We also propose to continue our study of specification
and verification of CLU programs by extending our current
work to programs that share changeable data bases and to
parallel programs.  We discuss this research in Section 4.
.chapter "The CLU Programming Language and System"
	CLU has been designed to support the use of
procedural, data and control abstractions in writing
programs.  Of these, only the procedural abstraction
is supported well by conventional languages, through
the procedure or subroutine.  CLU provides, in addition
to procedures, novel linguistic mechanisms that support
the use of data and control abstractions.
	In this section we give examples to illustrate
how CLU supports data abstractions and control abstractions.  We also discuss the two different kinds of data objects (mutable and
constant) in CLU.
The data and control abstraction examples
shown are very simple.  A more complicated
example, which illustrates the use of parameterized
abstractions, is contained in Appendix A.  Appendix B explains
the semantics of CLU.
.section "A Simple Cluster"
	Data abstractions may be incorporated in a programming
language by considering each data abstraction to be a
data type.
We have identified the following requirements which must be
satisfied by a language supporting data abstractions [Lis74]:
.ilist 3
1.	A linguistic construct is needed that permits
a data abstraction to be implemented as a unit.
The implementation involves selecting a storage representation
for the data objects and defining an algorithm for each
operation in terms of that representation.
.next
2.	The language must limit access to the
representation to just the operations.  This is
necessary to ensure that the operations completely
characterize the behavior of the objects.  
.end_list
CLU satisfies these requirements by providing a linguistic construct,
called a 2cluster*, for implementing data abstractions.
Access to the representation is
controlled by type-checking, which is done at 
compile time.  The way in which this works
is discussed below.
	In this section we illustrate the use of clusters
by showing how to implement the data abstraction
2rational_number*.  This abstraction provides operations
to create and copy rational numbers, to perform arithmetic
operations on them, and to tell whether two rational numbers
are equal.  The header of the cluster is
.sp .5
	2rational_number* = 1cluster is* create, plus, equal, copy, ... ;
.sp .5
This form emphasizes the idea that a data abstraction is a set of
operations as well as a set of objects.
.nr rat current_figure
	The cluster must provide a representation for
2rational_number* objects, and an implementation for each
operation.  The (partial) implementation of 2rational_number*
is shown in Figure rat.  Following the initial line, we find the
representation selected for rational numbers:
.sp .5
		1rep* = 1record* [num: 1integer*, denom: 1integer*]
.sp .5
This states that a 2rational_number* object contains two
integers, recording the numerator and denominator respectively.
.begin_figure "A partial implementation of the rational_number data abstraction."
.table
.new_font 0
rational_number = 1cluster is* create, plus, equal, copy, ... ;

	1rep* = 1record* [num: 1integer*, denom: 1integer*];
.sp
create = 1proc* (n, d: 1integer*) 1returns* (1cvt*);
	1if* d = 0 1then* ERROR; 1end*;
	1if* d < 0 1then* d := -d; n := -n; 1end*; 
	1return* {num: n, denom: d};
	1end* create;

plus = 1proc* (x, y: 1cvt*) 1returns* (1cvt*);
	n: 1integer* = (x.num * y.denom) + (y.num * x.denom);
	d: 1integer* = x.denom * y.denom;
	1return* {num: n, denom: d};
	1end* plus;

equal = 1proc* (x,y: 1cvt*) 1returns* (1boolean*);
	1if* x.num = 0 1then return* (y.num = 0); 1end*;
	reduce (x);
	reduce (y);
	1return* (x.num = y.num & x.denom = y.denom);
	1end* equal;

reduce = 1proc* (x: 1rep*);
	% reduce modifies the representation of x to lowest common terms
	div: 1integer* = gcd (abs (x.num), x.denom);
	x.num = x.num/div;
	x.denom = x.denom/div;
	1end* reduce;

copy = 1proc* (x: 1cvt*) 1returns* (1cvt*);
	1return* {num: x.num, denom: x.denom};
	1end* copy;
	.
	.
	.
1end* rational_number;
.ns
.end_table
.finish_figure
	A record is an object with one or more named
components.  The value of a component named n of the record r
can be acquired via the 2get_n@(r)* operation (usually
abbreviated 2r.n*).
The value of the n component of r can be
replaced by x via the 2put_n@(r,@x)* operation
(usually abbreviated 2r.n@*:=2@x*,
by analogy with the assignment statement).
A new record is created by an expression of the form
{name1: value1, ... , namen: valuen}.
	The first thing to notice is that there are two different
types associated with any cluster:  the new
type being defined (2rational_number* in this case) and the
representation type (the record).  Outside of the cluster,
type-checking will ensure that a 2rational_number* object will always be
treated as such.
In particular, the ability to convert a 2rational_number* object into its
representation is not provided.
.foot
Unless one of the 2rational_number* operations does so explicitly.
.efoot
	Inside the cluster, however, it is necessary to view
a 2rational_number* object as being of the representation type, because the
operations are defined in terms of the representation.  This
change of viewpoint is signalled by having the keyword cvt
appear as the type of an argument (as in the 2plus* and 2equal*
operations).
1Cvt* may also appear as a return type (as in the create
operation); here it
indicates that a returned object will be changed into an object of
abstract type.
Whether cvt appears as the type of an
argument or as a return type, it stipulates a ``conversion'' of
viewpoint between the external abstract type and the internal
representation type.  1Cvt* can be used only within a cluster,
and conversion can be done only between the single abstract
type being defined and the (single) representation type.
.foot
1Cvt* corresponds to Morris' seal and unseal [Mor73].
.efoot
	The implementation of the 2rational_number* operations
is straightforward.
The 2create* procedure accepts as arguments integers
representing the numerator and denominator of the
2rational_number*.
A zero denominator is erroneous.  (This error would
be reported to the caller by means of the CLU exception
handling mechanism, which is described in [LCS75]; we have
omitted details of exception handling here.)  The implementation
represents a negative 2rational_number* by a negative numerator
and a positive denominator.  The representation object is created by
use of the record constructor:
.sp .5
		{num: n, denom: d}
.sp .5
This object is converted to a 2rational_number* as it
is returned.
	The 2equal* procedure reduces its arguments
to lowest common terms by using the procedure 2reduce*.
2reduce* is an internal procedure of the cluster and is
not available for use outside.  Note that reduce makes use
of an external procedure, 2gcd*.  Note also that 2reduce*
modifies the representation of its 2rational_number* argument;
this modification is a side effect of testing for equality,
but it is not a detectable side effect since all other
2rational_number* operations are unaffected by it.
.foot
This is not strictly true for the implementation shown for
2plus*, since integer overflow might occur for
unreduced arguments but not for reduced ones.  The correct
implementation for plus would detect the overflow (using the CLU
exception handling mechanism), reduce the arguments and try again.
.efoot
	Note that both 2copy* and 2equal* must be
implemented as part of the 2rational_number* cluster.
With abstract data types, it is not possible for the compiler
to automatically generate implementations for these operations.
This is especially apparent for the 2equal* operation, because
a single 2rational_number* can have many representations.
.section "Mutable and Constant Objects"
	The semantics of CLU are similar to LISP [McC62] rather
than to Algol-like languages.  In particular, CLU variables
do 2not* contain objects, but are merely names for objects.
Assignment changes the object named by a variable; assignment
results in sharing of objects, but variables are 2never*
shared.  CLU semantics are discussed in Appendix B.
	There are two different kinds of objects in CLU:
mutable objects and constant objects.  A 2mutable* object has a state which may be modified by certain operations belonging to the
object's type in a way that is detectable by other operations
of the type.
Thus a mutable object may
exhibit time-varying behavior.  Records are examples of mutable objects.
The record update operations (written as 2r*.2s*@:=@2v*
in the example programs) change the state of record objects, and
the change is detectable by
subsequent applications of the select operations (written as 2r*.2s*).
	Objects that do not exhibit time-varying behavior are called
2constants*.  Examples of constants include integers, booleans,
characters, strings, and 2rational_number*.
	If a mutable object is shared by more than one variable,
then a modification made via one variable will
be visible as a side-effect via the other variables.
Communication
via shared mutable objects is most beneficial in the context
of procedure invocation, described below.  No communication is possible
via shared constant objects.
.section "A Simple Iterator"
	The purpose of many loops is to perform some action
on all of the objects in a collection.
For such loops,
it is often useful to separate the
selection of the next object
from the action performed on that object.
CLU provides a control abstraction that permits
a complete decomposition of the two activities.
The for statement available in many programming languages
provides a limited ability in this direction:
it allows iteration over ranges of integers.
The CLU for statement
allows iteration over collections of any
type of object.
The selection of the next object in the collection
is done by a user-defined 2iterator*.
The iterator produces the objects in the collection one at a time (the entire collection need not physically exist);
the objects are then consumed by the for statement.
.nr rra0 current_figure
	We illustrate the use of iterators by means of a simple
example.  Figure rra0 shows an iterator
called string_chars, which produces the characters in a string in
the order in which they appear.
.begin_figure "Use and definition of a simple iterator."
.table
.new_font 0
count_numeric = proc (s: string) returns (int);
	count: int := 0;
	for c: char in string_chars (s) do
		if char_is_numeric (c)
			then count := count + 1;
			end;
		end;
	return count;
	end count_numeric;

string_chars = iter (s: string) yields (char);
	index: int := 1;
	limit: int := string$size (s);
	while index <= limit do
		yield string$fetch (s, index);
		index := index + 1;
		end;
	end string_chars;
.ns
.end_table
.finish_figure
This iterator uses string operations 2size@(s)*,
which tells how many characters are in the string s,
and 2fetch@(s,@n)*,
which returns the nth character in the string s
(provided the integer n is greater than zero
and does not exceed the size of the string).
	The general form of the CLU for statement is
.table
	for declarations in iterator-invocation
		while expression do body end;
.end_table
An example of the use of the for statement
occurs in the count_numeric procedure
(see Figure rra0), which contains a loop
that counts the number of numeric characters in a string.
Note that the details of how the characters are obtained
from the string are entirely contained
in the definition of the iterator.
	Iterators work as follows:
A for statement initially invokes an iterator,
passing it some arguments.
Each time a yield statement is executed in the iterator,
the objects yielded
.foot
One or more objects may be yielded,
but the number and types of objects yielded each time by an iterator
must agree with the number and types of variables in
a for statement using the iterator.
.efoot
are assigned to the variables declared in the for statement
(following the reserved word for).
Then the test expression
(following the reserved word while),
if any, is evaluated.
If the result is false,
then the loop is terminated.
If the result is true,
then the loop body is executed.
Then the iterator is resumed at the statement
following the yield statement,
in the same environment as when the objects were yielded.
When the iterator terminates, either by an explicit
return statement (which must not return any objects)
or by completing the execution of the body,
then the invoking for statement terminates.
	For example, suppose that string_chars is invoked
with the string ``a3''.
The first character yielded is `a'.
At this point within string_chars, index@=@1 and limit@=@2.
Next the body of the for statement is performed.
Since the character `a' is not numeric,
count remains at 0.
Next string_chars is resumed at the statement after the yield
statement, and when resumed, index@=@1 and limit@=@2.
Then index is assigned 2,
and the character `3' is selected from the string and yielded.
Since `3' is numeric, count becomes@1.
Then string_chars is resumed,
with index@=@2 and limit@=@2, and index is incremented,
which causes the while loop to terminate,
and the return statement to be executed.
This terminates both the iterator and the
for statement, with control resuming at the statement
after the for statement,
and count@=@1.
	While iterators are useful in general,
they are especially valuable in conjunction with data abstractions
that are collections of objects (such as sets and arrays).
Iterators afford users of such abstractions access to all objects
in the collection,
while exposing a minimum of detail.
Several iterators may be included in a data abstraction.
Where the order of obtaining the objects is important,
different iterators may provide different orders.
For example, CLU provides an array iterator, elements,
which enumerates the elements of an array
from the low to high bounds; a reverse_elements iterator,
which enumerates the elements from the high
to low bounds, is also provided.
.bp
.chapter "Specifications for Data Abstractions"
	Our study of specification techniques for data abstractions 
has identified three different
methods [24, 25]: axiomatic, state machine, or
abstract model.
	The most promising form of axiomatic specification is
the algebraic technique, developed by Zilles at M. I. T.
[Zilles74], using some recent results in algebra [Birkhoff70].
The technique was investigated further by Guttag at the
University of Toronto [Guttag75], who worked out a criterion
for recognizing a "sufficiently complete" axiomatization of a
data type.  Further work on verification of data types using this
technique is in progress at ISI [Guttag76a,@Guttag76b].
	The state machine approach was first proposed by Parnas
[Parspec].  The approach as originally proposed was informal.
Work on formalization of this technique is underway
[SRI,@Parnas75].
	The abstract model approach has been used informally in
[HoareCDR].  Work on this technique is in progress under the
current grant.  Some work in this area has also been done by
[Wul84].
	In [Lis75], we developed some criteria for judging the
desirability of a specification technique for data abstractions.
Among the criteria were the ease of construction and
understandability of the specifications.  We believe that the
abstract model specification technique is best with respect to
these criteria; this is the motivation for our work on this
technique.
	In the remainder of this section, we discuss our work on
the abstract model technique.  We have worked out the theoretical
justification for this technique (which is also algebraic in
nature).  We have investigated the structure of the
specifications, and have arrived at a form that, we believe,
makes it easier to build specifications.  We have also developed
criteria for establishing consistency and completeness of
abstract model specifications (analogous to those developed by
Guttag [Guttag75] for algebraic specifications).  These
criteria are helpful in evaluating the specification of an
abstraction, since a specification that is not well formed
cannot define any behavior, let alone the intended behavior.
.section "Abstract Model Specifications"
	A sample specification using the abstract model
technique is shown in Figure current_figure!.
.begin_figure "Sample Abstract Model Specification"
.fi l
.ls 1
.sp
1Type* FILE[RECORD] 1is*
.sp
1Interface:*
	create() --> FILE,
	append(FILE, RECORD) --> FILE u {1error*(append-in-middle)},
	reset(FILE) --> FILE u {1error*(file-empty)},
	skip(FILE, 1int*) --> FILE u {1error*(skip-past-eof), 1error*(reverse-skip)},
	read(FILE) --> RECORD u {1error*(file-empty)},
	eof(FILE) --> 1bool*,
.sp
1Representation:*	1tuple*[ptr: 1int*, s: 1sequence*[RECORD]],
.sp
	1Invariant:*	1For all* f: FILE;
		0 le f.ptr le length(f.s) & (length(f.s) > 0 ==> f.ptr > 0),
.sp
	1Equivalence:*	1For all* (f1, f2): FILE;
		f1 = f2 eq (f1.ptr = f2.ptr & f1.s = f2.s),
.sp
1Operations:*	1For all* (f, f1, f2): FILE, r: RECORD, n: 1int*;
	create() = tuple[ptr: 0, s: emptyseq()],
	append(f, r) = 1if* f.ptr = length(f.s)
.align
@1then* tuple[ptr: f.ptr + 1, s: addlast(r, f.s)]
 1else* 1error*(append-in-middle),
.un_align
	reset(f) = 1if* length(f.s) > 0
.align
@1then* tuple[ptr: 1, s: f.s]
 1else* 1error*(file-empty),
.un_align
	skip(f, n) =
.align
@1if* n < 0 1then* 1error*(reverse-skip)
 1else if* f.ptr + n > length(f.s) 1then* 1error*(skip-past-eof)
 1else* tuple[ptr: f.ptr + n, s: f.s],
.un_align
	read(f) = 1if* f.ptr = 0
.align
@1then* 1error*(file-empty)
 1else* nth(f.ptr, f.s),
.un_align
	eof(f) eq f.ptr = length(f.s),
.br
1end type*.
.ls 2
.fi b
.finish_figure
A sequential file data type is defined, which can be
written in a restricted way: records can only be appended to a
file, but not deleted or updated.  The files are sequential
because they can only be scanned by starting at the beginning
and spacing forward.
	An abstract model specification has three major parts,
describing the interface, the abstract representation, and the
operations of the data type.
	The 1interface* of a data type consists of the names,
domains, and ranges of its operations.  This information is
singled out because the operations provide the sole access to
the abstract objects of the type.  Thus a program, a proof, or
even the rest of the specification can be checked for type
correctness using only the information contained in the
interface specifications of the data types that are used.  (This
is precisely the information that must be provided by the
declarations in CLU modules, and the CLU compiler checks the
modules for type correctness.)
	The 1abstract representation* is introduced into the
specification solely to provide a framework in which to define
the behavior of the operations of the type, and does 2not*
constrain the class of representations that may be used in the
implementation.  The types used in the abstract representation
are chosen for
simplicity rather than for efficiency.  The primary use of specifications is for communication, and (perhaps) in proofs of program
properties; how well they run as programs is of secondary
interest.  Therefore
simplicity and clarity are important, while hypothetical time
and space requirements are not.
	The 1abstract representation* has three
subcomponents in its specification: the representation type, the
abstract 1invariant*, and the abstract 1equivalence*
relation.  The representation type must be composed from
previously defined types.  We favor using finite sets, sequences,
and tuples to put together known types into new ones.  (Formal
definitions of these families of types are given in Appendix C.)
	Every meaningful abstract object should have a unique
abstract representation, and conversely.  The 1invariant*
describes a restriction on the representation type which
excludes those elements that do not represent any meaningful
abstract object.  (It is similar in this respect to the invariant
of the concrete representation [HoareCDR] used in proving the
correctness of an implementation of a data abstraction.)  The
1equivalence* is a relation stating which pairs of the
representation type represent the same abstract object.  If there
are multiple meaningful representations for each abstract
object, we can take the
entire set (equivalence class) of elements representing an
abstract object to be its unique abstract representation. The
abstract equivalence is important because it specifies precisely
which properties of the representation are being used to model
the abstract type.
	In the example, the state of a file
.foot
Note that we are not modelling files directly, but instead
are modelling file states.  This is necessary because files
are mutable objects.
.efoot
is represented by a
sequence of records, and a pointer into that sequence to indicate
which record is currently being scanned.  Note that the pointer
is a natural number, which by definition cannot be negative,
although it can be zero.  The invariant says that the pointer can
never get past the end of the sequence, and that provided the
file is not empty, the pointer will always point at some record
of the sequence (the first record has index 1).  The equivalence
tells us that each object of the representation type satisfying
the invariant represents a unique file object.
	The operations are defined as functions on the
representation type, in as simple and clear a way
as possible (efficiency does not matter).  Any formal method for defining functions is
acceptable. We will use both McCarthy's recursive conditional
expressions [McCcond], and input/output constraints expressed in the
predicate calculus, as we find most convenient.
	In the example, all of the operations except for 2eof* are
defined using conditional expressions, none of which need be
recursive because of the simplicity of the data abstraction.
2Eof* is defined as a predicate on the representation type,
which happens not to require conditionals or quantifiers.
.section "Consistency and Completeness of Abstract Model Specifications"
	A specification describes the behavior of some
abstraction, and it is important that it describe that behavior
correctly.  While it is clearly not possible to 2prove* that
the specification is correct, it is possible, by analyzing
properties of the specification, to identify problems, or
alternatively to gain confidence in the correctness of the
specification.  Guttag [Guttag75] has done some work along these
lines for algebraic specifications.  We discuss below some
criteria for abstract model specifications that we have
developed for this purpose.
	A well formed abstract model specification must satisfy
the following requirements:
.list
1.  Type Correctness.  The definitions of the operations
must be consistent with the interface specifications, and
all expressions of previously defined types must be
consistent with the interface specifications of those types. 
.next
2.  Representation consistency.
.br
A.  The invariant must be a well
formed unary predicate on the representation type.
.br
B.  The equivalence must be a well formed binary predicate on the
representation type, and it must define an equivalence relation
(it must be reflexive, symmetric, and transitive). 
.next
3.  Totality. Every operation mentioned in the interface
specification must be uniquely defined for all elements of the
representation type satisfying the invariant relation.
.next
4.  Closure. Every element in the intersection of the range of an
operation with the representation type must satisfy the
invariant relation.
.next
5.  Congruence. Every operation must be consistent with the
representation equivalence, which means that equivalent inputs
must result in equivalent outputs.
.end_list
Some of these requirements are easier to check than others.
The bulk of the type correctness check can be performed
by a fairly simple algorithm, such as the one used by the
CLU compiler.  (Showing that no error values are produced,
except for those described in the interface specifications,
may require some program analysis.)  At the other extreme,
deciding whether a recursive function is total is undecidable
in the general case, although there are well known techniques
for proving termination which apply to most programs that
are designed to terminate [Sites].  A moderately
powerful theorem proving facility is needed to demonstrate
that the requirements are met, comparable to the
facility required for verifying that programs meet their
specifications.
.section "Verifying Implementations Specified by Abstract Models"
	The criterion for the correctness of a cluster
implementing a data type is that it must behave in a way
indistinguishable from the abstract data type, given that only
the operations of the data type are allowed to have access to
the concrete representation (the 1rep* of a CLU cluster) or to
produce objects of the type.  Given our algebraic framework, we
can express this idea formally by saying that an implementation
of a data type is correct if there exists a homomorphism from
the implementation to the abstract type that reduces to the
identity transformation when restricted to the previously defined types.
This implies that any sequence of operations resulting in a
value in any previously defined type must yield the same result
for both the abstract type and for the alleged implementation of
that type.  This is obviously a necessary condition for
correctness.  It is also sufficient, because the only
way the external world can access objects of the type is by
means of the operations of the data type, so that the only way
to export any information from the data type is by means of
operations whose results belong to a different type.
	An implementation of a data abstraction will have the
same structure as an abstract model specification, although
the invarient and the equivalence will be implicit.  
In order to verify the correctness of the implementation,
the concrete invarient must be given.  Then it is
possible to check that the implementation is well formed;
a well formed implementation must satisfy the constraints of the previous section,
excluding those restrictions dealing with the equivalence (2B@and@5).
	The correctness of a well formed implementation is
established by defining a mapping 2h* from the objects and
operations of the implementation to the objects and operations
of the abstract data type, showing that the mapping is well
defined, and showing that it has the homomorphism property.
Intuitively, the function 2h* maps a concrete object into the
abstract object it represents. 
Specifically, we must show that:
.ilist 3
1. 	The function 2h* is defined for every element satisfying the
concrete invariant.
.next
2. 	The image of any object satisfying the concrete
invariant under 2h* must satisfy the abstract invariant (of
the abstract model specification).
.next
3. 	The function 2h* must be the identity transformation on all
previously defined types.
.next
4. 	The function 2h* must satisfy the homomorphism property.
.end_list
The homomorphism property is a standard algebraic concept, and
has been developed in the precise sense we mean in the theory of
many sorted algebras [Birkhoff70].  The definition is complicated
if we have to allow for operations with an arbitrary number of
arguments of several different types.  For the case of an
operation with one argument, the homomorphism property is just
2h*(2o*c(2x*))@=@2o*a(2h*(2x*)), for all 2x* satisfying
the concrete invariant, where 2o*a is an abstract operation (a
function mapping abstract objects to abstract objects), and
2o*c is the corresponding concrete operation (in the
implementation, mapping concrete objects to concrete objects).
.bp
.chapter "Proposed Research"
	During the next period of support, we propose to
continue our study of abstraction mechanisms that
support well structured programs, and of specification
and verification techniques for these programs.  Our goal
is to develop specification and verification to a state
where they are applicable to practical programs, involving
sharing of mutable objects and parallelism.  In particular,
we are interested in the specification and verification
of well-structured programs written in CLU (or a variant
of CLU, as discussed below).
	CLU is well-suited to verification because of
the strong distinction
made in the language between an abstraction and its implementation.
This is especially interesting in the
case of data abstractions.  Here the distinction is based on the constraint, inherent in CLU
and enforced by the compiler, that only the operations
of the abstraction may access the representations of the objects.
	The result of the distinction is that
an abstraction can be used without knowledge
of its implementation, and implemented
without knowledge of its use. The proof of a CLU program
can be similarly decomposed: a module (procedure, cluster,
iterator) using an abstraction can be proved separately from
the module that implements the abstraction.
.foot
The distinction between an abstraction and its implementation
also aids program modification and maintenance (see
[CSG144]).
.efoot
Decomposition of the proof is essential for
program proving, which is practical only for small
programs (like CLU modules).  Note that when the CLU
compiler does type checking, it is, in addition
to enforcing the constraint that permits the proof
to be decomposed, also performing a small part of the
actual proof.
	Although the constraints on CLU make verification
theoretically possible, much work remains to be done.
For example, as
was discussed in Section 2.2, CLU programs can share mutable
objects; the kind of sharing that can take place in CLU
programs is similar in power to what can be done with pointers,
although it is better-structured.  Little attention has been given
to the specification and verification of such programs, although
some work has been done [Laventhal, Suzuki, Burstall].
Nevertheless, this kind of sharing occurs in almost all large
programs, either for reasons of efficiency [8] or
because it is necessary when there is parallelism.  Therefore,
it is important that specification and verification
techniques be extended to handle such programs.
	The work to be done falls into two separate
but related pieces:
.ilist 3
1.  Modification and Extension of CLU.  We will use CLU
as a vehicle for studying the utility and complexity of
abstraction mechanisms.  Modifications will be made to CLU
to enhance the verifiability of CLU programs.  CLU will
be extended to support parallel programming.
.next
2.  Specification and Verification of CLU Programs.
We will continue our study of specification and
verification techniques for data abstractions, and
we will extend our study of program verification to include
control abstractions and programs with sharing and parallelism.
.end_list
In the following sections, we discuss the research in more detail.
.section "Continued Study of CLU"
	Among the topics in need of further study are the
following:
.br
1Improving the Verifiability of CLU Programs*
	As was discussed above, CLU contributes to
program verifiability by enforcing constraints that permit
each module to be verified indepedently of the others.
We believe that this is a major benefit.  Nevertheless, we
believe that further study will enable us to improve the
verifiability of CLU programs.  For example, analysis of
axioms for CLU constructs may enable us to simplify the
language.
	One problem that needs more work is the
interaction of sharing and mutability.
In Section 2.2 we
discussed mutable objects, such as records, which exhibit
time-varying behavior.
Mutability is of interest in the presence of sharing;
when a mutable object is shared between two variables,
changes made to the object through one of the variables
will be visible through the others.
	Our goal with respect to sharing and mutability
is to limit the interaction as much as possible, and to
make it evident in CLU programs when sharing or mutability
is introduced by a data abstraction.  The interaction of
mutability and sharing is an old problem, but
data abstractions introduce some new difficulties.  For example,
it is not possible for the CLU compiler to always distinguish
mutable types from constant ones, since this
question is, in general, undecidable.  For example, the
2rational_number* data abstraction shown in Section 2.1
is constant, even though it modifies the representation
of 2rational_number* objects as a side-effect of
testing two rationals for equality.
	Sharing between objects of abstract type also
causes difficulty, both in detecting whether sharing
occurs, and in writing programs that try to avoid
sharing.  Sharing can be avoided in CLU only by
copying objects, but since the copy operation is
user-defined, there is no guarantee that sharing is
actually avoided.
.foot
This is also true in Alphard [Wul84].
.efoot
	To limit the interaction, we intend to
investigate changes to CLU that constrain the introduction
of mutability and sharing.  One approach for limiting
sharing that deserves
study here is the idea of collections introduced in
Euclid [Euclid].  Mutability can be made more
evident by including such information as declarations
about a data abstraction, provided the compiler can enforce
the declarations.  We have done some investigation of
algorithms to be used by the compiler [Atk76].  The
algorithms are safe in that a cluster found to be constant
by the algorithm really is constant, but sometimes the
algorithm determines a cluster to be 
mutable when it is not.
.br
1Analysis of Data Abstraction Mechanisms*
	Languages supporting data abstractions differ
from one another, and it is important to understand
the differences and what they mean.  We propose to
study the relationships among three such languages:
CLU,
Alphard [Wul84], and SIMULA 67 [Dah70].
Issues to be investigated include the following:
.ilist 3
1.  In Simula, operations belong to objects, while in
CLU and Alphard they belong to the type.  This means that
operations like "+", which operate on two or more objects
of the type being defined, are handled more naturally in
CLU and Alphard.  However, in SIMULA, multiple
implementations of a type can exist in the same program.
.next
2.  When implementing a data abstraction in CLU,
the programmer provides concrete representations for the
objects.  In Alphard, however, it is necessary
to implement 2variables* containing the
objects, rather than the objects themselves.
For example,
in both CLU and Alphard, assignment involving copying
of the value on the right-hand side is user defined.  In
Alphard, this is done with the store operation; an example
for the 2rational_number* data abstraction is:
.sp .5
			store: 2rational_number* X 2rational_number* -->
.sp .5
This operation modifies the 2storage* associated with
the first 2rational_number*.  In CLU, the 2copy* operation
is used instead:
.sp .5
			copy: 2rational_number*  -->  2rational_number*
.sp .5
The programmer in CLU is not concerned with storage, but merely
with producing an object which is equal to the argument object.
.br
			We would like to understand the significance of this
difference.  Also, the difference arises from the semantics
of the two languages; CLU is heap oriented, while Alphard is
stack oriented.  We would like to understand whether
implementation of variables is a necessary consequence of stack
orientation.  We believe that
it is not.
.end_list
.br
1Parallelism*
	Our goal in extending CLU to parallel programs is
to support dynamic creation of
processes and to provide a synchronization method that is
unlikely to lead to deadlock.
In adding parallelism to CLU, we will make use of
existing constructs as much as possible.  Since the best
mechanisms for controlling sharing in the presence of
parallelism (monitors [HoareMON], serializers [Hewitt], path
expressions [Habermann]) describe how to control the use of
abstract data objects, we are in a good position to benefit
from prior work.
	For example, suppose that we decide to make use
of monitors.  To protect a data abstraction, it is not
sufficient to simply replace a cluster with a monitor, both
because of loss of concurrency and because deadlocks
are likely to arise.  For example, if data abstraction A
is represented by data abstraction B, and both A and B
are implemented by monitors, then the following situation
can arise:
.list
1.  Operations of B are called only from operations of A.
.next
2.  A call on A$op1 results in a call on B$op2.
.next
3.  B$op2 must wait.  This releases monitor B, but
not monitor A.
.end_list
Now we have a deadlock because, since monitor A is locked,
no more invocations of operations in B are possible. 
.nr pro current_figure
	Figure 4 contains a diagram that
illustrates the desired structure for protecting
a data abstraction.  The implementation of a 2protected_data_base*
has two parts:  a protection part and a data part. The
protection part could be implemented using a monitor.
.begin_figure "The Proper Structure for Protecting a Data Base."
.table

		protected_data_base



		protection	data


.end_table
.finish_figure
	For example, Figure 5 shows the implementation of a
2protected_data_base* with multiple concurrent readers.
Part of the representation is a type of monitor,
2readers_priority*, which will be used to control access
to the data base; the remainder is the data base itself.
Note that each 2protected_data_base* will have its own
instance of the monitor (created at the time the
2protected_data_base* is created).  Now observe the
2read* operation.  It makes use of two monitor operations,
2startread* and 2endread*, to ensure that reading the
data base (which is accomplished by operation T$op1) occurs
in accordance with the decisions of the monitor.  The 2write*
operation has a similar structure.
.nr mon current_figure
	Using monitors in this way has
several advantages:  The user of a 2protected_data_base* object
is provided with operations (2read* and 2write*) at
the proper level of abstraction.
.begin_figure "Example of Use of Monitors."
.table
protected_data_base = 1cluster is* create, read, write;

rp = 1monitor* readers_priority;

1rep* = 1record* [m: rp, d: T]

create = 1proc*( ) 1returns* (1cvt*);
	1return* {m: rp$create(), d: T$create()};
	1end* create;

read = 1proc*(x: 1cvt*) 1returns*(S);
	rp$startread(x.m);
	y: S := T$op1(x.d);
	rp$endread(x.m);
	1return* y;
	1end* read;

write = 1proc*(x: 1cvt*, y: S);
	rp$startwrite(x.m);
	T$op2(x.d, y);
	rp$endwrite(x.m);
	1end* write;

1end* protected_data_base;
.ns
.end_table
.finish_figure
The monitor is accessible only within the 2protected_data_base*
cluster, so that it is easy to prove that the monitor operations
are used correctly.  Deadlock problems of the sort discussed
above are avoided.  And the implementation of 2protected_data_base*
can be decomposed into the monitor (which controls the access) and
the data base operations themselves (type T in the example).  Note
that the 2readers_priority* monitor can be used to protect
many different types of objects; also, 2protected_data_base*
could easily be modified to use a different priority scheme.
	In addition to monitors, serializers [Hewitt] can be used to
achieve the structure in Figure pro.  It remains to be
determined which method is better.
.section "Specification and Verification of CLU Programs"
	Work to be done in this area includes the following topics:
.br
1Data Abstractions*
	As we discussed in Section 3, we have been studying
the abstract model technique for specifying data
abstractions.  We propose to continue this work.  Our goal
is to achieve a complete understanding of this technique
and its relationship to other techniques (axiomatic
[12] and state machine [31, 33]).
	One area in need of further study is how to prove
the correctness of an implementation of a data abstraction.
The general method of constructing a homomorphism from the concrete
algebra to the abstract one was discussed in Section 3.3.  However, there are several
ways of going about this proof, and it is not clear which
is simplest.  This problem has been studied by Hoare [HoareCDR]
and more recently by Wulf, London and Shaw [Wul84].  In the
latter approach, the programmer must supply input/output
assertions for each abstract operation, a mapping from
representation objects to abstract objects, an invariant
on the representation, and, in addition, input/output assertions for
each concrete operation.  This last step is difficult, and
seems unnecessary.  One method of avoiding it might be
to produce the concrete input/output assertions by using
predicate transformers [Dij76] on the other information.
	We believe that abstract model specifications
are easier to construct and understand than
axiomatic ones, and also easier to check for well-formedness.
However, axiomatic specifications appear to be better for
verification, both for proofs of implementation
and for proofs of use.  If this is true, we believe that it
would be worthwhile for a specification language to support
both methods: abstract model specifications would be used
to describe the abstraction initially, and later axioms
would be proved about the model.
.br
1Specification and Verification of Control Abstractions*
	The use of iterators contributes to the correctness
of software because the method of obtaining objects from a
collection need be implemented only once, rather than many
times. In order to verify programs using iterators it is
necessary to develop techniques for specifying the behavior
of iterators, and proof rules for verifying that an
iterator satisfies its specification, and for verifying
the correctness of a 1for* statement.  Work that is
relevant here includes [Clint, Hfor, Wulfit].
	We believe that a good way to specify the
behavior of an 1iterator* is to view it as mapping
its inputs into a sequence of outputs, since this is
close to the intuitive understanding of what an
iterator does.  (For example, the 2string_chars*
iterator shown in Figure 2 maps a string into a
sequence of characters.)  Then the 1yield* statement
can be described in terms of sequence concatenation, and the
1for* statement at each iteration partitions the
sequence into "past" objects (those already processed),
the "current" object, and "future" objects.  The
iterator is correct if the sequence of "past" objects at
the time the
iterator returns (completes processing) is equal to the desired sequence.
	One of the problems with iterators is that
various kinds of misbehavior are possible.  For example,
an iterator ought to be used purely for control; in
particular, it should not modify its input arguments.
In addition, the body of the 1for* statement must
not modify the inputs to the iterator in a way that
causes the wrong sequence to result. 
.foot
Note that this is not a new problem; within a loop body there
are always constraints on what can legally be done if
the loop is to terminate.
.efoot
For example, CLU provides an 2elements* iterator
for arrays, which produces the elements from the low bound
to the high bound.  Within a 1for* statement using this
iterator, array operations that do not modify the array
are clearly permitted.  Certain modification operations
are also permitted, for example, to remove the
element just yielded from the array.  It is important
to state exactly the conditions under which an iterator
will function correctly, so that the proof of use of the
iterator can be decoupled from the iterator implementation.
.br
1Error Handling*
	In order to construct reliable software, it is
necessary for programs to be well-behaved in the presence
of erroneous data.  Proper handling of errors impacts
both programming language design and specification and
verification techniques.
	It is not difficult to develop syntactic
methods for describing error handling (e.g., see Figure 3).
However, the methods used must be well-matched to the way
that programs deal with errors.  In particular, we
believe that both the specification technique and the
programming language should be based on the same model
of computation with respect to error handling, and
that this model should be chosen to be convenient
for programming.  In addition, techniques for
verifying use and implementation of programs with
error handling need to be developed.
	For example, we believe that the new approach
to error handling for algebraic specifications 
described in [12] is poorly matched to programs.
The approach taken requires that the compiler
guarantee that only legal invocations are made.
This requirement is impractical because often
knowledge of the implementation is needed in order
to determine whether an invocation is legal.  For
example, this is the case in the skip operation
for files (see Figure 3).
	A technique that does provide a reasonable
approach to errors is that of Parnas [32], but the
error handling part of this technique has not been
formalized.  In addition, CLU provides an alternative
approach to errors that we believe to be better.
CLU supports error handling by permitting a
procedure to either return normally or terminate in
one of several error conditions, which cause control to
pass to corresponding alternative return points.
All return points are within the invoking procedure;
associated with each return point is code to handle
the error [20].  This gives the programmer a disciplined
framework for dealing with runtime errors.
	We believe that the approach taken in CLU
to error handling is well matched to the needs of
programmers, and propose to develop specification
and verification techniques based on the CLU mechanism.
This implies that the idea of a procedure having many
return points must be formalized.  We have
investigated specifications
based on the CLU mechanism [Schaffert], but
more work is needed in this area.		
.br
1Mutable Objects and Sharing*
	As with error handling, it is not difficult to
develop syntactic methods for specifying mutable
data abstractions, but problems arise in developing
verification techniques for programs using and
implementing such abstractions.
Proof rules as defined by Hoare [HoareAx] are
inadequate for dealing with general mutable data types.
Hoare's technique is designed for an environment where the
only mutable objects are cells statically associated with
distinct program variables.  Hoare discusses some of the
difficulties associated with the potential sharing introduced
by the call-by-reference mechanism in [Hoare71].
The problem becomes even more severe in the presence of
general pointers.
	The difficulty arising from the use of
general pointers is that there may be several names
for the same object, both in the specification,
and in the intermediate assertions used in carrying out
the proof.  Whenever a change is made to the object
through one of the names, this change must be
reflected to all the other names for the object.  This
can be difficult to do in general, and the more sharing
there is, the more difficult it becomes.
	A partial solution to this problem is to limit
the programming language to restrict sharing and make it
obvious when it occurs.  This is the approach taken in the
Euclid language [Euclid] and, as we mentioned in Section
4.1, we are interested in applying the ideas in Euclid
to CLU.
	However, we believe that sharing of mutable
objects is desirable in some programs,
so it is necessary to develop techniques for verifying such
programs. An approach that we are investigating and believe to be promising is to write
specifications in terms of objects and their states rather
than in terms of program variables and their states [Schaffert].
.sp .5
1Parallel Programs*
	In specifying parallel programs, as in specifying
sequential programs, it is important to be able to specify
the behavior of 2modules*.  Modules in parallel programs
are most often implementations of data abstractions.  The
specification techniques discussed earlier, however, are
inadequate in the presence of parallelism, since they
provide no means for describing such concepts as
"starvation" and "priority."
	In studying techniques for specifying the behavior
of parallel programs [Lis76], we found that existing
techniques are either state oriented (as in the work of
Owicki [Owicki]),
or event oriented (as in the work of Greif [Greif75]).
Both techniques can be adapted (we believe) to modular
structure, but specifications in the event approach can
be readily constructed in a modular fashion.  Therefore,
we propose to adapt the event approach 
for use with the proposed extension of CLU.
	Greif's work is cast in terms of the actor formalism of Hewitt
[HewGr], where computations are viewed as structures of
primitive events, and where each primitive event is the
passing of a message from one actor to another.  In order
to make event oriented specifications practical, it
is necessary to establish the correspondence between the
formalism and a more conventional programming language semantics.  By
recasting the theory in terms of CLU, we hope to uncover
the problems involved in this process, and to establish
a secure framework for verifying parallel CLU programs.
.bp
.chapter "Personnel"
	This research will be under the direct supervision
of the principal investigator, Dr. Liskov, and will
constitute 25% of Dr. Liskov's support.
	Included in the budget is support for an
unspecified faculty member.  This position is intended to
be filled by a recent Ph.D candidate in the area of
specification and verification, who would join the M.I.T.
faculty as an assistant professor in September, 1977.
.bp
.chapter "Relation to Other Grants and Proposals"
	This proposal is for the main support of the
theoretical aspects of the Principal Investigator's own
research program, and is being submitted to no other
agency.
	Other support relevant to this proposal, but not
overlapping with it, is provided by the Advanced Research
Projects Agency of the Department of Defense under Office of
Naval Research contract number N00014-75-C-0661.  ARPA provides support for practical
aspects of the Principal Investigator's research, including
implementation of the CLU language and system.
.bp
3Appendix A.  Examples of CLU Programs*
	This appendix gives an extensive example illustrating
the use of abstractions, and the use of the abstraction
mechanisms in CLU.
	Consider the following problem:
Given some document, we wish to compute the total
number of words in the document, and, for each
distinct word, the number of times that it
occurs and its frequency.  The document will be
represented as a sequence of characters.  A word is any sequence of
alphabetic characters.  Adjacent words are
separated by one or more non-alphabetic
characters such as spaces, punctuation, or newline
characters.  In counting distinct words, the
difference between upper and lower case letters should
be ignored.
	The output is also to be a sequence of characters,
divided into lines.  Successive lines should contain an alphabetical
list of all the distinct words in the document,
one word per line.  Accompanying each word should
be the total number of occurrences and the
frequency of occurrence.  For example:
.table
.ta 8 20 28
	a:	2/57	3.509%	      
	access:	1/57	1.754%
	and:	2/57	3.509%
	        . . .
.rtabs
.end_table
	Specifically, we are required to write the
procedure count_words, which accepts two arguments:
an instream and an outstream.  The former is the
source of the document to be processed, and the latter
is the destination of the required output.  The form of
this procedure will be
.table
	count_words = proc (: instream, o: outstream);
		...
		end count_words;
.end_table
Note that count_words does not return any results;
its only effect is a modification of i (reading the entire
document) and of o (printing the required statistics).
	2Instream* and outstream are abstract types.
An instream i contains a sequence of characters.  Of the primitive
operations on instreams, only two will be of interest to us.
2Empty@(i)* returns true if there are no characters available
in i, and returns false otherwise.
2Next@(i)* removes the first character from the sequence
and returns it.  Invoking the next operation on an empty instream is an
error.
.foot
The CLU error handling mechanism is discussed in [1].
.efoot
An outstream also contains a sequence of
characters.  The interesting operation on outstreams is
2put_string@(s,@o)*,
which appends the string s to the existing sequence of characters
in o.
	Now consider how we might implement count_words.  Since the
main purpose of count_words is to process words, we begin by deciding
how to handle words.  Two alternatives are possible:  to define
a data abstraction for this purpose, or to use (a subset of)
strings directly.  We choose the latter, since
words are very similar to strings.  Note, however, that this
decision is not clear cut, and that we have lost some security
by this choice, since not all strings are words.  In
particular, we will consider only strings consisting of lower
case, alphabetic characters to be legal words.  
	Next, we investigate how to scan the document.
Reading a word
involves knowledge about the exact way in which words
occur in the input stream.  We choose to isolate this
information in a procedural abstraction, called next_word,
which takes in the instream i and returns the next word
(converted to lower case characters) in the document.  If
there are no more words, next_word must communicate this
fact to count_words.  A simple way to do this is by returning
an ``end of document'' word, one which is recognizably 
distinct from any other word.  A suitable choice for the
``end of document'' word is the empty string.
	It is clear that in count_words we must scan the
entire document before we can print our results, and
therefore, we need some receptacle
to retain information about words between these two
actions (scanning and printing).  Recording the
information gained in the scan and organizing it
for easy printing will probably be fairly complex.
Therefore, we will defer such considerations until later
by introducing a data abstraction wordbag with the
appropriate properties.  In particular, wordbag provides
three operations:  create, which creates an empty wordbag;
insert, which adds a word to the wordbag; and print, which
prints the desired statistical information about the words
in the wordbag.
.nr count_words current_figure
	The implementation of count_words is shown in
Figure count_words.  This procedure declares four variables,
.begin_figure "The count_words procedure."
.table
count_words = proc (i: instream, o: outstream);

	% create an empty wordbag
	wb: wordbag := wordbag$create ();

	% scan document, adding each word found to wb
	w: string := next_word (i);
	while w ~= ``'' do
		wordbag$insert (wb, w);
		w := next_word (i);
		end;

	% print the wordbag
	wordbag$print (wb, o);

	end count_words;
.ns
.end_table
.finish_figure
i, o, wb, and w.  The first two denote the instream and
outstream that are passed as arguments to count_words.
The third, wb, denotes the wordbag used to hold
the words read so far,
and the fourth, w, the word
currently being processed.
The symbol ``%'' marks the rest of the line as a comment.
The symbol ``~='' stands for the not-equal operation.
The while statement is entirely conventional.
	Operations of a data abstraction are called by
means of an operation call in which both the type and
the operation name are specified.  Three examples appear
in count_words:  2wordbag$create@()*,
2wordbag$insert@(wb,@w)* and 2wordbag$print@(wb,@o)*.
The CLU system provides a mechanism that avoids conflicts
between names of abstractions; this is discussed in
Section library.
However, operations of two different data abstractions may have
the same name;
the compound form serves to resolve this ambiguity.
Although the ambiguity could in most cases be resolved by context,
we have found in using CLU that the compound
form enhances the readability of programs.
.nr next_word current_figure
	The implementation of next_word is shown in
Figure next_word.
.begin_figure "The next_word procedure."
.table
next_word = proc (i: instream) returns (string);

	c: char := 1' '*;

	% scan for first alphabetic character
	while ~alpha (c) do
		if instream$empty (i)
			then return "";
			end;
		c := instream$next (i);
		end;

	% accumulate characters in word
	w: string := "";
	while alpha (c) do
		w := string$append (w, c);
		if instream$empty (i)
			then return w;
			end;
		c := instream$next (i);
		end;

	return w;	% the non-alphabetic character c is lost

	end next_word;
.ns
.end_table
.finish_figure
The symbol ``~'' stands for boolean negation; the 2string$append*
operation appends a character to a string (it does 2not* modify
the string argument).
Note the use of the instream operations
2next* and 2empty*.  Note also that two additional procedures
have been used:  2alpha@(c)*, which tests whether a character
is alphabetic or not, and 2lower_case@(c)*, which returns
the lowercase version of a character.
The implementations of these procedures are not shown in the paper.  
	Now we must implement the type wordbag.  The cluster
will have the form
.table
	wordbag = cluster is create, insert, print;
	    	...
		end wordbag;
.end_table
This form expresses the idea that the data abstraction is a set
of operations as well as a set of objects.  The cluster must
provide a representation for objects of the type wordbag and
an implementation for each of the operations.
	The representation that we choose should allow
reasonably efficient storage of words and easy printing,
in alphabetic order, of the words and associated statistics.
Since the total number of words in the document is probably
much larger than the number of distinct words, the
representation of a wordbag should contain only one ``item'' for
each distinct word (along with a multiplicity count), rather
than one ``item'' for each occurrence.  This requires that, at
each insertion, we check whether the new word is already
present in the wordbag.  We would like a representation which
allows the search for a matching ``item'' and the insertion of a
not-previously-present ``item'' to be efficient.
A binary tree representation [2] fits our requirements nicely.
	Thus the main part of the wordbag representation will
consist of a binary tree.
The binary tree is another data abstraction,
wordtree, providing operations very similar to those of wordbag:
2create@()* returns an empty wordtree;
2insert@(tr,@w)* returns a wordtree containing all the
words in the wordtree tr plus the additional word w
(the wordtree tr may be modified in the process);
and 2print@(tr,@n,@o)* prints the contents of the
wordtree tr in alphabetic order on outstream o, along with the
number of occurrences and the frequency (based on a total of
n words).
.nr wordbag current_figure
	The implementation of wordbag is given in Figure wordbag.
.begin_figure "The wordbag cluster."
.table
wordbag = cluster is
	create,		% create an empty bag
	insert,		% insert an element
	print;		% print contents of bag

	rep = record [contents: wordtree, total: int];

create = proc returns (cvt);
	return {contents: wordtree$create (), total: 0};
	end create;

insert = proc (x: cvt, v: string);
	x.contents := wordtree$insert (x.contents, v);
	x.total := x.total + 1;
	end insert;

print = proc (x: cvt, o: outstream);
	wordtree$print (x.contents, x.total, o);
	end print;

end wordbag;
.ns
.end_table
.finish_figure
Following the initial line, which states what the operations
are, we find the definition of the representation selected for
wordbag objects:
.table
	rep = record [contents: wordtree, total: int];
.end_table
This states that a wordbag object has two pieces:  a wordtree,
as explained above, and an integer, which records the total
number of words in the wordbag.  Records are discussed
in Section 2.1.
	The procedures in wordbag are very simple.  2Create*
builds a new instance of the rep by use of the
record constructor
.table
	{contents: wordtree$create ( ), total: 0}
.end_table
Here total is initialized to 0, and contents to the
empty wordtree (by calling the create operation of wordtree).
This rep object is converted to a wordbag object as it
is being returned.  2Insert* and print are implemented directly
in terms of wordtree operations.
.nr wordtree current_figure
.begin_page_figure "The wordtree cluster."
.table
wordtree = cluster is
	create,		% create empty contents
	insert,		% add item to contents
	print;		% print contents

	node = record [value: string, count: int, lesser: wordtree, greater: wordtree];
	rep = oneof [empty: null, non_empty: node];

create = proc returns (cvt);
	return rep$make_empty (nil);
	end create;

insert = proc (x: cvt, v: string) returns (cvt);
	tagcase x
		tag empty:
			n: node := {value: v, count: 1,
				    lesser: wordtree$create (),
				    greater: wordtree$create ()};
			return rep$make_non_empty (n);
		tag non_empty (n: node):
			if v < n.value
			    then n.lesser := wordtree$insert (n.lesser, v);
			    else if v = n.value
			        then n.count := n.count + 1;
			        else n.greater := wordtree$insert (n.greater, v);
			        end;
			    end;
			return x;
		end;
	end insert;

print = proc (x: cvt, total: int, o: outstream);
	tagcase x
		tag empty: ;
		tag non_empty (n: node):
			wordtree$print (n.lesser, total, o);
			print_word (n.value, n.count, total, o);
			wordtree$print (n.greater, total, o);
		end;
	end print;

end wordtree;
.ns
.end_table
.finish_figure
	The implementation of wordtree is shown in
Figure wordtree.  In the wordtree representation, each node
contains a word and the number of times that word has been
inserted into the wordbag, as well as two subtrees.  For any
particular node, the words in the ``lesser'' subtree must
alphabetically precede the word in the node, and the words
in the ``greater'' subtree must follow the word in the node.
This information is described by 
.table
	node = record [value: string, count: int,
		  	lesser: wordtree, greater: wordtree];
.end_table
This is a type definition and simply states that ``node'' is an
abbreviation for the information appearing on the right-hand side of
the equal sign.  (The reserved word rep is similarly used,
as an abbreviation for the representation type.)
	Now consider the representation of wordtrees.  A
non-empty wordtree can be represented as its top node.
An empty wordtree, however, needs no additional information at
all.  The ideal type for this is null,
which has a single data object nil.
So the representation of a wordtree should
be either a node or nil.
This is expressed by
.table
	rep = oneof [empty: null, non-empty: node];
.end_table
	Just as the record is the basic mechanism to form an object
which is a collection of other objects, the oneof is the
basic mechanism to form an object which is ``one of'' a
set of alternatives.  Oneof is CLU's method of forming a
discriminated union, and is somewhat similar to 
a variant component of a record in Pascal [3].
	An object of the type oneof@[s1:@T1 ... sn:@Tn]
can be thought of as a pair.  The ``tag'' component is an
identifier from the set {s1 ... sn}.  The ``value''
component is an object of the type corresponding to the
tag.  That is, if the tag component is si then the
value is some object of type Ti.
	Objects of type oneof@[s1:@T1 ... sn:@Tn]
are created by the operations 2make_si@(x)*,
which take an
object of type Ti and return the pair <si, x>.
Rather than directly using oneof operations to decompose
such an object, CLU provides a special tagcase statement to
do the decomposition:
.table
	tagcase e
		tag s1 (id1: T1):  statements ...
			...
		tag sn (idn: Tn):  statements ...
		end;
.end_table
This statement evaluates the expression 2e*
to obtain an object of type
oneof@[s1:@T1@...@sn:@Tn].
If the tag is si, then the value is assigned to the new variable
idi and the statements following the ith alternative
are executed.  The variable idi is local to those
statements.  If, for some reason, we do not need the
value, we can omit the parenthesized variable declaration.
The important point is that the tagcase
statement protects the programmer from any possibility
of making a type error.
	The reader should now know enough to understand
Figure wordtree.  Note in the create operation the use
of the construction operation 2make_empty*
of the representation type of wordtree
(the discriminated union oneof@[empty:@null,@non-empty:@node])
to create the empty wordtree.
The tagcase statement is used in both insert and print.
Note that if insert is given an empty wordtree, it creates a
new top node for the returned value,
but if insert is given a
non-empty wordtree, it modifies the given wordtree and returns
it.  The insert operation depends on the fact
that record creation dynamically allocates space for the new
record; this was discussed in Section 2.3.
	The print operation uses the
obvious recursive descent.  It makes use of procedure
2print_word@(w,@c,@t,@o)*, which generates a single line of
output on 2o*, consisting of the word 2w*, count 2c*,
the total count 2t*, and the
frequency of occurrence derived from 2c* divided by 2t*.  The
implementation of 2print_word* has been omitted.
	Now we consider a generalization of the 2wordbag*
abstraction.  Although wordbag has properties that are specific to the usage
in count_words,
it also has properties in common with a more general abstraction,
sorted_bag.
A bag is similar to a set
(it is sometimes called a multi-set)
except that an item can appear in a bag many times.
For example, if the integer 1 is inserted in the set {1,2},
the result is the set {1,2},
but if 1 is inserted in the bag {1,2},
the result is the bag {1,1,2}.
A sorted_bag is a bag that affords access
to the items it contains
according to an ordering relation on the items.
	The concept of a sorted_bag is meaningful not only for strings
but for many types of items.
Therefore, we would like to parameterize the sorted_bag abstraction,
the parameter being the type of item to be collected
in the sorted_bag objects.
	Most programming languages provide built-in parameterized
data abstractions.
For example, the concept of an array is a parameterized
data abstraction.
An example of a use of arrays in Pascal is
.table
	1array* 1..n 1of* 1integer*
.end_table
These arrays have two parameters,
one specifying the array bounds (1..n)
and one specifying the type of element in the array (integer).
In CLU we provide mechanisms allowing user-defined
data abstractions (like sorted_bag) to be parameterized.
	In the sorted_bag abstraction,
not all types of items make sense.
Only types that define a total ordering on the objects
are meaningful,
since the sorted_bag abstraction depends on the presence
of this ordering.
In addition,
information about the ordering must be
expressed in a way that is useful for programming.
A natural way to do this is by means of operations of the item type.
Therefore, we require that the item type provide operations
called less_than and equal.
This constraint is expressed in the header for sorted_bag:
.table
	sorted_bag = cluster [t: type] is create, insert, ... 
		where t has less_than, equal: proc [t, t] returns [bool];
.end_table
The item type t is a 2formal parameter* of the sorted_bag
cluster; whenever the sorted_bag abstraction is used,
the item type must be specified as an 2actual parameter*, e.g.,
.table
	sorted_bag[string]
.end_table
The information about required operations
informs the programmer about legitimate uses of sorted_bag.
The compiler will check each use of sorted_bag to ensure
that the item type provides the required operations.
.foot
The where clause specifies exactly the information
that the compiler can check.
Of course, more is required in sorted_bag than simply
operations of appropriate names and functionalities;
these operations must also define a total ordering on the items.
This latter requirement can only be checked by a verification
procedure.
.efoot
	Now that we have decided to define a
sorted_bag abstraction that works for many item types,
we must decide what operations this abstraction provides.
When an abstraction (like wordbag)
is written for a very specific purpose,
it is reasonable to have an operation that assumes some specific
properties.
When the abstraction is intended for a more general use,
less should be assumed.
	The 2print* operation is a case in point.
Printing is only one possible use of the information contained
in a 2sorted_bag*.
It was the only use in the case of 2wordbag*,
so it was reasonable to have a 2print* operation.
However, if 2sorted_bags* are to be generally useful,
there should be some way for the user to obtain
the elements of the 2sorted_bag*; the user can then
perform some action on the elements (for example, print them).
What we would like is an operation on sorted_bags
that makes all of the elements available to the caller
in increasing order.  Such an operation can be provided
by means of an 1iterator* (iterators were discussed
in Section 2.3).
	Now we can describe a minimal set of operations
for sorted_bag.
The operations are create, insert, size, and increasing.
2Create*, insert, and size are procedural abstractions
which respectively 
create a sorted_bag, insert an item into a sorted_bag,
and give the number of items in a sorted_bag.
2Increasing* is a control abstraction
which produces the items in a sorted_bag in increasing order;
each item produced is accompanied by
an integer representing the number of times
the item appears in the sorted_bag.
Note that other operations might also
be useful for sorted_bag,
for example an iterator that yielded the items
in decreasing order.
In general, the definer of a data abstraction
can provide as many operations as seems reasonable.
	In Figure current_figure we give an implementation
of the sorted_bag abstraction.
.begin_figure "The sorted_bag cluster."
.table
sorted_bag = cluster [t: type] is create, insert, size, increasing;
		where t has equal, less_than: proc [t, t] returns [bool];

	rep = record [contents: tree[t], total: int];

create = proc returns (cvt);
	return {contents: tree[t]$create (), total: 0};
	end create;

insert = proc (sb: cvt, v: t);
	sb.contents := tree[t]$insert (sb.contents, v);
	sb.total := sb.total + 1;
	end insert;

size = proc (sb: cvt) returns (int);
	return sb.total;
	end size;

increasing = iter (sb: cvt) yields (t, int);
	for item: t, count: int in tree[t]$increasing (sb) do
		yield item, count;
		end;
	end increasing;

end sorted_bag;
.ns
.end_table
.finish_figure
It is implemented using a sorted binary tree,
just as wordbag is implemented.
Thus, a subsidiary abstraction is necessary.
This abstraction, called tree, is a generalization
of the wordtree abstraction which has been parameterized to work for all ordered types.
An implementation of tree is given in Figure current_figure.
Notice that both the tree abstraction and the sorted_bag abstraction
place the same constraints on their type parameters.
.begin_page_figure "The tree cluster."
.table
tree = cluster [t: type] is create, insert, increasing;
	where t has equal, less_than: proc [t, t] returns [bool];

	node = record [value: t, count: int, lesser: tree[t], greater: tree[t]];
	rep = oneof [empty: null, non_empty: node];

create = proc returns (cvt);
	return rep$make_empty (nil);
	end create;

insert = proc (x: cvt, v: t) 1returns* (1cvt*);
	tagcase x
		tag empty:
			n: node := {value: v, count: 1,
				   lesser: tree[t]$create (),
				   greater: tree[t]$create ()};
			return rep$make_non_empty (n);
		tag non_empty (n: node):
			if t$less_than (v, n.value)
			    then n.lesser := tree[t]$insert (n.lesser, v);
			    else if t$equal (v, n.value)
				then n.count := n.count + 1;
				else n.greater := tree[t]$insert (n.greater, v);
				end;
			    end;
			return x;
		end;
	end insert;

increasing = iter (x: cvt) yields (t, int);
	tagcase x
		tag empty: ;
		tag non_empty (n: node):
			for item: t, count: int in tree[t]$increasing (x.lesser) do
				yield item, count;
				end;
			yield x.value, x.count;
			for item: t, count: int in tree[t]$increasing (x.greater) do
				yield item, count;
				end;
		end;
	end increasing;

end tree;
.ns
.end_table
.finish_figure
	The first thing to notice in these implementations
is the way that the cluster parameter is used in places
where the type string was used in wordbag and wordtree.
This is especially evident in the implementation of tree.
For example, tree has a representation that stores values of
type t.
This can be seen in the definition of the node type,
in which the 2value* component must be an object of type t.
	In the insert operation of tree,
the less_than and equal operations of type t are used.
We have used the compound form, e.g. 2t$equal@(v,@n.value)*,
to emphasize that the equal operation of t is being used.
The short form, 2v@=@n.value*, could have been used instead.
	The increasing iterator of tree works as follows:
First it yields all items in the current tree
that are less than the item at the current node;
it does this by a recursive use of itself,
passing the 2lesser* subtree as a parameter.
Next it yields the contents of the current node,
and then it yields all items in the current tree
that are greater than the item at the current node
(again by a recursive use of itself).
In this way it performs a complete walk over the tree,
yielding the values at all nodes, in increasing order.
.nr rra1 current_figure
.begin_figure "The count_words procedure using iterators."
.table
count_words = proc (i: instream, o: outstream);
	wordbag = sorted_bag[string];
	wb: wordbag := wordbag$create ( );
	for word: string in next_word (i) do
		wordbag$insert (wb, word);
		end;
	total: int := wordbag$size (wb);
	for w: string, count: int in wordbag$increasing (wb) do
		print_word (w, count, total, o);
		end;
	end count_words;
.ns
.end_table
.finish_figure
	Finally, we show in Figure rra1 how the original
procedure count_words can be implemented in terms of sorted_bag.
Note that the count_words procedure now uses 2sorted_bag*[string]
instead of wordbag.
This is a legitimate use of sorted_bag, since the type string
provides both less_than and equal operations.
Note that two for statements are used in count_words.
The second for statement prints the words
in alphabetic order,
using the increasing iterator of sorted_bag.
The first for statement inserts the words into the sorted_bag;
it uses an iterator
.table
	next_word = iter (i: instream) yields (string);
		...
.end_table
The definition of next_word is left as an exercise for the reader.
.sp 2
1References*
.bp 
3Appendix B.  CLU Semantics*
  	Most aspects of CLU semantics are fairly conventional.
Procedure invocation follows the usual call/return discipline, with
(mutual) recursion allowed.  Procedures use local
variables to refer to data; there are no global variables.
Less conventional are the notions of variable and object,
and the definitions of assignment and argument passing which
follow from these notions.
.br
3B1.  Variables and Objects*
	The basic elements of CLU semantics are
2variables* and 2objects*.
Objects are the data entities that are created and manipulated
by CLU programs.  Each object has a particular 2type* which
characterizes its behavior.  A type defines a set of operations
which create and manipulate objects of that type.  An object
may be created and manipulated only via the operations of its type.
	Variables are names used in CLU programs to 2denote*
particular objects at execution time.
Unlike variables in many common programming
languages which 2are* objects that 2contain* values, CLU
variables are simply names which the programmer uses to refer to
objects.  As such, it is possible for two variables to denote the
same object; such an object is 2shared* by the two variables.
This notion of variable is similar to that used in LISP [1].
.nr f3 current_figure+1
(See Figure f3!b below for an example where a record
object is shared by the variables a and x.)
	Objects may refer to objects, but not to variables.
For example,
a record object refers to the objects that are the components
of the record.  This notion is one of logical, not physical, containment.
In particular, it is possible for two record objects to share a
component or (in the case of a cyclic structure) for an object
to ``contain'' itself.  Thus, one can have recursive data
structure definitions and shared data objects without explicit
reference types.
	CLU objects exist independently of procedure activations.
Objects are allocated from a 2heap* as the result of invoking
constructor operations of certain primitive CLU types.  For example,
the record constructor is used in the implementation of 2rational_number*
(Figure 1) to construct new 2rational_number* objects.
Objects are
retained as long as they are 2accessible* by CLU programs.
An object is accessible if it is denoted by a variable of an active
procedure or is contained in an accessible object.
.br
3B2.  Assignment and Procedure Invocation*
	The basic actions in CLU are assignment and procedure invocation.
The assignment primitive 2x*@:=@2E*, where x is a variable
and 2E* is an expression, causes x to denote
the object resulting from the evaluation of 2E*.  For example,
if 2E* is a simple variable y, then the assignment x@:=@y
causes x to denote the object denoted by y.  The object
is 2not* copied; after the assignment is performed, it will be
2shared* by x and y.  Assignment does not affect
the state of any object.  (Examples of assignment are discussed
below.)
	Procedure invocation involves passing argument objects
from the caller to the called procedure and returning result
objects from the procedure to the caller.  The formal arguments
of a procedure are considered to be local variables of the procedure,
and are initialized, by assignment, to the objects resulting from the
evaluation of the argument expressions.  Thus, procedure arguments
objects are shared between the caller and the called procedure.
A procedure may modify mutable argument 2objects*, but it cannot
access or assign to the 2variables* of its caller.
	If a procedure returns a single value, then an invocation of
that procedure may be used as an expression; the object returned by the
procedure becomes the value of the invocation expression.  A procedure
that returns more than one value is invoked by a 2multiple
assignment* statement, which assigns each of the returned objects
to one of a list of variables.  Any procedure, whether or not it
returns values, may be invoked by an invocation statement which
ignores all returned values.
.nr f2 current_figure
.begin_figure "An example of communication via a shared mutable object."
.new_font 0
.table
counter = record [value: int];

increment = proc (x: counter);
	x.value := x.value + 1;
	end increment;

q = proc;
	a: counter;
	z: int;

	a := {value: 0};
	increment (a);
	z := a.value;
	end q;
.ns
.end_table
.finish_figure
	Some examples of assignment and procedure invocation are
provided by the procedure q in Figure f2.
The first statement of q creates a new record object and names
it a (Figure f3!a).
.if f3~=current_figure
.tm figure counting error
.end
.begin_figure "Snapshots during the execution of q."
.table
.sp
.new_font 0
(a) before the invocation of increment:
.new_font 1
	a

		value:			0

.new_font 0
(b) at the invocation of increment:
.new_font 1

	a		x

		value:			0

.new_font 0
(c) after the invocation of increment:
.new_font 1

	a

		value:		1	0

.new_font 0
(d) after the assignment to z:
.new_font 1

	a				z

		value:		1	0

.new_font 0
.ns
.end_table
.finish_figure
The next statement causes the increment
procedure to be invoked, with the record object as the argument.
The formal argument x is initialized by assignment; it thus
2shares* the record object with the variable a (Figure f3!b).
The result of invoking increment is that the value component
of the record denoted by a has been updated to refer to the
integer 1 (Figure f3!c); the next statement thus causes z to
denote 1 (Figure f3!d).  The record object and the variable z
now 2share* the integer object 1, but since CLU integers are
constant, this sharing cannot result in side-effects.
.br
3B3.  Type Correctness*
	Every variable in a CLU program must be declared as to the type(s)
of objects it may denote.  All assignments to a variable must satisfy
the variable's declaration.  Because argument passing is defined
in terms of assignment, this implies that the types of actual
argument objects must be consistent with the declarations of the
corresponding formal arguments.
	These restrictions, plus the restriction that only the code
in a cluster may use cvt to convert between the abstract
and representation types, ensure that the behavior of an object
is indeed characterized completely by the operations of its type.
For example, the type restrictions ensure that the only
possible modification to a record object that represents a
2rational_number* (Figure 1) is the (benevolent) side
effect performed by the 2equal* operation.
.sp 2
1Reference*
.bp
3 Appendix C.  Specifications for Sets, Sequences, and Tuples*
	Finite sets, sequences, and tuples are families of
types, parametrized by the types of their components. All of the
components of a set or sequence must be of the same type, while
each component of a tuple may be of a different type. Sets,
sequences, and tuples can be used to construct more complex
types from simpler ones, and are useful in defining abstract
representations for data type specifications.
	Axiomatic specifications for these three families of
data types are presented on the following pages.
.bp
.fi l
.ls 1
.ta 8 40
1type set*[T 1with* equal(T, T) --> 1bool*] 1is*
.br
1Comment*:
	Sets are defined only for those element types T
	that have an "equal" operation, denoted T$equal.
.br
1Interface*:
	nullset()	--> 1set*[T],
	add(T, 1set*[T])	--> 1set*[T],
	remove(T, 1set*[T])	--> 1set*[T],
	union(1set*[T], 1set*[T])	--> 1set*[T],
	intersection(1set*[T], 1set*[T])	--> 1set*[T],
	difference(1set*[T], 1set*[T])	--> 1set*[T],
	restrict(1set*[T], (T --> 1bool*))	--> 1set*[T],
	is-empty(1set*[T])	--> 1bool*,
	is-in(T, 1set*[T])	--> 1bool*,
	subset(1set*[T], 1set*[T])	--> 1bool*,
	equal(1set*[T], 1set*[T])	--> 1bool*,
	size(1set*[T])	--> 1int*,
	element(1set*[T])	--> T u {1error*(no-unique-element)},
.br
1Axioms*:
	1For all* (x, y): T, (s, t): 1set*[T], p:(T --> 1bool*);
	s = t	eq all!x mem T[is-in(x, s) eq is-in(x, t)],
	is-empty(s)	eq all!x mem T [not is-in(x, s)],
	not is-in(x, nullset()),
	is-in(x, add(y, s))	eq T$equal(x, y) or is-in(x, s),
	is-in(x, remove(y, s))	eq not T$equal(x, y) & is-in(x, s),
	is-in(x, union(s, t))	eq is-in(x, s) or is-in(x, t),
	is-in(x, intersection(s, t))	eq is-in(x, s) & is-in(x, t),
	is-in(x, difference(s, t))	eq is-in(x, s) & not is-in(x, t),
	is-in(x, restrict(s, p))	eq is-in(x, s) & p(x),
	subset(s, t)	eq all!x mem T [is-in(x, s) ==> is-in(x, t)],
	equal(s, t)	eq all!x mem T[is-in(x, s) eq is-in(x, t)],
	is-empty(s)	==> size(s) = 0,
	is-in(x, s)	==> size(s) = size(remove(x, s)) + 1,
	size(s) = 1 & is-in(x, s)	==> element(s) = x,
	not size(s) = 1	==> element(s) = 1error*(no-unique-element),
.br
1Comment*:
	The identity relation denoted by "=" is not an operation
	on the data type, but can be used in assertions and in
	proofs. It is an equivalence relation with the property
	that = things can be substituted for each other.
.br
1end type*.
.rtabs
.bp
.ta 8 48
1type sequence*[T] 1is*
.br
1Interface*:
	emptyseq()	--> 1sequence*[T],
	addfirst(T, 1sequence*[T])	--> 1sequence*[T],
	addlast(T, 1sequence*[T])	--> 1sequence*[T],
	butfirst(1sequence*[T])	--> 1sequence*[T] u {1error*(no-butfirst)},
	butlast(1sequence*[T])	--> 1sequence*[T] u {1error*(no-butlast)},
	concat(1sequence*[T], 1sequence*[T])	--> 1sequence*[T],
	subseq(1int*, 1int*, 1sequence*[T])	--> 1sequence*[T] u {1error*(no-subseq)},
	nth(1int*, 1sequence*[T])	--> T u {1error*(out-of-bounds)},
	first(1sequence*[T])	--> T u {1error*(out-of-bounds)},
	last(1sequence*[T])	--> T u {1error*(out-of-bounds)},
	is-empty(1sequence*[T])	--> 1bool*,
	length(1sequence*[T])	--> 1int*,
.br
1Axioms*:
	1For all* (i, j, k): 1int*, (s, t): 1sequence*[T], x: T;
	s = t eq (length(s) = length(t) & all!i mem 1int* [1 le i le length(s) ==> nth(i, s) = nth(i, t)]),
	i < 1 ==> nth(i, s) = 1error*(out-of-bounds),
	i > length(s) ==> nth(i, s) = 1error*(out-of-bounds),
	length(emptyseq()) = 0,
	length(addfirst(x, s)) = length(s) + 1,
	nth(1, addfirst(x, s)) = x,
	1 < i le length(s) + 1 ==> nth(i, addfirst(x, s)) = nth(i - 1, s),
	length(addlast(x, s)) = length(s) + 1,
	1 le i le length(s) ==> nth(i, addlast(x, s)) = nth(i, s),
	nth(length(s) + 1, addlast(x, s)) = x,
	length(s) = 0 ==> butfirst(s) = 1error*(no-butfirst),
	length(s) > 0 ==> length(butfirst(s)) = length(s) - 1,
	1 le i < length(s) ==> nth(i, butfirst(s)) = nth(i + 1, s),
	length(s) = 0 ==> butlast(s) = 1error*(no-butlast),
	length(s) > 0 ==> length(butlast(s)) = length(s) - 1,
	1 le i < length(s) ==> nth(i, butlast(s)) = nth(i, s),
	length(concat(s, t)) = length(s) + length(t),
	1 le i le length(s) ==> nth(i, concat(s, t)) = nth(i, s),
	length(s) < i le length(s) + length(t) ==> nth(i, concat(s, t)) = nth(i, t),
	j < 1 ==> subseq(j, k, s) = 1error*(no-subseq),
	j + k - 1 > length(s) ==> subseq(j, k, s) = 1error*(no-subseq),
	k < 0 ==> subseq(j, k, s) = 1error*(no-subseq),
	0 le k & 1 le j le 1 + length(s) - k ==>length(subseq(j, k, s)) = k,
	1 le i le k ==> nth(i, subseq(j, k, s)) = nth(j + i - 1, s),
	length(s) = 0 ==> first(s) = 1error*(out-of-bounds),
	length(s) > 0 ==> first(s) = nth(1, s),
	length(s) = 0 ==> last(s) = 1error*(out-of-bounds),
	length(s) > 0 ==> last(s) = nth(length(s), s),
	is-empty(s) eq length(s) = 0,
.br
1end type*.
.rtabs
.bp
.ta 8 56
1type tuple*[SELECTOR-TYPE: S --> 1type*] 1is*
.br
1Comment*:
	1tuple* is a family of types parametrized by functions
	from selectors to types. We will write the names of particular
	types of this family in the form 1tuple*[s1: T1, ... , sn: Tn].
	For legibility, we will write component[s](t) as t.s
	and tuple(v1, ... , vn) as tuple[s1: v1, ... , sn: vn].
.br
1Interface*:
	1Let* S = {s1, ... , sn},
	1For all* 1 le i le n;
	1Let* Ti = SELECTOR-TYPE(si),
	tuple(T1, ... , Tn)	--> 1tuple*[SELECTOR-TYPE],
	component[si](1tuple*[SELECTOR-TYPE])	--> Ti,
	selectors(1tuple*[SELECTOR-TYPE])	-->1set*[S],
.rtabs
.br
1Comment*:
	Note that component[si] is a parametrized family of operations.
.br
1Axioms*:
	1For all* 1 le i le n, vi: Ti, (t, u): 1tuple*[SELECTOR-TYPE];
	t = u eq all!i mem 1int* [1 le i le n ==> component[si](t) = component[si](u)],
	component[si](tuple(v1, ... , vn)) = vi,
	selectors(tuple(v1, ... , vn)) = S,
.br
1end type*.
.ls 2
.fi b
.insert_refs
