Professional Documents
Culture Documents
Editor
Ronald V. Book, University of California
Editorial Board
Erwin Engeler, ETH Zentrum, Zurich, Switzerland
Jean-Pierre Jouannaud, Universite de Paris-Sud, Orsay, France
Robin Milner, Cambridge University, England
Martin Wirsing, UniversiHit Passau, Passau, Germany
ALGOL-like Languages
Volume 1
Peter W. O'Hearn
Robert D. Tennent
Editors
Birkhauser
Boston • Basel • Berlin
Peter O'Hearn Robert D. Tennent
Dept. of Computer Science Dept. of Computing and Infonnation Science
Queen Mary&Westfield College Queen's Uni versity
London E l 4NS Kingston, Ontario
England Canada K7L 3N6
987 6 5 4 3 2 I
Table of Contents
Contributors vii
Introduction 1
Contents
Introduction to Volume 1 1
Part I Historical Background 1
Part n Basic Principles 3
Part III Language Design 5
Introduction to Volume 2 6
Part IV Functor-Category Semantics 7
Part V Specification Logic 7
Part VI Procedures and Local Variables 8
Part vn Interference, Irreversibility and Concurrency 9
Acknowledgements 11
Bibliography 11
Introduction to Volume 1
This volume contains historical and foundational material, and works on lan-
guage design. All of the material should be accessible to beginning graduate
students in programming languages and theoretical Computer Science.
Introduction to Volume 2
The problem is that previous models contain "snapback" operators which con-
tradict this idea of irreversibility. (A treatment of irreversibility has recently
been obtained in an explicit-state setup as well [OR96], using a linear form of
parametricity to exclude snap-back operators.)
While Reddy's model accounts nicely for local state and irreversibility, it
does not treat interference as well. That is, non-interference seems to be built
into the treatment of procedures, so a semantics of syntactic control of inter-
ference can be given, but not full IDEALIZED ALGOL. This was partially resolved
in [OR95] by embedding the model into a functor category but, though the
semantics has good theoretical properties, the model construction is rather
technical and ad hoc in comparison to both Reddy's basic model and other
functor-category models.
In "Linearity, Sharing and State," reprinted here as Chapter 20, Samson
Abramsky and Guy McCusker use the framework of game semantics to for-
mulate an impliCit-state model for full IDEALIZED ALGOL (with side effects in
expressions). Their model construction is striking; it extends the semantics of
the functional language PCF developed by Martin Hyland and Luke Ong [H094]
by simply dropping the "innocence" condition (innocence is a game-theoretic
expression of functional behaviour). Programs denote strategies for playing
a game, and as such may be sensitive to a whole history of events (or "prior
moves"): non-innocent access to the history corresponds to imperative, im-
plicitly stateful, behaviour. It is shown that all "compact" strategies in the
model are definable in the language, and after quotienting, this results in a
fully abstract model.
The game model exemplifies Abramsky's general theme of "composition
as parallel composition plus hiding" [Abr93, Abr94]. It is interesting to re-
visit Reddy's work in light of this. Reddy also emphasized the parallel nature
of composition or application, but focused on what might be thought of as a
"true-concurrency" or "independent-composition" view of parallelism. This is
what, it appears, stopped him from accounting for interference between pro-
cedure and argument in a determinate fashion. In contrast, the parallelism in
the game model is more reminiscent of coroutines (and as such harkens back
to [BC82j), with a single thread of control jumping back and forth between pro-
cedure and argument. This allows interference to be handled quite naturally
in a way that maintains determinacy.
Up to this point the focus has been on sequential versions of ALGOL. The
question arises as to whether all of this development is dependent on the
sequential nature of the languages studied, or, rather, what parts of it can
extend smoothly to concurrency. Steve Brookes addresses this question in
Chapter 21. He formulates a functor-category model for a parallel version
of IDEALIZED ALGOL; the use of a functor category shows that the shape of
the store is the same in the initial and final states in the evaluation of any
command. In a parallel language this does not imply that allocation is truly
Bibliography 11
stack-like if two threads are interleaved on a single processor, but it does es-
tablish invariants for the allocator. The semantics of parallel features is then
a straightforward extension of one previously given for a parallel language
without procedures [Br0931, with care taken to account for appropriate natu-
rality conditions. Brookes regards the integration of this semantics with the
treatment of procedures and storage allocation given by functor categories
as evidence for the orthogonality of procedures and parallelism in ALGoL-like
languages.
Acknowledgements
We are very grateful to all of the contributors for their cooperation in produc-
ing these volumes, to Robin Milner for supporting this project to Birkhauser,
to Edwin Beschler of Birkhauser for his confidence in us, to Lockwood Morris
for proofreading, to Elaine Weinman for T£Xnical typing, and to John Jorgensen
for assistance in setting up our fonts.
P. W. O'Hearn and R. D. Tennent
September, 1996.
Bibliography
[Abr93) S. Abramsky. Computational interpretations of linear logic. Theoretical
Computer Science, 111(1-2):3-57, April 12 1993.
[Abr94) S. Abramsky. Interaction categories and communicating sequential pro-
cesses. In Roscoe [Ros941, chapter I, pages 1-16.
[AW85) S. K. Abdali and D. S. Wise. Standard, storeless semantics for ALGOL-style
block structure and call-by-name. In A. Melton, editor, Mathematical Foun-
dations of Programming Semantics, volume 239 of Lecture Notes in Com-
puter Science, pages 1-19, Manhattan, Kansas, April 1985. Springer-Verlag,
Berlin (1986).
[Bac59) 1. W. Backus. The syntax and semantics of the proposed international alge-
braic language of the Zurich ACM-GAMM Conference. In Information Pro-
cessing, Proceedings of the International Conference on Information Process-
ing, pages 125-131, Paris, June 1959.
[Bac78) J. Backus. Can programming be liberated from the von Neumann style? a
functional style and its algebra of programs. Comm ACM, 21(8):613-641,
August 1978.
[BC82) G. Berry and P-L. Curien. Sequential algorithms on concrete data structures.
Theoretical Computer Science, 20:265-321, 1982.
[Br093) S. Brookes. Full abstraction for a shared variable parallel language. In Pro-
ceedings, 8th Annual IEEE Symposium on Logic in Computer Science, pages
98-109, Montreal, Canada, 1993. IEEE Computer Society Press, Los Alami-
tos, California.
[Bur70) R. M. Burstall. Formal description of program structure and semantics in
first-order logic. In B. Meltzer and D. Michie, editors, Machine Intelligence 5,
pages 79-98. Edinburgh University Press, Edinburgh, 1970.
12 Introduction
[FMS96) M. Fiore, E. Moggi, and D. Sangiorgi. A fully abstract model for the
TT-calculus. In [LIC96), pages 43-54.
[HJ82) W. Henhapl and C. B. jones. ALGOL 60. In D. Bj0rner and C. B. jones, editors,
Formal Specification and Software Development, pages 141-173. Prentice-
Hall International, London, 1982.
[HJ89) c. A. R. Hoare and C. B. jones, editors. Essays in Computing Science. Prentice
Hall International, 1989.
[H094) j. M. E. Hyland and C.-H. L. Ong. On full abstraction for PCF: I, II and III.
Submitted for publication, 1994.
[Hoa69) C. A. R. Hoare. An axiomatic basis for computer programming. Comm.
ACM, 12(10):576-580 and 583, 1969.
[Hoa74) C. A. R. Hoare. Hints on programming-language design. In C. Bunyan, editor,
Computer Systems Reliability, volume 20 of State of the Art Report, pages
505-34. Pergamon/lnfotech, 1974. Also pages 193-216 of [HJ89).
[Hud89) P. Hudak. Conception, evolution, and application of functional program-
ming languages. Computing Surveys, 31:359-411, 1989.
HISTORICAL BACKGROUND
Chapter 1
Revised Report on the Algorithmic Language
ALGOL 60
Contents
1. Structure of the Language 20
1.1. Formalism for Syntactic Description 21
2. Basic Symbols, Identifiers, Numbers, and Strings. Basic Concepts. 22
2.1. Letters 22
2.2. Digits. Logical Values 22
2.3. Delimiters 22
First appeared in Comm. ACM, 6(1):1-17, The Computer Jouma/5:349-67, and Numerische Math-
ematik 4:420-53, 1963. Some introductory material (containing primarily historical information)
and two examples of procedure declarations have been omitted.
1 William Turanski of the American group was killed by an automobile just prior to the January
1960 Conference in Paris.
20 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
2.4.Identifiers 23
2.5.Numbers 23
2.6.Strings 24
2.7.Quantities, Kinds and Scopes 24
2.8.Values and Types 25
3. Expressions 25
3.1. Variables 25
3.2. Function Designators 26
3.3. Arithmetic Expressions 27
3.4. Boolean Expressions 30
3.5. Designational Expressions 32
4. Statements 32
4.1. Compound Statements and Blocks 33
4.2. Assignment Statements 34
4.3. Go To Statements 35
4.4. Dummy Statements 36
4.5. Conditional Statements 36
4.6. For Statements 37
4.7. Procedure Statements 39
5. Declarations 41
5.1. Type Declarations 42
5.2. Array Declarations 42
5.3. Switch Declarations 43
5.4. Procedure Declarations 44
Alphabetic Index of Definitions of Concepts and Syntactic Units 47
In order to facilitate the study, the symbols used for distinguishing the met-
alinguistic variables (i.e., the sequences of characters appearing within the
brackets ( ) as ab in the above example) have been chosen to be words de-
scribing approximately the nature of the corresponding variable. Where words
which have appeared in this manner are used elsewhere in the text they will re-
fer to the corresponding syntactic definition. In addition some formulae have
been given in more than one place.
Definition:
(empty) ::=
(Le., the null string of symbols)
2Whenever the precision of arithmetic is stated as being in general not specified, or the outcome
of a certain process is left undefined, this is to be interpreted in the sense that a program only
fully defines a computational process if the accompanying information specifies the precision
assumed, the kind of arithmetic assumed, and the course of action to be taken in all such cases
as may occur during the execution of the computation.
3Cf. J. W. Backus, The syntax and semantics of the proposed international algebraic language
of the Zunch ACM-GAMM conference. Proc. internat. Conf. Inf. Proc., UNESCO, Paris, June 1959.
22 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
2.1. LEITERS
I kill min I 0 I pi qI r I sit I u I v I w I x I y I z I
{letter} ::= a I b I c I die I fl 9 I hi i Ij
AIBlclvlEIFICIHIIIJIKILIMINlolplQIRlslTlulVIWIXlylZ
This alphabet may arbitrarily be restricted, or extended with any other dis-
tinctive character (i.e., character not cOinciding with any digit, logical value or
delimiter).
Letters do not have individual meaning. They are used for forming identi-
fiers and strings4 (cf. sections 2.4. Identifiers, 2.6. Strings).
2.2.1. Digits
{digit} ::= 0 11 I 2 I 3 14 I 5 161 71 8 19
Digits are used for forming numbers, identifiers, and strings.
2.3. DELIMITERS
{delimiter} ::= {operator} I (separator) I {bracket} I {declarator} I {specificator}
{operator} ::= {arithmetic operator} I (relational operator) I (logical operator) I
(sequential operator)
(arithmetic operator) ::= + I - I x I / I + I t
(relational operator) ::= < I ;;; I = I ;1; I > I ""
{logical operator} ::= == I ::l I v I " I . .,
(sequential operator) ::= go to I if I then I else Ifor I do 5
(separator) ::= .1. IlO I : I ; I := I I step I until I while I comment
(bracket) ::= (I) I [ I ] I . I ' I begin I end
(declarator) ::= own I Boolean I integer I real I array I switch I procedure
(specificator) ::= string I label I value
4It should be particularly noted that throughout the reference language underlining in type·
written copy and boldface in printed copy are used for defining independent basic symbols (see
sections 2.2.2 and 2.3). These are understood to have no relation to the individual letters of which
the. are composed. Within the present report (not including headings), boldface will be used for
no other purpose.
sdo is used in for statements. It has no relation whatsoever to the do of the preliminary report,
which is not included in ALGOL 60.
P. Naur (ed.), J. W. Backus, F. L. Bauer, J. Green, C. Katz, J. McCarthy, et al. 23
Delimiters have a fixed meaning which for the most part is obvious or else
will be given at the appropriate place in the sequel.
Typographical features such as blank space or change to a new line have
no Significance in the reference language. They may, however, be used freely
for facilitating reading.
For the purpose of including text among the symbols of a program the
following "comment" conventions hold:
The sequence of basic symbols: is equivalent to
; comment (any sequence not containing ;);
begin comment (any sequence not containing ;); begin
end (any sequence not containing end or ; or else) end
By equivalence is here meant that any of the three structures shown in the
left-hand column maybe replaced, in any occurrence outside of strings, by the
symbol shown on the same line in the right-hand column without any effect on
the action of the program. It is further understood that the comment structure
encountered first in the text when reading from left to right has precedence in
being replaced over later structures contained in the sequence.
2.4. IDENTIFIERS
2.4.1. Syntax
(identifier) ::= (letter) I (identifier)(letter) I (identifier)(digit)
2.4.2. Examples
q
soup
VI7a
a34kTMNs
MARILYN
2.4.3. Semantics
Identifiers have no inherent meaning, but serve for the identification of
simple variables, arrays, labels, Switches, and procedures. They may be chosen
freely (cf., however, section 3.2.4. Standard Functions).
The same identifier cannot be used to denote two different quantities ex-
cept when these quantities have disjoint scopes as defined by the declarations
of the program (cf. section 2.7. Quantities, Kinds and Scopes, and section 5.
Declarations).
2.5. NUMBERS
2.5.1. Syntax
(unsigned integer) ::= (digit) I (unsigned integer)(digit)
(integer) ::= (unsigned integer) I + (unsigned integer) I - (unsigned integer)
(decimal fraction) ::= .(unsigned integer)
(exponent part) ::= lO(integer)
(decimal number) ::= (unsigned integer) I (decimal fraction) I
(unsigned integer) (decimal fraction)
24 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
2.5.2. Examples
0 -400.084 -.08310-02
177 +07.43108 -107
.5384 9.3410+ 10 10-4
+0.7300 210-4 +10+5
2.5.3. Semantics
Decimal numbers have their conventional meaning. The exponent part is a
scale factor expressed as an integral power of 10.
2.5.4. Types
Integers are of type integer. All other numbers are of type real (cf. sec-
tion 5.1. Type Declarations).
2.6. STRINGS
2.6.1. Syntax
(proper string) ::= (any sequence of basic symbols not containing' or ') I (empty)
(open string) ::= (proper string) I '(open string)' I (open string) (open string)
(string) ::= '(open string)'
2.6.2. Examples
'5k" -'[[['" = I :'Tt"
'.. This is a 'string"
2.6.3. Semantics
In order to enable the language to handle arbitrary sequences of basic sym-
bols the string quotes ' and ' are introduced. The symbol denotes a space. It
has no significance outside strings.
Strings are used as actual parameters of procedures (cf. sections 3.2. Func-
tion Designators and 4.7. Procedure Statements).
3. Expressions
In the language the primary constituents of the programs describing al-
gorithmic processes are arithmetic, Boolean, and designational expressions.
Constituents of these expreSSions, apart from certain delimiters, are logical
values, numbers, variables, function designators, and elementary arithmetic,
relational, logical, and sequential operators.
Since the syntactic definition of both variables and function designators
contains expreSSions, the definition of expreSSions, and their constituents, is
necessarily recursive.
3.1. VARIABLES
3.1.1. Syntax
(variable identifier) ::= (identifier)
(simple variable) ::= (variable identifier)
(subscript expression) ::= (arithmetic expression)
(subscript list) ::= (subscript expression) I (subscript list),(subscript expression)
(array identifier) ::= (identifier)
(subscripted variable) ::= (array identifier)[ (subscript list) 1
(variable) ::= (simple variable) I (subscripted variable)
3.1.2. Examples
epsilon
detA
al7
Q[7,2]
x[sin(n x pi/2), Q[3, n, 4]]
26 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
3.1.3. Semantics
3.1.4. Subscripts
3.1.4.2. Each subscript position acts like a variable of type integer and the
evaluation of the subscript is understood to be equivalent to an assignment to
this fictitious variable (cf. section 4.2.4). The value of the subscripted variable
is defined only if the value of the subscript expression is within the subscript
bounds of the array (cf. section 5.2. Array Declarations).
3.2.1. Syntax
(procedure identifier) ::= (identifier)
(actual parameter) ::= (string) 1 (expression) 1 (array identifier) 1 (switch identifier) 1
(procedure identifier)
(letter string) ::= (letter) 1 (letter string) (letter)
(parameter delimiter) ::= ,I) (letter string): (
(actual parameter list) ::= (actual parameter) 1
(actual parameter list) (parameter delimiter) (actual parameter)
(actual parameter part) ::= (empty) 1 «actual parameter list»
(function designator) ::= (procedure identifier) (actual parameter part)
3.2.2. Examples
sin(a - b)
l(v + s,n)
R
S(s - 5)Temperature:(T)Pressure:(P)
Compile(' := ')Stack:(Q)
P. Naur (ed.), J. W. Backus, F. L. Bauer,]. Green, C. Katz,]. McCarthy, et al. 27
3.2.3. Semantics
Function designators define single numerical or logical values, which result
through the application of given sets of rules defined by a procedure declara-
tion (cf. section 5.5. Procedure Declarations) to fixed sets of actual parame-
ters. The rules governing specification of actual parameters are given in sec-
tion 4.7. Procedure Statements. Not every procedure declaration defines the
value of a flllction designator.
3.3.2. Examples
Primaries:
7.39410-8
sum
w[i +2,8]
cos(y + z x 3)
(a-3/y+vut8)
Factors:
omega
sum t cos(y + z x 3)
7.39410-8 t w[i + 2,8] t (a - 3/y + vu t 8)
Terms:
U
omega x sum t cos(y + z x 3)/7.39410-8 t w[i + 2,8] t
(a - 3/y + vu t 8)
Arithmetic expressions:
wxu-Q(S+Cu) t 2
if q > 0 then S + 3 x Q/ A else 2 x S + 3 x q
if a < 0 then U + V else ifax b > 17 then U /V else if
k '* y then V /U else 0
a x sin(omega x t)
0.571012 x a[N x (N - 1) /2,0]
(A x arctan(y) + Z) t (7 + Q)
ifqthenn-1elsen
if a < 0 then A/B else if b = 0 then B/A else z
3.3.3. Semantics
An arithmetic expression is a rule for computing a numerical value. In case
of simple arithmetic expressions this value is obtained by executing the indi-
cated arithmetic operations on the actual numerical values of the primaries of
the expression, as explained in detail in section 3.3.4 below. The actual nu-
merical value of a primary is obvious in the case of numbers. For variables,
it is the current value (assigned last in the dynamic sense), and for function
designators it is the value arising from the computing rules defining the pro-
cedure (cf. section 5.4.4. Values of Function Designators) when applied to the
current values of the procedure parameters given in the expression. Finally,
for arithmetic expressions enclosed in parentheses the value must through a
recursive analysis be expressed in terms of the values of primaries of the other
three kinds.
In the more general arithmetic expressions, which include if clauses, one
out of several simple arithmetic expressions is selected on the basis of the
actual values of the Boolean expressions (cf. section 3.4. Boolean Expressions).
This selection is made as follows. The Boolean expressions of the if clauses
P. Naur (ed.). J. w. Backus. F. L. Bauer.]. Green. C. Katz. J. McCarthy. et al. 29
are evaluated one by one in sequence from left to right until one having the
value true is found. The value of the arithmetic expression is then the value
of the first arithmetic expression following this Boolean (the largest arithmetic
expression found in this position is understood). The construction
else (simple arithmetic expression)
is equivalent to the construction
else if true then (simple arithmetic expression)
3.3.4. Operators and types
Apart from the Boolean expressions of if clauses. the constituents of sim-
ple arithmetic expressions must be of types real or integer (cf. section 5.l.
Type Declarations). The meanings of the basiC operators and the types of the
expressions to which they lead are given by the following rules.
3.3.4.1. The operators +, -, and x have the conventional meaning (addition,
subtraction, and multiplication). The type of the expression will be integer if
both of the operands are of integer type, otherwise real.
3.3.4.2. The operations (term) / (factor) and (term) -;- (factor) both denote
division, to be understood as a multiplication of the term by the reciprocal of
the factor with due regard to the rules of precedence (cf. section 3.3.5). Thus
for example
a/bx7/(p-q)xv/s
means
««a x (b- 1 » x 7) x «p - q)-l» x v) x (s-l)
The operator / is defined for all four combinations of types real and integer
and will yield results of real type in any case. The operator -;- is defined only
for two operands both of type integer and will yield a result of type integer,
mathematically defined as follows:
a""" b = sign(a/b) x entier(abs(a/b»
(cf. sections 3.2.4 and 3.2.5).
3.3.4.3. The operation (factor) f (primary) denotes exponentiation, where
the factor is the base and the primary is the exponent. Thus, for example,
2 tnt k means (2")k
while
2 I (n I m) means 2(n m )
Writing i for a number of integer type, r for a number of real type, and a for
a number of either integer or real type, the result is given by the following
rules.
at i If i > 0, a x a x ... x a (i times). of the same type as a.
Ifi = 0, if a *" 0, I, of the same type as a.
If i < 0, if a*" 0, 1/(a x a x ... x a) (the denominator has
-i factors), of type real.
if a = 0, undefined.
at r If a> 0, exp(r x In(a». of type real.
If a = 0, if r > 0, 0.0, of type real.
if r ~ 0. undefined.
If a < 0, always undefined.
30 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
3.3.5.1. According to the syntax given in section 3.3.1 the following rules of
precedence hold:
first: T
second: x/+
third: +-
3.3.5.2. The expression between a left parenthesis and the matching right
parenthesis is evaluated by itself and this value is used in subsequent calcu-
lations. Consequently the desired order of execution of operations within an
expression can always be arranged by appropriate positioning of parentheses.
Numbers and variables of type real must be interpreted in the sense of nu-
merical analysis, i.e., as entities defined inherently with only a finite accuracy.
Similarly, the possibility of the occurrence of a finite deviation from the mathe-
matically defined result in any arithmetic expression is explicitly understood.
No exact arithmetic will be specified, however, and it is indeed understood
that different hardware representations may evaluate arithmetic expressions
differently. The control of the possible consequences of such differences must
be carried out by the methods of numerical analysiS. This control must be con-
sidered part of the process to be described, and will therefore be expressed in
terms of the language itself.
3.4.1. Syntax
(relational operator) ::= < I ~ I = I ~ I >
(relation) ::= (simple arithmetic expression) (relational operator)
(simple arithmetic expression)
(Boolean primary) ::= (logical value) I (variable) I (function designator) I (relation) I
( (Boolean expression) )
(Boolean secondary) ::= (Boolean primary) I ..., (Boolean primary)
(Boolean factor) ::= (Boolean secondary) I (Boolean factor) /\ (Boolean secondary)
(Boolean term) ::= (Boolean factor) I (Boolean term) v (Boolean factor)
(implication) ::= (Boolean term) I {implication):J (Boolean term)
(simple Boolean) ::= (implication) I (simple Boolean) =(implication)
(Boolean expression) ::= (simple Boolean) I
(if clause) (simple Boolean) else (Boolean expression)
P. Naur (ed.),]. W. Backus, F. L. Bauer,]. Green, C. Katz,]. McCarthy, et al. 31
3.4.2. Examples
x = -2
Y>Vvz<q
a + b > -5 A z - d > q t 2
pAqVX*y
9 == -.a A b A -'c v d v e :::> -.f
ifk<lthens>welseh;§;c
if if if a then b else c then d else f then 9 else h < k
3.4.3. Semantics
A Boolean expression is a rule for computing a logical value. The principles
of evaluation are entirely analogous to those given for arithmetic expressions
in section 3.3.3.
3.4.4. Types
Variables and function esignators entered as Boolean primaries must be
declared Boolean (cf. section 5.1. Type Declarations and section 5.4.4. Values
of Function Designators).
3.4.6.1. According to the syntax given in section 3.4.1 the following rules of
precedence hold:
first: arithmetic expressions according to section 3.3.5.
second: <;§;=~>*
third:
fourth: A
fifth: V
sixth: :::>
seventh:
3.4.6.2. The use of parentheses will be interpreted in the sense given in sec-
tion 3.3.5.2.
32 Chapter 1. Revised Report on the Algorithmic Language ALeoL 60
3.5.1. Syntax
(label) ::= (identifier) I (unsigned integer)
(switch identifier) ::= (identifier)
(switch designator) ::= (switch identifier) [(subscript expression)]
(simple designational expression) ::= (label) I (switch designator) I
«designational expression»
(designational expression) ::= (simple designational expression) I
(if clause) (simple designational expression) else (designational expression)
3.5.2. Examples
17
p9
Choose[n - 1]
Town[if y < 0 then N else N + 1]
if Ab < c then 17 else q[if w ;'i; 0 then 2 else n]
3.5.3. Semantics
A designational expression is a rule for obtaining a label of a statement
(cf. section 4. Statements). Again the principle of the evaluation is entirely
analogous to that of arithmetic expressions (section 3.3.3). In the general case
the Boolean expressions of the if clauses will select a simple designational
expression. If this is a label the desired result is already found. A switch
designator refers to the corresponding switch declaration (section 5.3. Switch
Declarations) and by the actual numerical of its subscript expressions selects
one of the designational expressions listed in the switch declaration by count-
ing these from left to right. Since the designational expression thus selected
may again be a switch designator, this evaluation is obviously a recursive pro-
cess.
4. Statements
The units of operation within the language are called statements. They
will normally be executed consecutively as written. However, this sequence of
operations may be broken by go to statements, which define their successor
explicitly, and shortened by conditional statements, which may cause certain
statements to be skipped.
P. Naur (ed.),]. W. Backus, F. L. Bauer,]. Green, C. Katz,]. McCarthy, et al. 33
4.1.2. Examples
Basic statements:
a:= p + q
go to Naples
START: CONTINUE: W := 7.993
Compound statement:
begin x := 0; for y:= 1 step 1 until n do
x:= x +A[y];
if x > q then go to STOP else if x > w - 2 then
go to S;
Aw: W := x + bob end
34 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
Block:
4.1.3. Semantics
4.2.1. Syntax
(left part) ::= (variable):= I (procedure identifier) :=
(left part list) ::= (left part) I (left part list) (left part)
(assignment statement) ::= (left part list) (arithmetic expression)
(left part list) (Boolean expression)
4.2.2. Examples
s := prO] := n := n + 1 + s
n:= n + 1
A:= B/C - v - q x S
S[v, k + 2] := 3 - arctan(s x zeta)
V:= Q > Y"Z
P. Naur (ed.),]. W. Backus, F. L. Bauer, J. Green, C. Katz,]. McCarthy, et al. 35
4.2.3. Semantics
Assignment statements serve for assigning the value of an expression to
one or several variables or procedure identifiers. Assignment to a procedure
identifier may only occur within the body of a procedure defining the value of
a function designator (cf. section 5.4.4). The process will in the general case
be understood to take place in three steps as follows:
4.2.3.1. Any subscript expressions occurring in the left part variables are
evaluated in sequence from left to right.
4.2.3.2. The expression of the statement in evaluated.
4.2.3.3. The value of the expression is assigned to all the left part variables,
with any subscript expressions having values as evaluated in step 4.2.3.1.
4.2.4. Types
The type associated with all variables and procedure identifiers of a left
part list must be the same. If this type is Boolean, the expression must like·
wise be Boolean. If the type is real or integer, the expression must be arith·
metic. If the type of the arithmetic expression differs from that associated
with the variables and procedure identifiers, appropriate transfer functions
are understood to be automatically invoked. For transfer from real to integer
type, the transfer function is understood to yield a result equivalent to
entier(E + 0.5)
where E is the value of the expression. The type associated with a procedure
identifier is given by the declarator which appears as the first symbol of the
corresponding procedure declaration (cf. section 5.4.4).
4.3. Go To STATEMENTS
4.3.1. Syntax
(go to statement) .. - go to (designational expression)
4.3.2. Examples
go to 8
go to exit[n + 1]
go to Town[if y < 0 then N else N + 1]
go to if Ab < C then 17 else q[if w < 0 then 2 else N]
4.3.3. Semantics
A go to statement interrupts the normal sequence of operations, defined
by the write-up of statements, by defining its successor explicitly by the value
of a designational expression. Thus the next statement to be executed will be
the one having this value as its label.
4.3.4. Restriction
Since labels are inherently local, no go to statement can lead from out-
side into a block. A go to statement may, however, lead from outside into a
compound statement.
36 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
4.4.1. Syntax
(dummy statement) .. - (empty)
4.4.2. Examples
L:
begin ... ; John: end
4.4.3. Semantics
A dummy statement executes no operation. It may serve to place a label.
4.5.1. Syntax
(if clause) ::= if (Boolean expression) then
(unconditional statement) ::= (basiC statement) I (compound statement) I (block)
(if statement) ::= (if clause) (unconditional statement)
(conditional statement) ::= (if statement) I (if statement) else (statement)
(if clause) (for statement) I (label): (conditional statement)
4.5.2. Examples
if x > 0 then n := n + 1
if v > u then V : q := n + m else go to R
if s < 0 v P ~ Q then AA: begin if q < v then a := vis else y := 2 x a end
else if v> s then a:= v - q else if v> s - 1 then go to S
4.5.3. Semantics
Conditional statements cause certain statements to be executed or skipped
depending on the running values of specified Boolean expressions.
and
if Bl then 51 else if B2 then 52 else if B3 then 53 ; 54
P. Naur (ed.),]. W. Backus, F. L. Bauer, J. Green, C. Katz, J. McCarthy, et al. 37
4.6.2. Examples
for q := 1 step s until n do A[q] := B[q]
for k := 1, VI x 2 while VI < N do
for j := I + G, L, 1 step 1 until N, C + D do
A[k,j] := B[k,j]
38 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
4.6.3. Semantics
A for clause causes the statement S which it precedes to be repeatedly
executed zero or more times. In addition, it performs a sequence of assign-
ments to its controlled variable. The process may be visualized by means of
the following picture:
Initialize ;
r
test
- - - -
statement S ;
- -
1
advance successor
L - - - - - -
for list exhausted
- - - J
In this picture, the word initialize means: perform the first assignment of the
for clause. Advance means: perform the next assignment of the for clause.
Test determines if the last assignment has been done. If so, the execution con-
tinues with the successor of the for statement. If not, the statement following
the for clause is executed.
4.6.4. The for list elements
The for list gives a rule for obtaining the values which are consecutively
assigned to the controlled variable. This sequence of values is obtained from
the for list elements by taking these one by one in the order in which they are
written. The sequence of values generated by each of the three species of for
list elements and the corresponding execution of the statement S are given by
the following rules.
4.6.4.1. Arithmetic expression. This element gives rise to one value, namely
the value of the given arithmetic expression as calculated immediately before
the corresponding execution of the statement S.
4.6.4.2. Step-until-element. An element of the form A step B until C, where
A, B, and C are arithmetic expressions, gives rise to an execution which may be
described most concisely in terms of additional ALGOL statements as follows:
V:=A;
L1: if (V - C) x sign(B) > 0 then go to element exhausted;
statement S;
V:= V +B;
go to L1;
where V is the controlled variable of the for clause and element exhausted
points to the evaluation according to the next element in the for list, or if the
step-until-element is the last of the list, to the next statement in the program.
4.6.4.3. While-element. The execution governed by a for list element of the
form E while F, where E is an arithmetic and F a Boolean expression, is most
concisely described in terms of the following additional ALGOL statements as
follows.
L3: V:=E;
ifof then go to element exhausted;
Statement S;
go toL3;
where the notation is the same as in 4.6.4.2 above.
P. Naur (ed.),]. W. Backus, F. L. Bauer,]. Green, C. Katz,]. McCarthy, et al. 39
4.7.1. Syntax
(actual parameter) ::= (string) I (expression) I (array identifier)
(switch identifier) I (procedure identifier)
(letter string) ::= (letter) I (letter string) (letter)
(parameter delimiter) ::= ,I )(letter string) : (
(actual parameter list) ::= (actual parameter) I
(actual parameter list) (parameter delimiter) (actual parameter)
(actual parameter part) ::= (empty) I «actual parameter list»
(procedure statement) .. - (procedure identifier) (actual parameter part)
4.7.2. Examples
Spur(A)Order:(7)Result to:(V)
Transpose(W, v + 1)
Absmax(A,N,M, YY,I,K)
Innerproducr(A[t,p, u],B[P], 10,P, y)
4.7.3. Semantics
A procedure statement serves to invoke (call for) the execution of a proce-
dure body (cf. section 5.4. Procedure Declarations). Where the procedure body
is a statement written in ALGOL, the effect of this execution will be equivalent
to the effect of performing the following operations on the program at the
time of execution of the procedure statement.
4.7.3.1. Value assignment (call by value). All formal parameters quoted in
the value part of the procedure declaration heading are assigned the values
(cf. section 2.8. Values and Types) of the corresponding actual parameters,
these assignments being considered as being performed expliCitly before en-
tering the procedure body. The effect is as though an additional block embrac-
ing the procedure body were created in which these assignments were made to
variables local to this fictitious block with types as given in the corresponding
specifications (cf. section 5.4.5). As a consequence, variables called by value
are to be considered as nonlocal to the body of the procedure, but local to the
fictitious block (cf. section 5.4.3).
40 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
4.7.3.2. Name replacement (call by name). Any formal parameter not quoted
in the value list is replaced, throughout the procedure body, by the corre-
sponding actual parameter, after enclOSing this latter in parentheses whenever
syntactically possible. Possible conflicts between identifiers inserted through
this process and other identifiers already present within the procedure will
be avoided by suitable systematic changes of the formal or local identifiers
involved.
4.7.3.3_ Body replacement and execution. Finally the procedure body, modi-
fied as above, is inserted in place of the procedure statement and executed. If
a procedure is called from a place outside the scope of any nonlocal quantity
of the procedure body, the conflicts between the identifiers inserted through
this process of body replacement and the identifiers whose declarations are
valid at the place of the procedure statement or function designator will be
avoided through suitable systematic changes of the latter identifiers.
4.7.5. Restrictions
For a procedure statement to be defined it is evidently necessary that the
operations on the procedure body defined in sections 4.7.3.1 and 4.7.3.2 lead
to a correct ALGOL statement.
This imposes the restriction on any procedure statement that the kind
and type of each actual parameter be compatible with the kind and type of
the corresponding formal parameter. Some important particular cases of this
general rule are the following.
4.7.5.5. Any formal parameter may have restrictions on the type of the cor-
responding actual parameter associated with it. (These restrictions may, or
may not, be given through specifications in the procedure heading.) In the
procedure statement such restrictions must evidently be observed.
4.7.6. Deleted.
4.7.7. Parameter Delimiters
All parameter delimiters are understood to be equivalent. No correspon-
dence between the parameter delimiters used in a procedure statement and
those used in the procedure heading is expected beyond their number being
the same. Thus the information provided by using the elaborate ones is en-
tirely optional.
5. Declarations
Declarations serve to define certain properties of the quantities used in the
program, and to associate them with identifiers. A declaration of an identifier
is valid for one block. Outside this block, the particular identifier may be used
for other purposes (cf. section 4.1.3).
Dynamically this implies the following: at the time of an entry into a block
(through the begin, since the labels inside are local and therefore inaccessible
from outside), all identifiers declared for the block assume the Significance
implied by the nature of the declarations given. If these identifiers had already
been defined by other declarations outSide, they are for the time being given a
new Significance. Identifiers which are not declared for the block, on the other
hand, retain theW old meaning.
At the time of an exit from a block (through end, or by a go to statement),
all identifiers which are declared for the block lose their local significance.
42 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
Syntax
(declaration) ::= (type declaration) I (array declaration) I (switch declaration) I
(procedure declaration)
5.1.2. Examples
integer P. q. s
own Boolean Aeryl. n
5.1.3. Semantics
Type declarations serve to declare certain identifiers to represent simple
variables of a given type. Real declared variables may only assume positive
or negative values including zero. Integer declared variables may only assume
positive and negative integral values including zero. Boolean declared vari-
ables may only assume the values true and false.
In arithmetic expressions any position which can be occupied by a real
declared variable may be occupied by an integer declared variable.
For the semantics of own. see the fourth paragraph of section 5 above.
5.2.2. Examples
array a,b,e[?: n, 2: m],s[ -2,10]
own integer array A[if e < 0 then 2 else 1 : 20]
real array q[ -?: -1]
5.2.3. Semantics
An array declaration declares one or several identifiers to represent mul-
tidimensional arrays of subscripted variables and gives the dimensions of the
arrays, the bounds of the subSCripts, and the types of the variables.
5.2.3.1. Subscript bounds. The subscript bounds for any array are given in
the first subscript bracket following the identifier of this array in the form
of a bound pair list. Each item of this list gives the lower and upper bound
of a subscript in the form of two arithmetic expressions separated by the
delimiter:. The bound pair list gives the bounds of all subscripts taken in
order from left to right.
5.2.3.2. Dimensions. The dimensions are given as the number of entries in
the bound pair lists.
5.2.3.3. Types. All arrays declared in one declaration are of the same quoted
type. If no type declarator is given, the type real is understood.
5.2.4. Lower upper bound expressions
5.2.4.1. The expressions will be evaluated in the same way as subscript ex-
pressions (cf. section 3.1.4.2).
5.2.4.2. The expressions can only depend on variables and procedures which
are nonlocal to the block for which the array declaration is valid. Consequently
in the outermost block of a program only array declarations with constant
bounds may be declared.
5.2.4.3. An array is defined only when the values of all upper subscript
bounds are not smaller than those of the corresponding lower bounds.
5.2.4.4. The expressions will be evaluated once at each entrance into the
block.
5.2.5. The identity of subscripted variables
The identity of a subscripted variable is not related to the subscript bounds
given in the array declaration. However, even if an array is declared own
the values of the corresponding subscripted variables will, at any time, be
defined only for those of these variables which have subscripts within the
most recently calculated subscript bounds.
5.3. SWITCH DECLARATIONS
5.3.1. Syntax
(switch list) ::= (designational expression) I (switch list), (designational expression)
(switch declaration) ::= switch (switch identifier) := (switch list)
44 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
5.3.2. Examples
switch S := Sl, S2, Q[m], if v > - 5 then S3 else S4
switch Q := pl, W
5.3.3. Semantics
A switch declaration defines the set of values of the corresponding switch
designators. These values are given one by one as the values of the designa-
tional expressions entered in the switch list. With each of these designational
expressions there is associated a positive integer 1,2, ... , obtained by count-
ing the items in the list from left to right. The value of the switch designator
corresponding to a given value of the subscript expression (cf. section 3.5.
Designational Expressions) is the value of the designational expression in the
switch list having this given value as its associated integer.
5.4.2. Examples
procedure Spur(a)Order:(n)Result:(s); value n;
array a; integer n; real s;
begin integer k;
s:= 0;
for k := 1 step 1 until n do s := s + ark, k]
end
5.4.3. Semantics
A procedure declaration serves to define the procedure associated with
a procedure identifier. The principal constituent of a procedure declaration
is a statement or a piece of code, the procedure body, which through the
use of procedure statements and/or function designators may be activated
from other parts of the block in the head of which the procedure declara-
tion appears. Associated with the body is a heading, which specifies certain
identifiers occurring within the body to represent formal parameters. Formal
46 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60
5.4.5. Specifications
In the heading a specification part, giving information about the kinds and
types of the formal parameters by means of an obvious notation, may be in-
cluded. In this part, no formal parameter may occur more than once. Specifi-
cations of parameters called by value (cf. section 4.7.3.1) must be supplied and
specifications of formal parameters called by name (cf. section 4.7.3.2) may be
omitted.
real, synt 2.3, 5.1.1 text 5.1.3 (subscript list), def 3.1.1
(relation), def 3.4.1 text 3.4.5 successor, text 4
(relational operator), def 2.3, 3.4.1 switch, synt 2.3, 5.3.1, 5.4.1
(switch declaration), def 5.3.1 synt 5
scope, text 2.7 text 5.3.3
semicolon;, synt 2.3, 4.1.1, 5.4.1 (switch designator), def 3.5.1 text 3.5.3
(separator), def 2.3 (switch identifier), def 3.5.1 synt 3.2.1,
(sequential operator), def 2.3 4.7.1, 5.3.1
(simple arithmetic expression), def 3.3.1 (switch list), def 5.3.1
text 3.3.3 (term), def 3.3.1
(simple Boolean), def 3.4.1 ten 10, synt 2.3, 2.5.1
(simple designational expression), then, synt 2.3, 3.3.1, 4.5.1
def 3.5.1 transfer function, text 3.2.5
(simple variable), def 3.1.1 synt 5.5.1 true, synt 2.2.2
text 2.4.3 (type), def 5.1.1 synt 5.4.1 text 2.8
space ,synt 2.3 text 2.3, 2.6.3 (type declaration), def 5.1.1 synt 5
(specification part), def 5.4.1 text 5.4.5 text 5.1.3
(specificator), def 2.3 (type list), def 5.1.1
(specifier), def 5.4.1
standard function, text 3.2.4, 3.2.5 (unconditional statement), def 4.1.1,
(statement), def 4.1.1 synt 4.5.1, 4.6.1, 4.5.1
5.4.1 text 4 (complete section) (unlabelled basic statement), def 4.1.1
statement bracket, see begin end (unlabelled block), def 4.1.1
step, synt 2.3, 4.6.1 text 4.6.4.2 (unlabelled compound), def 4.1.1
string, synt 2.3, 5.4.1 (unsigned integer), def 2.5.1,3.5.1
(string), def 2.6.1 synt 3.2.1,4.7.1 (unsigned number), def 2.5.1 synt 3.3.1
text 2.6.3 until, synt 2.3, 4.6.1 text 4.6.4.2
string quotes' " synt 2.3, 2.6.1 text 2.6.3 (upper bound), def 5.2.1 text 5.2.4
subscript, text 3.1.4.1
value, synt 2.3, 5.4.1
subscript bound, text 5.2.3.1
value, text 2.8, 3.3.3
subscript brackets [ ], synt 2.3, 3.1.1,
(value part), def 5.4.1 text 4.7.3.1
3.5.1, 5.2.1
(variable), def 3.1.1 synt 3.3.1, 3.4.1,
(subscripted variable), def 3.1.1
4.2.1, 4.6.1 text 3.1.3
text 3.1.4.1
(variable identifier), def 3.1.1
(subscript expression), def 3.1.1
synt 3.5.1 while, synt 2.3, 4.6.1 text 4.6.4.3
Chapter 2
The Varieties of Programming Language
Christopher Strachey
This paper suggests an analysis of the domains used in programming lan-
guages. It identifies some of the characteristic domains and shows that
programming languages vary widely in their definition of these domains.
Preface
(With apologies to Professor William James, Miss Stella Gibbons and the late Herr
Baedeker.)
1 In my belief that a large acquaintance with particulars often makes us wiser than
the mere possession of abstract formulas, however deep, I have ended this paper
with some concrete examples, and I have chosen these among the extreme designs
of programming languages. To some readers I may consequently seem, by the time
they reach the end of the paper, to offer a caricature of the subject. Such convulsions
of linguistic purity, they will say, are not sane. It is my belief, however, that there is
much of value to be learnt from the study of extreme examples, not least, perhaps,
that our view of sanity is rather easily influenced by our environment; and this, in
the case of programming languages, is only too often narrowly confined to a single
machine. My ambition in this and other related papers, mostly so far unwritten, is to
develop an understanding of the mathematical ideals of programming languages and
to combine them with other principles of common sense which serve as correctives
of exaggeration, allowing the individual reader to draw as moderate conclusions as he
will.
Contents
o. Introduction 52
1. Mathematical Basis 53
1.1 Functions 53
1.2 Domains 54
1.3 Reflexive Domains 55
2. Characteristic Domains in Programming Languages 56
2.1 Denotations 56
2.2 Stored Values 58
2.3 The Assignment Command 58
3. Two Specimen Languages 60
3.1 ALGOL 60 60
3.2 PAL 62
4. Conclusion 63
References 63
First appeared as Technical Monograph PRG-IO, Oxford University Computing Laboratory, Pro-
gramming Research Group, Oxford, March 1973, which is a revised and slightly expanded version
of a paper given at the International Computing Symposium at Venice, 12-14 April, 1972 [10).
©1973 by Christopher Strachey; reprinted with the permission of Barbara Strachey Halpern.
1William James, The Varieties of Religious Experience, (Preface), Longmans & Co., London; Cam-
bridge, Mass., 1902.
Stella Gibbons, Cold Comfort Farm, (Preface), Longmans & Co., London, 1932.
52 Chapter 2. The Varieties of Programming Language
o. Introduction
There are so many programming languages in existence that it is a hopeless
task to attempt to learn them all. Moreover many programming languages
are very badly described; in some cases the syntax, or rather most of the syn-
tax, is clearly and concisely defined, but the vitally important question of the
semantics is almost always dealt with inadequately.
Part of the reason for this is that there is no generally accepted formalism
in which to describe the semantics; there is nothing for semantics correspond-
ing to BNF for syntax. BNF is far from adequate to describe the whole syntax
of any programming language, but with a little goodwill and a few informal
extensions here and there, it is enough to be of considerable help in describ-
ing a large part of the syntax of many languages. Moreover, and this is one of
its chief advantages, it is very widely understood and used by programming
language designers and implementers.
When we come to the semantics the situation is not nearly so satisfactory.
Not only is there no generally accepted notation, there is very little agreement
even about the use of words. The trouble seems to be that programming
language designers often have a rather parochial outlook and appear not to
be aware of the range of semantic possibilities for programming languages.
As a consequence they never explain expliCitly some of the most important
features of a programming language and the decisions among these, though
rarely mentioned (and frequently I suspect made unconsciously), have a very
important effect on the general flavour of the language. The main purpose
of this paper is to discuss some general features of this range of semantic
possibilities in the hope that it may make it easier to classify the varieties of
programming language.
One of the greatest advantages of using a high level programming language
is that it allows us to think about abstract mathematical objects, such as in-
tegers, instead of their rather arbitrary representations by bit-patterns inside
the machine. When we write programs we can now think of variables instead
of locations (or addresses) and (unctions instead of subroutines. When we write
a statement such as
x := Sin(y + 3)
in ALGOL 60, what we have in mind is the mathematical functions sine and
addition. It is true that our machines can only provide an approximation to
these functions but the discrepancies are generally small and we usually start
by ignoring them. It is only after we have devised a program which would be
correct if the functions used were the exact mathematical ones that we start
investigating the errors caused by the finite nature of our computer.
This is the "mathematical" approach to writing programs; we are chiefly
interested in the values of the expressions and not in the steps by which they
are obtained. The alternative, earlier approach, which might be called "opera-
tional," involves specifying in detail the sequence of steps by which the result
can be obtained. While the ability to do this is also an important facet of com-
puting, it should be regarded as a means to an end; the important thing is to
compute the right quantity. It is generally much easier to prove that one par-
ticular program provides an approximation to a mathematically exact function
Christopher Strachey 53
1. Mathematical Basis
1.1 Functions
1.2 Domains
There are two main classes of domain: elementary and compound. The el-
ementary domains correspond to the familiar simple types in programming
languages: their properties are in general mathematical in nature and inde-
pendent of the design choices of a programming language. Some common
elementary domains are:
T Truth values (Booleans)
N Integers
R Reals
Q Character strings (Quotations)
These can all be considered as "data types" to be manipulated by the pro-
gramming language and their properties are the usual mathematical ones. The
various expressions in the programming language may have values which lie
in one or other of these domains. The domain Q is generally confined to those
character strings which are manipulated by the program; the text of the pro-
gram itself is not one of these and so is not regarded as lying in Q.
Compound domains are constructed from other domains, either elemen-
tary or compound, by one of three methods. If Do and Dl are any two domains
(not necessarily different) we write
Do + Dl for their sum
Do X Dl for their product
An element of Do + Dl is either an element of Do or an element of Dl (but
not both).
An element of Do x Dl is an ordered pair whose first component is an
element of Do and whose second component is an element of Dl.
Sums and products of domains can be extended to more than two com-
ponents without any serious difficulty and we shall write expressions such as
Do + Dl + D2 and Do + Dl + [Do x DIl in a rather informal manner to represent
such domains.
There are two notational abbreviations for sums and products which are
sometimes convenient. We write
Dn for D x D x D x ... x D (n factors)
and D* for Dl + D2 + D3 + ...
Examples
• N + R is the domain of numbers which may be either integers or re-
also The arithmetic operators in programming languages often use this
domain.
• R x R is the domain of ordered pairs of reals. A complex number, for
example, can be represented by an element in this domain.
• R6 is the domain of sixtuples all of whose components are reals. A real
vector of dimension 6 might be an element of this domain.
• R* is the domain of all real vectors of any dimenSion.
Christopher Strachey 55
relationship between the name and the thing it denotes is, of course, a func-
tion in the mathematical sense; we shall call it the environment and reserve
the Greek letter p to stand for an individual environment. The functional na-
ture of the environment, which we shall write as Id - D, varies widely from
one programming language to another and is well worth closer study.
The domain (i.e., the set of arguments of p), which we wrote as Id, is the
set of names or identifiers in the programming language. In the sense of § 1.2
above, Id is an elementary domain, and it is also the only domain we shall
encounter whose members are elements of the text of a program, and are
therefore parts of the programming language itself and not the objects ma-
nipulated by it. Id is generally defined by the syntax rules of the programming
language. It is a very simple domain whose only property is the relation of
equality-it is always possible to say if two names are the same or not, but
names in general have no internal structure.
The only remarkable thing about Id is the number of different words which
have been used to describe its members. The fact that they have been called
"names," "identifiers" and "variables" in different languages would not mat-
ter so much if these same words had not also been used with quite different
meanings. I have preferred to use the word "name" or sometimes "identifier"
for a member of Id as I think this accords best with the non-technical use of
the word, but the reader should be warned that both ALGOL 60 and ALGOL 68
use the word "name" in quite different senses. ALGOL 60 uses the term "call-
by-name" where "call-by-substitution" would be more appropriate; ALGOL 68
uses the word "name" in an equally incorrect and even more misleading man-
ner to mean approximately what is generally meant by the word "address."
The range of p-i.e., those things which can be given names-will be writ-
ten as D. (In the earlier parts of this paper D has been used as a general symbol
for any domain. In what follows it will be reserved for the domain of denota-
tions.) In many languages D is a large compound domain with many compo-
nent parts. It must include anything which can be passed to a procedure as a
parameter (as inside the procedure this will be denoted by the corresponding
formal parameter) as well as the objects declared as local or global variables.
Thus in ALGOL 60, D must include procedures (and type-procedures), labels,
arrays and strings.
The domain D does not, however, include integers, reals or booleans. The
reason for this is that we want to preserve the static nature of p. In ordi-
nary mathematics, the thing denoted by a name remains constant inside the
lexicographical scope of the name; it is determined only by context and not
by history. In programming languages this is also true of some names-for
example, procedures in ALGOL 60; once declared they keep the same meaning
(denotation) throughout their scope. On the other hand, for names declared
in ALGOL 60 as real, integer or Boolean, it is possible to change the value as-
sociated with the name by an assignment command. For names such as these,
the associated integer, real or boolean value can only be obtained dynami-
cally and depends on the current state of the store of the machine. In spite
of this, however, the address in the store associated with the name remains
constant-it is only the contents of this address which are altered by the as-
signment command. It is therefore appropriate to say that the name denotes
58 Chapter 2. The Varieties of Programming Language
a location which remains fixed, and that the ordinary value associated with
the name is the contents of this location. The location is sometimes known
as the L-value of the name and its content is called the R-value. The concepts
of an L-value, which is a location, and the corresponding R-value which is its
content, can be extended to cover expressions (for example, array elements)
as well as names.
We therefore need an elementary domain of locations; which we shall call
L, and it must be a component part of D for all languages which have an
assignment command.
b. Derived Domains
Expression Values
E=D+V
E must contain D because a name by itself is a simple form of expression.
Procedures
Calls by Name
W = 5- [E x 5]
Formal parameters called by name are rather like type-procedures (functions)
with no parameters; they produce a value and may have a side effect and so
alter the store. The value they produce, however, is not confined to V but may
be anywhere in E.
c. Characteristic Domains
Denotations
D L (booleans, integers, reals)
+ P (procedures, type-procedures)
+ L** (arrays)
+ W (calls by name)
+ W* (switches)
+ Q (strings)
+ J (labels)
62 Chapter 2. The Varieties of Programming Language
V=T+N+R
Note that V is rather small and D very large.
3.2 PAL
PAL is a language developed by Evans and Wozencraft (7) at MIT for teaching
purposes. It is an unusually "clean" language but difficult to implement ef-
ficiently. It resembles GEDANKEN of Reynolds (8) and EULER of Wirth (9) in
the fact that its type checking and coercion, if any, are done dynamically at
run-time and not at compile time.
a. Elementary Domains
These are the same as for ALGOL 60; viz: T, N, R, Q, J, Land S. The jumps in
PAL are considerably more powerful than those in ALGOL 60, so that J in PAL
is different from J in ALGOL 60; PAL also has some basic operators on Q.
b. Derived Domains
Expression Values
E=D+V
This domain is hardly needed in PAL.
Procedures
There is only one sort of procedure (or function) in PAL. This takes a single
location (L-value) as an argument and produces a single location as a result,
also perhaps altering the state of the machine as a side-effect. The effect of
several arguments can be obtained by handing over a single list of arguments
(a tuple as defined below); a pure procedure, which yields no useful result and
is used, like a command, merely to alter the machine state, is given a dummy
result.
Tuples
These are the only structural values in PAL; they take the place of arrays in
ALGOL 60. A tuple is a vector of locations and is therefore a member of L*.
c. Characteristic DomainS
Denotations
D=L
All names can be assigned to, and so denote locations.
Christopher Strachey 63
Stored Values
v T+N+R (booleans, integers, reals)
+ Q+J (strings, labels)
+ L* (tuples)
+p (procedures)
+ {dummy}
All of the values in PAL (except a single location) can be stored and so are
part of V. Note that L is not itself a member of L* -in that a I-tuple is distin-
guishable from a location. In fact, a I-tuple is an ordinary R-value and can be
assigned or stored.
Note that in contrast to ALGOL 60, D in PAL is very small and V very large.
4. Conclusion
The differences between the domain structure of ALGOL 60 and PAL are very
striking. They lie, moreover, at a rather deep level and do not depend in
any way on the syntax or even the range of basic semantic operations in the
language. They are in some sense structural. It is clear that there are many
important features of a programming language which cannot be revealed in
any analysis as general as this; there are also some further structural features
which are not made evident by a study of the domains. (An example of this
is the different way in which ALGOL 60 and PAL deal with type checking and
coercion.) In spite of this inevitable incompleteness, I think it would be well
worth the effort of any language designer to start with a consideration of the
domain structure.
The general idea of investigating the domain structure of programming
languages grew from a collaboration between the author and Dana Scott which
started in the autumn of 1969. Our main objective was to produce a mathe-
matical theory of the semantics of programming languages. A general outline
of this work is given in Scott [l]; Scott and Strachey [4] gives an introduction
to the theory of mathematical semantics based on these ideas. Other papers
in print [2, 3] and in preparation [5, 6] give further details. Much remains to be
done before we have a reasonably complete theory and we hope to continue
our work along these lines.
References
[IJ D. S. Scott. Outline of a mathematical theory of computation. In Proceedings
of the Fourth Annual Princeton Conference on Information Sciences and Systems,
pages 169-176, Princeton, 1970. Department of Electrical Engineering, Prince-
ton University. Also Technical Monograph PRG-2, Oxford University Computing
Laboratory, Programming Research Group, Oxford.
[2J D. S. Scott. The lattice of flow diagrams. In E. Engeler, editor, Symposium on Se-
mantics of Algorithmic Languages, volume 188 of Lecture Notes in Mathematics,
pages 311-66. Springer-Verlag, Berlin, 1971. Also Technical Monograph PRG-3,
Oxford University Computing Laboratory, Programming Research Group, Oxford.
64 Chapter 2. The Varieties of Programming Language
2Editors' note: a paper with this title was published by Dana Scott in SIAM]. of Computing,
5:522-87, 1976.
Part II
BASIC PRINCIPLES
Chapter 3
The Essence of ALGOL
John C. Reynolds
Contents
1 The Influence of Models of ALGOL 67
2 Some Principles 69
3 Data Types and Expressions 71
4 The Simple Imperative Language 73
5 Procedures and Their Declarations 77
6 Variable Declarations 79
7 Call by Value 82
8 Arrays 82
9 Labels 83
10 Products and Sums 84
11 Final Remarks 86
References 87
2 Some Principles
The preceding somewhat biased history is intended to motivate a new model
that I believe captures the essence of ALGOL and can be used to develop a
more uniform and general "IDEALIZED ALGOL" retaining the character of its
prototype. Although its genesis lies in the definition of the simple imperative
language given in [19], the crux of the model is a treatment of procedures and
block structure developed by F. J. Oles and myself.
This paper only describes the basic nature of the model, and it avoids the
mathematical sophistication, involving universal algebra and category theory,
that is needed to reveal its elegance. A complete and mathematically literate
description is given in [20].
It should be emphasized that the description of "IDEALIZED ALGOL" in this
paper is extremely tentative and only intended to illustrate the model.
Before delving into the details, we state the principles that we believe em-
body the essence of ALGOL:
1. ALGOL is obtained from the Simple imperative language by imposing
a procedure mechanism based on a fully typed, call-by-name lambda
calculus.
In other words, Landin was right in perceiving the lambda calculus underlying
ALGOL, but wrong in embracing call by value rather than call by name.
70 Chapter 3. The Essence of ALGOL
I /I~
<integer exp> or <real exp> + <real exp>
/I~ I I
<integer exp> + <integer exp> <integer exp> <integer exp>
Except for overflow and (with unfortunate hardware) roundoff, both parses
should have the same meaning.
4. Facilities such as procedure definition, recursion, and conditional and
case constructions should be uniformly applicable to all phrase types.
John C. Reynolds 71
This principle leads to procedures whose calls are procedures, but under a
call-by-name regime such procedures do not violate a stack discipline in the
way that, for example, function-returning functions in GEDANKEN violate such
a discipline. More interestingly, this principle leads to conditional variables
and procedures whose calls are variables; indeed arrays can be regarded as a
special case of the latter.
5. The language should obey a stack discipline, and its definition should
make this diScipline obvious.
Almost any form of language definition can be divided into primary and sec-
ondary parts, e.g.
Primary Secondary
By "should make the stack discipline obvious" we mean that the stack disci-
pline should be a consequence of the primary part of the language definition.
Specifically, the primary part should show that the execution of a command
never changes the "shape" of the store, Le. the aspect of the store that reflects
storage allocation.
• Boolean
• integer
and say that T is a subtype of T' when T ::; T'.
For each data type T there is a phrase type T exp(ression), and these phrase
types inherit the subtype relation of the data types:
• real exp
• Boolean exp
• integer exp
When (J ::; (J' we again say that (J is a subtype of (J', now meaning that any
phrase of type (J can appear in any context requiring a phrase of type (J',
e.g. any integer expression can occur in any context requiring a real expression.
72 Chapter 3. The Essence of ALeoL
univ:s; ()
74 Chapter 3. The Essence of ALeoL
<comm, rr> ::= skip I <T acc, rr> := <T exp, rr>
I <comm, rr> ; <comm, rr>
I while <Boolean exp, rr> do <comm, rr>
<e, rr> ::= if <Boolean exp, rr> then <e, rr> else <e, rr>
I case <integer exp, rr> of «e, rr>, ... , <e, rr»
In the last two lines, e stands for any phrase type, including the nonprimitive
types to be introduced later. The minimal typing property holds for these
productions if, in the partial ordering of phrase types, every finite set with
an upper bound has a least upper bound. In fact, the achievement of this
property for primitive phrase types was the real goal of the arguments about
acceptors, variables, and univ at the beginning of this section.
For mathematical Simplicity, it is tempting to make the partial ordering of
phrase types into a lattice by introducing a phrase type ns (nonsense), of which
all phrase types are subtypes. However, although a nonsense type simplifies
certain theoretical techniques, as in [19), it is not germane to the purposes of
this paper.
A complete semantic definition of the simple imperative language is given
in [19]; here we will only delineate the basic nature of such a definition by
giving its domain equations. For each phrase type e, there is a domain of
meanings De, and for each type assignment rr, there is a domain of envi-
ronments Envrr, which is the product IT! E dom(rr) Drr (!) of the domains for
the type of each identifier in dom(rr). Then for each phrase class <e, IT>
there is a semantic function from phrases to environments to meanings,
i.e. /le,rr E <e, IT> ~ (Envrr ~ De).
For direct semantics Dconun is a domain of state transitions, i.e. 5 ~ 51.,
where 5 is the set of states of the store (hereafter Simply called states), and
51. indicates the formation of a domain by adding an undefined element .L
(denoting nontermination) to the set 5. Similarly D Texp is 5 ~ (VT ) 1-, where
Vinteger is the set of integers, Vreal is the set of real numbers, and VBoolean is the
set {true, false}.
There are two ways of treating variables. The more conventional is to say
that, for each data type, a state has a component mapping an appropriate set
of "L-values" (or "names" or "references" or "abstract addresses") into values
of that data type, i.e.
5 = (Linteger ~ Vinteger) x (Ireal ~ Vreal) X (I Boolean ~ VBoolean)·
Then DTVar is 5 ~ (IT) 1-.
A preferable approach, however, avoids any commitment to a notion such
as I-values or references, and more clearly reveals the relationship among
variables, acceptors, and expressions. One regards the meaning of an acceptor
as a function mapping each value into the state transformation caused by
assigning that value to the acceptor, so that DTacC = V T ~ (5 ~ 51-). Then
the meaning of a variable is a pair of functions describing its meanings in its
dual roles of acceptor and expression, so that DTI T2 var = DTI acc x DT2 expo The
John C. Reynolds 75
Deomm(S) = S - S1.,
DTexp(S) = S - (VT)1.,
Dncc(S) = VT - Deomm(S),
DTIT2var(S) = DT1aeC<S) X DT2exp (S).
However, although the semantics of a phrase is a family of environment-to-
meaning functions, the members of this family must bear a close relationship
to one another. Roughly speaking, whenever a state set S can be "expanded"
into another state set S', the semantics of a phrase for S must be related to its
semantics for S'.
To make the notion of expansion precise, we first introduce some useful
notation:
Identity and composition of functions
We write Is for the identity function on S, and . for functional composi-
tion in diagrammatic order (so that (f· g)(x) = g(f(x») ).
Strict extension
When f E S - S~, we write fil for the .i-preserving extension of f to
S1. - S~. Whenf E S - S', we write flo for the .i-preserving extension of
f toS1. - S~.
Identity and composition of state-transition functions
We write Js for the identity injection from S to S1.' When f, 9 E S - S1.,
*
we write f 9 for f· (gil) E S - S1.'
76 Chapter 3. The Essence of ALGOL
Diagonalization
We write Ds for the continuous function from S - S - SJ. to S - SJ.
such that Ds(h)(a) = h(a)(a).
In the last definition (and later in this paper) we assume that - is right asso-
ciative and that function (and procedure) application is left associative.
Then we define an expansion of S to S' to be a pair (g, G) of functions
9 E S' - S, G E (S - SJ.) - (S' - S~) such that
1. G is continuous and .L -preserving.
2. GUs) = Is'.
[rr 1 LI: e l 1···1 L,,: ell] = [ ... [rr 1 LI: eI]···1 L,,: ell],
[LI : ell· .. 1 L" : ell] = [€ 1 LI : ell· .. 1 L" : ell],
where € is the type assignment with empty domain. Note that the latter form
can be used to notate any type assignment explicitly.
The obvious approach to semantics is to take the meanings of phrases of
type e l - e2 to be continuous functions from meanings of phrases of type e l
to meanings of phrases of type e 2, i.e. Do 1 -o 2(5) = DOl (5) - D02 (5). However,
when we consider variable declarations in the next section we will find that this
approach conflicts with Principle 5.
Even in the absence of a definite semantics, meaning can be clarified by
equivalences. We write P =O,TT Q to indicate that IJO,TT(P) = IJO,TT(Q), i.e. that P
and Q have the same meaning when regarded as phrases in <e, rr>.
First we have the standard equivalences of the (typed) lambda calculus. If
P E <e2, [rr 1 L: e11> and Q E <e I , rr>, then
(beta reduction)
where Plt-Q denotes the result of substituting Q for the free occurrences of L
in P, with appropriate renaming of bound identifiers in P. If P E <e t - e2, rr>
and L ~ dom(rr), then
(eta reduction)
Next, an obvious equivalence describes the fixed-point property of rec. If
P E <e - e, rr>, then
rec P =O,TT P(rec P).
Finally, two equivalences relate procedures to the conditional construction.
If P E <Booleanexp, rr>, Q,R E <eI - e2, rr> and 5 E <e lo rr>, then
(if P then Q else R)(5) =02,TT if P then Q(5) else R(5).
If P E <Booleanexp, rr>, Q,R E <e2, [rr 1 L: eI]>, and L ~ dom(rr), then
At: eI.if P then Q else R =OI-02,TT if P then At: e l . Q else At: eI.R.
For the declaration of procedures, we prefer the let and letrec notation of
Landin [3] to that of ALGOL 60; it is uniformly applicable to all phrase types
(not just procedures), it distinguishes clearly between nomecursive and recur-
sive cases, and it doesn't make declarations look like commands. The syntax
is
<e, rr> ::= let LI be <e I , rr> & ... & l" be <ell, rr> in <e, rr'>
Iletrec II : e l be <e lo rr'> & ... & L,,: ell be <ell, rr'> in <e, rr'>
where rr' = [rr 1 lI: ell· .. 1 L,,: ell]' (Note that the types e lo ••• , ell must be
stated explicitly for letrec but not let.)
This notation can be defined as syntactic sugar in terms of application,
abstraction, and rec. The nomecursive let construction is straightforward, If
PI E <e lo rr>, ... ,P" E <e",rr>, andP E <e,rr'>, then
let II be PI & ... & L" be P" in P =O,TT
(ALl: e l .... AL,,: e".p)(PIl ... (PI!)
John C. Reynolds 79
This equivalence can be used to remove all occurrences of let from a pro-
gram without changing its meaning. Although it is formally similar to the
equivalence given by Landin [3], it has a different import since call by name is
being used instead of call by value. For example, if E is an integer expression,
then let x be E in 3 has the same meaning as ('\x: integer expo 3) (E) which, by
beta reduction, has the same meaning as 3, even when E is nonterminating. If
x and yare integer variables, let z be x in (x:= 4;y:= z) has the same mean-
ing as ('\z: integer integer var. (x := 4; y := z») (x) which, by beta reduction,
has the same meaning as x := 4; y := x.
To treat the recursive letrec construction, we will first define the nonmul-
tiple case and then show how multiple declarations can be reduced to this
case. For the nonmultiple case we follow Landin: If PI E <e1. [rr I LI: ell> and
P E <e, [rr I LI: ell>, then
letrec LI: e l be PI in P =IJ.TT (ALl: eI.P)(rec ALl: eI.Pd.
For the multiple case we give an equivalence, suggested by F. J. Oles,
that avoids the use of products of phrase types. If PI E < e I , rr' >, ... ,
Pn E <en, rr'>, and P E <e, rr'>, where rr' = [rr ILl: e l I ... I Ln: en], then
letrec LI : e l be PI & ... & Ln : en be Pn in P =IJ.TT
letrec LI : e l be
(letrec L2 : e2 be P2 & ... & Ln: en be Pn in PI)
in (letrec L2 : e2 be P2 & ... & Ln : en be Pn in P).
6 Variable Declarations
To declare variables, we use the syntax
<comm, rr> ::= new T var L in <comm, [rr I L: TTvar]>
(Note that declared variables always accept and produce the same data type.)
However, since this construction involves binding we want to desugar it into a
form in which the binding is done by a lambda expression. The solution is to
introduce the more basic construction
<comm, rr> .. - newvar(T) <TTvar - comm, rr>
and to define
new T var L inP =comm.TT newvar(T)'\L:TT var.P,
where P E <comm, [rr I L: TT var]>.
Semantically, variable declarations raise a serious problem. The conven-
tional approach is to permit the set S of store states to contain states with
different numbers of L-values, and to define variable declaration to be an op-
eration that adds an L-value to the state. For example, one might take a state
to be a collection of strings of values for each data type
S = V~teger X V~ X V800lean ,
and define the declaration of a T variable to be an operation that adds one
more component to the string of values of type T.
80 Chapter 3. The Essence of ALGOL
The problem with this view is that it violates Principle 5 by obscuring the
stack discipline. Execution of a command containing variable declarations per-
manently alters the shape of the store, i.e. the number of I-values or the length
of the component strings. In effect, the store is a heap without a garbage col-
lector, rather than a stack. It is hardly surprising that this kind of model
inspired languages that are closer to LISP than to ALGOL.
Our solution to this difficulty takes advantage of the fact that the seman-
tics of a phrase is a family of environment-to-meaning functions for differ-
ent sets of states. Instead of using a single set containing states of different
shapes and regarding variable declaration as changing the shape of a state,
we use sets of states with the same shape and regard variable declaration as
changing the set of states. Specifically, if C is new T var L in C', then the se-
mantics of C for a state set 5 depends upon the semantics of C' for the state
set 5 x VT • Thus, since the semantics of C for 5 maps an environment into a
mapping in Dcomm(5) = 5 - 5.1., it is obvious that executing C will not change
the shape of a state.
To make this precise, suppose C' E <comm, [1T I L: TT var]>, so that
C E <comm, 1T>. We first note that 5 and 5 x VT are related by the expansion
(g, G) in which 9 is the function from 5 x VT to 5 such that g( (0", v») = 0" and
G is the function from 5 - 5.1. to (5 X VT ) - (5 X VT).1. such that
This expansion induces a function EnV1T «g, G» from EnV1T (5) to Env1T (5 x V T ).
Let e be the function from 5 x VT to (VT).1. such that e«O", v») = v, and a be
a function from VT to (5 x VT ) - (5 x VT).1. such that a(v')«O", v» = (0", v').
Then (a, e) E D TTvar (5 X VT ) is an appropriate meaning for the variable being
declared.
To obtain the meaning of new T var L in C' for the state set 5 and an
environment 11 E Env1T (5), we use Env1T «g,G» to map 11 into Env1T (5 x VT )
and then alter the resulting environment to map L into (a, e), obtaining
11' = [Env 1T «g,G»)(I1) I L: <a,e>].
Then we take the meaning of C' for the state set 5 x VT and the environment
11', and compose this meaning, which is a state-transition function from 5 x V T
to (5 X VT).1., with a function that initializes the new variable to some standard
initial value initT E VT , and a function which forgets the final value of the
variable:
IlcOlnm,1T(new T var L in C')(5)(11) =
state set S at the point of definition, though there must be an expansion from
S toS'.
As a consequence, a member P of DO I -02 (S) must be a family of functions
describing the meaning of a procedure for different S'. Moreover, each of these
functions, in addition to accepting the usual argument in DOl (S') must also
accept an expansion from S to S' that shows how the states of S are embedded
in the richer states of S'.
As one might expect, the members of the family P must satisfy a strin-
gent relationship (which can be expressed by saying that P is an appropriate
kind of natural transformation). A precise definition is the following (where
expand(S, S') is the set of expansions from S to S'): P E DO I -02 (S) if and only
if P is a state-set-indexed family of functions,
p(S') E expand(S, S') x DOl (S') - D02 (S'),
D02 «g', G'» (p(S')«g, G), a») = p(S"){ (g' . g, G . G'), DOl «g', G') )(a»).
To make DO I -02 (S) into a domain, its members are ordered pointwise, i.e. PI !;;;
P2 if and only if ('itS') PI (S') !;;; P2(S').
Finally, we must say how an expansion from S to S' induces a function
from Do I -02 (S) to DoI -02 (S'): If (g,G) E expand(S,S') and P E Do I -02 (S),
then Do I -02 «g,G»(p) E Do I -02 (S') is the family p' of functions such that,
for allS", (g',G') E expand(S',S"), and a E DOl (S"),
p'(S")«g',G'),a) = p(S")«g'· g,G· G'),a).
I Editors' note: this statement is slightly Inaccurate because DOrrF is nor Cartesian dosed;
however. a dosely related category is. See [20). or Chapter 11. page 6.
82 Chapter 3. The Essence of ALGOL
7 Call by Value
<comm, [rr I ( : T exp] > ::= T value (in <comm, [rr I (: TT var] >
where C E <comm, [rr I 1: TT var] > and l' rt dom(rr) u {I}. (This is only a gen-
eralization of call by value for proper procedures; an analogous generalization
for function procedures would require block expressions.)
Notice that T value ( in C has a peculiar binding structure: the first oc-
currence of ( is a binder whose scope is C, yet this occurrence is itself free.
(A similar phenomenon occurs in the conventional notation for indefinite in-
tegration.)
Call by result, as in ALGOL W [22], can obviously be treated similarly.
8 Arrays
Arrays of the kind used in ALGOL 60 can be viewed as procedures whose calls
are variables. Thus an n-dimensional T array is a phrase of type
integer exp - ... - integer exp - TT var.
\ T '
n times
(Notice that this is a phrase type. If arrays were introduced as a data type, one
could assign to entire array variables (as in APL) but not to their elements.)
The declaration of such arrays is a straightforward generalization of vari-
able declarations, and can be desugared similarly. The details are left to the
reader.
Unfortunately, this kind of array, like that of ALGOL, has the shortcom-
ing that it does not carry its own bounds information. A possible solution is
to introduce, for each n ~ 1, a phrase type array(n, T) that is a subtype of
the type displayed above, and to provide bound-extraction operations that act
upon these new phrase types. The concept of array in [28] could be treated
Similarly.
John C. Reynolds 83
9 Labels
Since all one can do with a label L is to jump to it. its meaning can be taken to
be the meaning of goto L. Thus labels can be viewed as identifiers of phrase
type comm. and goto L can simply be written as L.
However. as suggested in ALGOL 68. labels denote a special kind of com-
mand. which we will call a completion. that has the property that it never
returns control to its successor. If completions are not distinguished as a
separate phrase type. it becomes difficult for either a human reader or a com-
piler to analyze control flow. particularly when procedure parameters denot-
ing completions are only specified to be commands. To avoid this. we intro-
duce compl(etion) as an additional phrase type that is a subtype of comm (so
that completions can always be used as commands but not vice-versa).
Thus labels are identifiers of phrase type compI. Moreover. the production
schemas for conditional and case constructions. procedure application. and
recursion provide a variety of compound phrases of type compI. This variety
can be enriched by the following syntax. in which various ways of forming
commands are used to form completions:
<compl.1T> ::= <comm. 1T> ; <compl. 1T>
I new T var L in <compl. [1T I L: TT var]>
I newvar(T) <TT var - compl.1T>
<compl. [1T I L : T exp]> ::= T value Lin <compl. [1T I L: TT var]>
Two more schemas suffice to describe commands and completions in which
labels are declared in an ALGOL-like notation:
<comm.1T> ::= <comm.1T'>; Ll: <comm.1T'>; ... ; Ln: <comm.1T'>
where LIo •••• Ln are distinct and 1T' = [1T I Ll:compll ... I Ln:compl];
<compl.1T> ::= <comm.1T'>; Ll: <comm.1T'>; ... ; Ln: <compl.1T'>
where LIo •••• Ln are distinct and 1T' = [1T I Ll: compl; ... I Ln: compl]. Since
these declarative constructions involve binding. we must desugar them into
more basic forms. For this purpose. we introduce an escape operation that is
a parameterless variant of Landin's J-operator [3].
<comm.1T> ::= escape <compl - comm.1T>.
This operation can be described in terms of a conventional label declaration:
If P E <compl - comm.1T> and L rt dom(1T). then
escape P =comm,rr (P(L); L: skip).
Our present goal. however. is the reverse. To describe label declara-
tions in terms of escapes. we proceed in two steps. First. we describe
a label-declaring command in terms of a label-declaring completion by
adding a final jump to an enclOSing escape: If LIo .... Ln. L are distinct iden-
tifiers. 1T' = [1T I Ll: compll ... I Ln: compl]. Co ..... Cn E <comm.1T'>. and
L rt dom(1T). then
Co; Ll: Cl ; ... ; Ln: Cn =comm,rr
escape At: compI. (Co; Ll: Cl; .•• ; Ln: (Cn ; L»).
Then we describe a label-declaring completion in terms of recursive defini-
tions: If LIo •.•• Ln are distinct identifiers. 1T' = [1T I Ll: compll ... I Ln: compl].
Co ....• Cn -l E <comm.1T'>. and K E <compl.1T'>. then
84 Chapter 3. The Essence of ALGOL
letrec Ll:compl be (Cl; (2) & ... & Ln_l:compl be (CIl - 1 ; LIl )
& LIl : compl be K
in (Co; Ld.
We have chosen to de sugar the ALGOL notation for declaring labels because
of its familiarity. Other, possibly preferable, notations can be treated similarly;
for example, Zahn's event facility [29] can be described by escapes without
recursion. Actually, the wisest approach might be to avoid all syntactic sugar
and simply provide escapes.
Semantically, the introduction of labels requires a change from direct to
continuation semantics, which will not be discussed here. In [20] it is shown
that hidden abstraction on state sets can be extended to continuation seman-
tics, though with a different notion of expansion.
11 Final Remarks
I have neglected the topic of program proving since I have discussed it else-
where at length. Although Hoare's work on proving procedures is incompat-
ible with call by name and procedure parameters, an alternative approach
called specification logic appears promising. In [23] this logic is formulated
for a subset of ALGOL W; in [24] it is given for a language closer to that de-
scribed here.
Like ALGOL itself, our illustrative language raises problems of interference,
i.e. variable aliaSing and interfering side effects of statements and proper pro-
cedures. The language is rich enough that an assertion that two phrases do
not interfere must be proved (as in specification logic) rather than derived syn-
tactically. Several years ago in [25], I attempted to restrict a language like that
described here to permit interference to be detected syntactically. Unfortu-
nately, this work led to some nasty syntactic complications (described at the
end of [25]) that have yet to be resolved. Still, I have hopes for the future of
this approach.
John C. Reynolds 87
Although this paper has dealt with nearly all the significant aspects of
ALGOL 60, it has not gone much beyond the scope of that language. More
for lack of understanding than space, I have avoided block expressions, user-
defined types, polymorphic procedures, recursively defined types, indetermi-
nate and concurrent computation, references, and compound data types.
It remains to be seen whether our model can be extended to cover these
topics. Of course, some of them could reasonably be labelled unALGOL-like.
But the essence of ALGOL is not a straitjacket. It is a conceptual universe for
language design that one hopes will encompass languages far more general
than its progenitor.
References
[1) P. Naur et aI., Revised report on the algorithmic language ALGOL 60, Comm. ACM
6 (1) (January 1963) 1-17. See Chapter l.
[2) P. J. Landin, A A-calculus approach, in: L. Fox (Ed.), Advances in Programming
and Non·Numerical Computation (Pergamon Press, Oxford, 1966) pp. 97-14l.
[3) P. J. Landin, A correspondence between ALGOL 60 and Church's lambda-notation,
Comm. ACM 8 (2,3) (February-March 1965) 89-101 and 158-165.
[4) ]. McCarthy, Recursive functions of symbolic expressions and their computation
by machine, Part I, Comm. ACM 3 (4) (April 1960) 184-195.
[5) P. Lucas, P. Lauer and H. Stigleitner, Method and Notation for the Formal Defini-
tion of Programming Languages, TR 25.087, IBM Laboratory Vienna. (june 1968).
Contents
1 Introduction 89
2 Idealized ALGOL 90
3 Conservativity 93
3.1 Semantic Conservativity 93
3.2 Observational Conservativity 95
4 Conclusion 97
Acknowledgements 97
References 98
1 Introduction
In "The essence of ALGOL," Reynolds [1] presents a view of ALGOL as a call-
by-name language based on the typed i\-calculus, with "imperative" primitive
types. A central feature of the design is the interaction between assignment
and procedures. Side effects are wholly isolated in a primitive type comm
of commands, and do not occur when computing a value of functional type.
That is to say, side effects in procedures are latent, in the sense that an effect
occurs only by evaluating a procedure call as a term of type comm. As a
result, function types retain a genuine "functional character." For instance,
the full f3 and 17 laws are valid equivalences in ALGoL-like languages. This
functional aspect of ALGOL has been emphasized strongly by Reynolds [1, 2, 3],
and echoed in the works of Tennent [4, 5] and Weeks and Felleisen [6].
The purpose of this short note is to give a technical result further exem-
plifying this functional character. Specifically, observational (or contextual)
equivalence in a simple idealized ALGOL conservatively extends equivalence in
a Simply-typed assignment-free functional sublanguage. This means that two
program fragments that can be interchanged in all aSSignment-free programs
without affecting observable behaviOur can also be safely interchanged in any
context in the full imperative language. Thus, not only are f3, 17, and so on
preserved, but so are all equivalences from the assignment-free fragment of
the language.
First appeared as "Note on ALGOL and Conservatively Extending Functional Programming," Jour-
nal of Functional Programming, 6(1):171-180, 1995. Reprinted with the permission of Cambridge
University Press.
90 Chapter 4. ALGOL and Functional Programming
2 Idealized ALGOL
Our idealized ALGOL extends Simply-typed functional programming with prim-
itive types for imperative features. We take the language PCF, a typed
A-calculus with recursion and basic arithmetic constructs, as our representa-
tive pure functional language. The language IA (for Idealized ALGOL) extends
PCF with two additional primitive types, the type comm of commands and the
type var of storage variables. Altogether, the types of IA are
t ::= nat I booll var I comm I t - t .
additional type, beyond those of PCF, by defining var as syntactic sugar for
(nat - comm) x nat [l).
Many of the essential properties of IA can be immediately brought to light
by considering a semantics for the types. In the following, each type t deter-
mines an w-complete partial order S[t] with a least element.
S[comm] = S '* S1-
S[nat] = S '* N1-
S[bool] = S'* T1-
S[var] = S '* L1-
S[t' -+ t] = S[t'] '* S[t]
Here, '* is is the continuous function space, T = {tt, ff} is a two-point set (of
truth values), L is a countably infinite set (of locations), N is the set of natural
numbers, and S is a suitable set of states.
The striking point to notice is that the interpretation of the function
type is exactly as in a domain-theoretic semantics of a purely-functional lan-
guage. In comparison, in most imperative languages such as PASCAL, ML, or
SCHEME, the collection of states would be used to interpret functions them-
selves. Furthermore-and this is related to the interpretation of the function
type-side-effects are wholly concentrated in the type comm, since no other
primitive types have the state in an output position. The nat and bool types
are state-dependent, but in a read-only way. These aspects of the language
are an example of what Strachey [20) termed structural properties, on display
from the semantics of types alone, prior to considering primitive operations
or terms at all, let alone operational semantics.
IA is an applied .:\-calculus with certain constants. An infinite set of vari-
ables xt : t, for each type t, is assumed, together with formation rules for
.:\-abstraction and application:
M:s-t N:s M:s
MN:t .:\xt.M: t - s
The constants come in two groups. One group consists essentially of the op-
erations of PPCF, i.e., PCF together with a parallel conditional.
succ, pred: nat - nat
ifb:bool- b - b - b
pifo:bool- 6 - 6 - 6
0: nat
O?: nat - bool
tt,ff:hool
Y t : (t - t) - t
92 Chapter 4. ALGOL and Functional Programming
In the rule for ifb, the sequential conditional, b ranges over all primitive types
including var and comm. In the rule for pif,5, the parallel conditional, 8 ranges
over only nat and boo!. In the rule for Y t , the recursion combinator, t ranges
over all types of IA.
The constants for the imperative fragment of IA are as follows.
:=:var - nat - comm
deref: var - nat
skip:comm
;: comm - comm - comm
new: nat - (var - comm) - comm
new v P creates a local storage variable t, initializes its contents to v, exe-
cutes pet), and de-allocates t on completion. With this explanation, the bind-
ing of an identifier denoting a local variable is accomplished using A, as in
new v (Ax. C).
PPCF is a sublanguage of IA. The PPCF types are
p ::= nat I booll p --+ p.
PPCF terms are build from variables x P , abstraction, application, and the con-
stants just given (with the restriction that, in ifb, b is nat or bool). We will
denote the standard continuous-function model of PPCF by :P[.]. The inter-
pretation of types is as usual:
:P[nat] = NJ.
:P[bool] = TJ.
:P[p' - p] = :P[p'] '* :P[p]
A :P[ . ]-environment u is a type-respecting map that to each variable xP assigns
a value u(x P ) E :P[p], and the meaning of a PPCF term is a (continuous) map
from environments into values so that :P[M]u E :P[p] when M : p. All of the
constants have their usual interpretations, with pif,5 being the parallel condi-
tional. We often suppress mention of environments when speaking of :P[K],
for K one of the given constants. We refer to [9, 21] for detailed definitions.
Returning to lA, to complete the semantics of types we have to define the
set S of states. There are a number of ways to do this, one of the simplest of
which is to set
S = L '* (N + {unused})
The unused portion is used to define the local-variable declarator new. For
this to work, we must assume that there is a partial function new: S ~ L that
selects a new unused location if there is one, and is undefined if all locations
are in use; see the textbook [5] for a more detailed discussion.
An S[· ]-environment u is a function associating an element u(x t ) E S[t]
to each variable xt. The semantic equations in Table 1 define a continuous
function S[M]:E --+ S[t] for M: t, where E is the (componentwise ordered)
domain of environments and (s It ...... v) is like s except that t maps to v. With
the various constants, we have suppressed mention of environments.
Peter W. O'Hearn 93
S[x t ] u = u(x t )
S[Ydf = UiENfi(.l)
S[M(N)] u = S[M] u (S[N]u)
S[O]s = 0
S[.\xt.M]ua = S[M](ulxt-a)
S[O?] as = :P[O?](a(s»)
S[pred]as = :P[pred](a(s»)
S[tt] s = tt
S[succ] as = :P[succ](a(s»)
S[fI]s = If
S[skip]s = s
b(S'), if a(s) = s' *' .l
S[;]abs= { .l, ifa(s)=.l
S[d f] _ {S(-e),
ere as - .l,
if a(s) = -e
if a(s) =.l
*' .l
S[.-] b _ { s(-e - v),
.- a s - . l ,
*'
if a(s) = -e .l, b(s) = v
if a(s) = .l or b(s) = .l
*' .l
b(S)' if a(s) = tt
S[ifb] abc s = { c(s), if a(s) = If
.l, if a(s) = .l
b(S)' if a(s) = tt
S[pif,5]a b C s = { c(s), if a(s) = If or c(s) = b(s)
.l, if a(s) = .l
(s' I-e - unused), *'
if new(s) = -e, e(s) = v .l
S[neW,5]eps = { and p(.\s.-e)(s l-e - v) = s'
.l, otherwise
3 Conservativity
3.1 Semantic Conservativity
The model of IA given in the previous section is standard and, even if it is im-
perfect, it is certainly computationally adequate with respect to a suitable op-
erational semantics [22]. Thus, we may consider the semantics as a reference
point for defining the language. However, the model S[ . ] is not conservative
over P[·], as the following example shows.
Consider the type bool - bool.
S[bool - bool]
The two occurrences of the set S of states allow us to (semantically) evaluate
different parts of an expression at different states. An example is the function
94 Chapter 4. ALGOL and Functional Programming
e( ) = {ff' ~ 0;
if s(.e)
s tt, otherwIse.
Now, let s be a state where s(.e) * o. Then g(e)s = ff while g('\s' .tt)s = tt.
Therefore, if we consider an environment u where u(f) = 9 and u(x) = e,
we get S[M]us = ff while S[N]us = tt. So M and N are not equal in the
model S[·], and semantic equality in the standard model S[·] of IA is not
conservative over equality in the model P[·] of PPCF.
The function 9 is an example of the "snapback" effect, so named because
the state change is not recorded globally in the semantics. For instance, in an
environment where ( denotes function g, an assignment statement x := f{l)
will leave location .e unchanged (unless x denotes .e) because the change to .e
during evaluation of f{l) is temporary.
We now present a semantic model that overcomes this speCific difficulty
pertaining to conservativity. The model does not address the general problem
of irreversibility of state change; see [7, 23, 8] for discussion of this. The aim
is to provide a simple (though ad hoc) work-around that is just enough to
achieve conservativity.
The main idea of the new semantics e[ .] is to push the state as far out-
ward as pOSSible, by interpreting the PPCF fragment in a way that, given any
state s, "compiles" to a meaning in the PPCF model P[·] by reading values of
variables. In intuitive terms, we will maintain the following property for the
PPCF fragment:
e[M]u s = P[M]u' where u' (x) is obtained by "looking up" u(x) in state s
Here is the semantics of types.
e[p] = s ~ P[p] (for PPCF types p)
e[comm] = S[comm]
e[var] = S[var]
e[t' - t] = e[t'] ~ e[t] (provided one of t' or t is not a PPCF type)
Peter W. O'Hearn 95
For PPCF types there is now only one occurrence of S, at the outermost level.
For example, e[bool- bool] = S => (T.L => T.L).
The semantic equations for the PPCF constants must be altered in certain
cases.
e[M(N)] us = e[M] us(e[N]us) (M,N of PPCF type)
e[succ] s :P[succ]
e[pred]s :P[pred]
e[if8] s = :P[if8]
e[pif] s = :P[pif]
e[yp] s = :P[yp]
For the remaining constants and cases, the equations are exactly as for S[ . ].
The non-standard semantics of the PPCF fragment of IA can be easily seen
to satisfy the laws of the typed '\-calculus. In fact, it is just an interpretation
of the typed '\-calculus in the Kleisli category of a monad on the category of
w-complete po sets and continuous functions. The functor part of this monad
is S => (-), and the resultant Kleisli category is cartesian closed.
Lemma 1 (Semantic Conservativity) For all PPCF terms M, N, :P[M] = :P[N]
iff e[M] = e[N].
Proof: For any PPCF term M and e[· ]-environment u, a routine induction
shows that e[M]u s = :P[M]u', where u' is a :P[. ]-environment such that
u' (x) = u(x)s. As a consequence, for any closed PPCF term M, we clearly have
e[M] = '\s E S.:P[M], and so the result holds for closed terms. For open
terms M and N the result follows by considering closures '\"x.M and ,\il.N,
which are equal iff M and N are (by virtue of '\-calculus laws). I
The reader may enjoy explicitly verifying that the terms M and N from the
example at the beginning of this section are indeed equivalent in e[·].
There are typical implicit provisos in this definition, such as that M and N
be of the same type and that C[·] be a context that captures all their free
identifiers.
As we indicated before, we take the standard model S[ .] as defining lAo
The model e[ . ], though non-standard, is adequate with respect to this model.
Lemma 3 (Adequacy) For all closed IA terms M of ground type, S[M] = 1. iff
e[M] = l..
Proof: The proof uses a standard "logical-relation" argument [5, 21) to con-
nect the meanings in the two models. Given (complete and pointed) relations
Rb !; e[b] x S[b] on lA primitive types, we lift to higher types by the clauses:
where one of t or t' is not a PPCF type. Then taking Rb as the equality relation,
this generates a family of relations. One checks that each constant of lA is
invariant under the resulting relation, using the fact that each Rt is pointed
and closed under lubs of w-chains in the case of fixed-point. One then shows
that the meanings of all terms map related environments to related meanings
in the usual way, and adequacy follows. I
The interesting part of this argument is the use of the non-standard model
of lAo It shows that the presence of snapback operations is the only reason for
the failure of conservativity in standard models of ALGOL. The result also illus-
trates, by way of equivalences, some of the undesirable properties of snapback
operations, and thus weaknesses in the models of, e.g., [24, 7, 25). Among the
more advanced models of ALGoL-like languages, Tennent's (26) model of spec-
ification logic is the only one in which a semantic conservativity result holds.
Peter W. O'Hearn 97
4 Conclusion
Reynolds's ALGOL, unlike ALGOL 60, disallows side effects in integer and
boolean expressions. This leads to a clear distinction between the types of
phrases (integers, booleans) that are evaluated for the value they produce, and
commands, which are evaluated solely for their side effects. Analogous con-
servation results typically fail for languages where there is a less strict separa-
tion. For instance, in ML or SCHEME procedure invocation is inextricably bound
up with state change, and equivalences such as f(l) + f(2) == f(2) + f(l) that
(viewed at an appropriate level of abstraction) hold in the effect-free subset-
what is often referred to as the "pure" subset-do not hold in contexts where
f can have a side effect. In versions of ALGOL that allow side effects in ex-
preSSions, such as [6], conservativity is also lost, though the laws of the typed
A-calculus remain valid.
Some recent proposals for integrating imperative and functional program-
ming also use types to isolate effects from the procedure mechanism [13, 15].
A type T(a) is used for state transformers that change the state and also re-
turn a value of type a; the type comm in IA resembles T(unit) for a type
unit with a trivial value. In these languages integer and boolean expressions
are completely state-independent, whereas in IA expressions are read-only or
passive, in that they are state-dependent but side-effect free. The imperative
A-calculus [10] is even closer to lA, but also uses state-independent expres-
sions. In order to maintain equational laws in a setting that does not allow for
passive or read-only types, excessive sequencing of dereferencing operations
is required. This is one of the motivations for considering general notions of
passivity [27, 11, 28, 29].
Although every specific equation true in the functional sublanguage re-
mains true in lA, it is important to note that not all "global properties" of
equivalence are preserved. One example is the context lemma [30]: two closed
terms M,N of functional type in PPCF are equivalent iff MY == NY for all
closed vectors Y of arguments. This property fails in IA already at the type
comm --+ comm. For instance, the procedures Ac.c and Ac.c;c are not ob-
servationally equivalent, but closed applicative contexts are not sufficient to
distinguish them: up to equivalence, skip and n are the only closed terms of
type comm in IA. To create a distinguishing context we must use new, as in
This failure of the context lemma might be attributed to the presence of im-
pure features in lA, though it is difficult to make this attribution precise since
"impure" is ill-defined.
Acknowledgements
Thanks to Uday Reddy, Jon Riecke and Bob Tennent for comments and encouragement.
98 Chapter 4. ALGOL and Functional Programming
References
[1) J. C. Reynolds. The essence of ALGOL. In J. W. de Bakker and J. C. van Vliet, ed-
itors, Algorithmic Languages, pages 345-372, Amsterdam, October 1981. North-
Holland, Amsterdam. See Chapter 3.
[2) J. C. Reynolds. Preliminary design of the programming language FORSYTHE. Re-
port CMU-CS-88-159, Computer Science Department, Carnegie Mellon Univer-
sity, June 21, 1988. See Chapter 8.
[3) J. C. Reynolds. Replacing complexity with generality: The programming language
FORSYTHE. April 12 1991. See Chapter 8.
[4) R. D. Tennent. Elementary data structures in ALGoL-like languages. Science of
Computer Programming, 13:73-110, 1989.
[5) R. D. Tennent. Semantics of Programming Languages. International Series in
Computer Science. Prentice-Hall International, 1991.
[6) S. Weeks and M. Felleisen. On the orthogonality of assignments and procedures
in ALGOL. In POPL [31), pages 57-70. See Chapter 5.
[7) P. W. O'Hearn and R. D. Tennent. Parametricity and local variables. ]. ACM,
42(3):658-709, May 1995. See Chapter 16.
[8) P. W. O'Hearn and U. S. Reddy. Objects, interference and the Yoneda embedding.
In Brookes et al. [32).
[9) G. D. Plotkin. LCF considered as a programming language. Theoretical Computer
Science, 5:223-255, 1977.
[10) V. Swarup, U. S. Reddy, and E. Ireland. Assignments for applicative languages.
In J. Hughes, editor, Functional Programming and Computer Architecture, vol-
ume 523 of Lecture Notes in Computer Science, pages 193-214, Cambridge, Mas-
sachusetts, August 1991. Springer-Verlag, Berlin. See Chapter 9.
[11) P. Wadler. Linear types can change the world! In M. Broy and C. Jones, editors, IFIP
TC-2 Working Conference on Programming Concepts and Methods, pages 347-
359, Sea of Galilee, Israel, April 1990. North Holland, Amsterdam.
[12) P. Wadler. Comprehending monads. Mathematical Structures in Computer Sci-
ence, 2:461-493, 1992.
(13) P. Wadler. A syntax for linear logic. In S. Brookes et al., editors, Mathematical
Foundations of Programming Semantics, volume 802 of Lecture Notes in Com-
puter Science, pages 513-529, New Orleans, 1993. Springer-Verlag, Berlin.
(14) J. Guzman and P. Hudak. Single-threaded polymorphic lambda calculus. In Pro-
ceedings, Fifth Annual IEEE Symposium on Logic in Computer Science, pages 333-
343, Philadelphia, PA, 1990. IEEE Computer Society Press, Los Alamitos, Califor-
nia.
[15) J. Launchbury and S. Peyton Jones. State in HAsKELL. LIsp and Symbolic Compu-
tation, 8(4):293-341, December 1995.
[16) M. Odersky, D. Rabin, and P. Hudak. Call by name, assignment, and the lambda
calculus. In POPL [31), pages 43-56.
[17) M. Odersky. A functional theory of local names. In Conference Record of the
21st ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages.
ACM, New York, 1994.
[18) J. G. Riecke. Delimiting the scope of effects. In ACM Conference on Functional
Programming and Computer Architecture, pages 146-158. ACM, New York, 1993.
Peter W. O'Hearn 99
Contents
1 Introduction 102
2 An Idealized Dialect 102
2.1 Syntax and Informal Semantics 103
2.2 Semantics and Calculus 105
2.3 Characteristics of eval 107
3 Postponement 109
4 Strong Normalization 113
5 Extensions and Alternatives 115
Acknowledgements 116
References 116
A Church-Rosser Theorem for ia 118
B Uniform Evaluation 120
C Postponement 121
D Strong Normalization 123
Revision by the authors of a preliminary version that appeared in Conference Record of the Twen-
tieth Annual ACM SIGPLAN-SIGACT Symposium on Principles of Programming Languages, pages
57-70, Charleston, South Carolina, 1993. ACM, New York. © 1993 Association for Computing
Machinery.
102 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL
1 Introduction
According to folklore, ALGOL 60 "orthogonally" extends a simple imperative
programming language with a typed A-calculus. The underlying imperative
language consists of a standard collection of constructs: assignment state-
ments, branching statements, loops, and statement sequences. The typed
A-calculus adds recursive higher-order procedures, which provide the power
to abstract over algorithms in the command language. In his most recent de-
scription of FORSYTHE, a modern variant of ALGOL, Reynolds expounds this
view, which he says is
implicit in ALGOL 60, underlies FORSYTHE, and distinguishes it from such
languages as ALGOL 68, SCHEME, and ML: the programming language is
a typed lambda calculus with a primitive type comm, such that terms
of this type, when reduced to normal form, are programs in the simple
imperative language. [18, p. 3]
Moreover, he reiterates the common belief that, as a result of this deSign, a
FORSYTHE program
is executed in two phases. First the program is reduced to normal form.
(In ALGOL jargon, the copy rule is repeatedly applied to eliminate proce-
dure calls.) Then the reSUlting simple imperative program is executed.
[18, p. 3]
Given the recent interest in integrating variations of assignment statements
into functional programming languages in a controlled manner and in the de-
sign of simple logics for such mixed languages [2, 8, 9, 11, 12, 15,21], these
folklore claims about ALGOL'S design clearly deserve a rigorous analysis. On
one hand, such an analysis enhances our understanding of the role of "orthog-
onality" in ALGOL. l On the other hand, the results improve our understanding
of the simple equational logics for mixed functional-imperative programming
languages and of their abstract implementation.
The next section introduces the syntax, calculus, and semantics of a small
ALGOL-like language. The calculus includes the full p-rule, despite the pres-
ence of assignments. Section 2.3 addresses the basic consistency results for
the calculus and the derived semantics, Le., Church-Rosser, Standardization,
and Strong Normalization for the recursion-free subset. Section 3 contains
the Postponement Theorem, which shows how the evaluation of a program
can indeed be separated into a functional phase followed by an imperative
phase. Section 4 contains the proof that Postponement combined with Strong
Normalization for each sub-calculus entails Strong Normalization for the com-
plete calculus. The last section lists the shortcomings of our ALGOL dialect and
includes a brief discussion of related work.
2 An Idealized Dialect
Our version of IDEALIZED ALGOL (IA) is an idealized variant of ALGOL 60, simi-
lar to, but significantly simpler than, Reynolds's FORSYTHE [18, 16, 17]. Specif-
ically, the type system is that of the simply-typed A-calculus; the imperative
1 This goal is comparable to the attempts of Sendergard and Sestoft (19) to clarify the often
misused terminology of "referential transparency" and of Felleisen (5) to clarify the concept of
"expressive power of programming languages."
Matthias Felleisen and Stephen Weeks 103
• Constants • References
IT I> r n , : int IT I> M: int, IT[x/int ref] I> N : S
IT I> op: int-int-int IT I> new(x,M).N: s
IT I> M : int ref
• Variables ITI>!M:int
IT I> x: IT(x), x E dom(IT) IT I> M : int ref, IT I> N : int
IT I> M := N : comm
• Functions
• Simple Commands & Expressions
IT[X/T] I> M: T'
IT I> Ax.M : T-T' IT I> skip: comm
IT I> M: T-T IT I> L : int, IT I> M : 0, IT I> N : 0
IT I> (rec M) : T IT I> if L N N : a
ITI>M:T'-T, ITI>N:T' IT I> M : comm, IT I> N: 0
IT I> (M N) : T IT I> begin M N : 0
IA restricts references to contain only basic data values (int's), and the results
of new-blocks are of ground type.
The extension of the imperative sub-language to IA is accomplished by
relaxing the constraint on expression types. The full language admits expres-
sions of type T-T for arbitrary types T; e.g., int-int-comm is a permissible
type now. The restrictions on references and new-blocks remain.
For the formal specification of the type system, we begin with a definition
of the full language of types:
T
.... OIT-T
0 .. - comm I int I int ref
s .... comm lint
The type inference system is defined by a set of inference rules, whose basic
components are typings. A typing, IT I> M: T consists of a type aSSignment, IT,
an expression, M, and a type, T. A type aSSignment, IT, is a finite function
from variables to types. We use the notation IT[X/T] to indicate the type
assignment such that IT[X/T](X) = T and IT[X/T](Y) = IT(Y), if Y ;f= x. The
typing IT I> M : T asserts that expression M has type T when the free variables
in M are assigned types by IT. Figure 1 contains the inference rules for IA.
While the type inference system is a straightforward extension of that for
the simply typed A-calculus, two apparent restrictions deserve some com-
ments. Both if-expressions and begin-expressions are commands as well as
expressions, depending on which ground type they are assigned. In addition,
Matthias Felleisen and Stephen Weeks 105
and the B rule. Together, these only allow non-local assignments in the second
subexpression of a begin-expression to take place after the first subexpression
has been completely evaluated to a skip and removed by the B rule. Despite
the presence of assignments, function applications satisfy the full p-axiom.
Potential conflicts between assignments to and dereferences of the same ref-
erences are eliminated by the use of evaluation contexts due to the above-
mentioned unique-partition property for phrases. In contrast, assignments to
distinct references may be reduced in parallel. For example, during the evalu-
ation of
«+
(~! rIO'» (~! r5'» ,
the assignments in the two distinct procedure calls of ~! can be reduced inde-
pendently, which implies that a machine could execute them in parallel. 3
Based on the primitive notions of reduction, we define three compound
term relations. The first, ia, is intended to describe evaluation in the full
language IA. The second, Pfix, corresponds to the axioms describing the func-
tional part of the language. The third, p, corresponds to the axioms describing
the command component of the language:
ia 'f[ Pfixu p
pfix 'f[ P ufix
p 'f[ 6 upopuD u (T uTuFuB
To emphasize that the above is a calculus, we sometimes write r I- M = N
when M =r N where r is either ia, Pfix, or p.
Given the calculus, we can specify the semantics of IA. An IA program is
a closed integer expression. Its result is any numeral to which it is provably
equal.
Definition 1 A program M is a closed expression of type int; i.e.,
M E Programs if and only if 0 l> M : into
The evaluator is a partial function from programs to numerals:
eva': Programs ~ Numerals.
If M is a program, then eval(M) = r n, if and only if ia I- M = r n,. We write
evalp(M) = r n , when pI- M = r n ,.
3 Postponement
The restrictions on ALGOL'S type system are motivated by the desire to execute
programs in two phases. The first phase eliminates procedures and their uses
by reducing programs with Pfix sufficiently far. The result is a mixed program
whose functional components are irrelevant for the rest of the evaluation. The
second phase executes the imperative program according to the p-rules, which
can be implemented with a regular stack machine. The following theorem
makes this idea precise.
Theorem 7 Let M be a program, let n E 71.. Then,
i times
but
[(recM)]/ = ([M];(··· ([M]/n»).
\ , I
I times
To formalize the preCise relationship between the two operations, we intro-
duce an ordering, 1;;, on terms. It is the usual prefix-ordering for terms with
respect to n.
Matthias Felleisen and Stephen Weeks III
Proof: The proof of the lemma reduces to showing that both the function and
argument position of an application in a normal form expression cannot be a
A-expression See Appendix C, Lemma 30 for details. I
Since it is impossible to know a priori how far recursive procedures must
be unrolled, the "compilation" of an IA program into a W program must pro-
duce the set of p-normal-form's of all unrollings of the original program.
Hence, to understand the evaluation of an IA program as the execution of a W
program, we need to extend evalp (see Definition 1) to sets of W programs.
Definition 13 Let W be a set of W programs, i.e., a set of dosed phrases of
ground type. Then, evalp applied to this set is the pointwise extension of the
original evaluator:
evalp(W) = {evalp(w) I WE W} = {rn' I W -; rn', WE W}.
112 Chapter 5. Orthogonality of Assignments and Procedures in ALeoL
Proof: By induction on the length of the reduction M ~; r n'. For a single step
M ~ p Mr. since M I;; N, either M == MI if it is an n reduction or the "same"
redex exists in N. The first case is trivial. For the second case, it is simple to
check that reducing the corresponding redex in N produces a phrase NI such
that MI I;; Nr. hence the induction hypothesis may be applied. I
Finally, we state and prove a more general version of the postponement
theorem that characterizes compilation as ~-normalization to an infinite tree
and machine execution as an evaluation of the infinite tree in the imperative
fragment.
Theorem 16 For all 14 programs L,
{eval(L)} = evalp ( {M I M is ~-normal-form of[L]1 for i E N}).
Proof: Let W = {M I M is ~-normal-form of [L]/ for i EN}. By Lemma 14, W
is a chain, i.e., a totally ordered set. Thus, the theorem reduces to the claim:
eval(L) = rn,
N by .0 to produce a term N' such that N' !;;; Nand N' - ~ 'n' . Next, let i be the
number of fix steps in the reduction from L to N. Directly corresponding to the
sequence of fJ reductions in L - ~fix N is a sequence of fJ reductions that takes
[L]i to a term N", which looks like N with some subterms of the form (rec N1 )
replaced by (N1 ( . . . (Nl .0))). Hence N' !;;; N". Let M be the fJ-normaHorm
of N", which exists because fJ is strongly normalizing. By Lemma 33 and the
fact that N' is in normal form, we know that N' !;;; M. Lemma 15 now implies
that M -~ 'n'.
Combining the above, we have the following situation:
L ----fJ-fix---· N :/,n.
N'~ p
[L]i----,-----+-· N" - - - - - : - - - - - +
fJ fJ
where double lines indicate a partial order and vectors denotes reductions.
We have thus found a normal form M such that [L]i reduces to M, and M
imperatively reduces to the final answer. This completes the proof of the
auxiliary claim. I
A straightforward implementation of this compilation/execution schema
relies on lazy evaluation. The compiler suspends after producing sufficient
output and pipes its output into an abstract machine for imperative programs.
When the machine runs out of executable code, it resumes the compiler. The
abstract machine is a modification of the CEK machine (6). The control por-
tion of the machine is a member of W. The environment acts as a stack of
references. The continuation corresponds to an evaluation context. Figure 3
contains a formal specification of the machine and its instructions.
4 Strong Normalization
The Simply-typed A-calculus has the important property that terms without
the recursion construct always reduce to normal form. As a result, the equa-
tional theory is decidable, which is important for the implementation of a
broad class of compile time optimizations. Since the imperative sub-language
of IA is also clearly strongly normalizing, the natural question is whether the
combined language (without fix) satisfies the strong normalization theorem.
The key to the Strong-Normalization Theorem for lA is (a stronger version
of) the Postponement Theorem of Section 3 and a proof technique for com-
binations of two strongly-normalizing systems that satisfy the postponement
property. 5 Appendix D contains the proof of the meta-theorem on combining
SYan Daalen [20, p. 80] apparently proves the same result, but he ignores the additional condi·
tions we impose. Their absence breaks the meta-theorem.
114 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL
Before After
C E K C E K
«op tIl t2) E K t1 E ( (op .) t2) :: K
en' E ( (op .) t2) :: K t2 E «op en') .):: K
em' E ( (op 'n') .) :: K 'n op m' E K
t1 := t2 E K t1 E • := t2 :: K
x E • := t2 :: K t2 E x:= .:: K
en' E x:= • ::K skip E!(x, en') K
! t1 E K t1 E !.:: K
x E !.:: K E.x E K
if h t2 t3 E K t1 E if • t2 t3 :: K
'0' E if • t2 t3 :: K t2 E K
'n + l' E if • t2 t3 :: K t3 E K
version of lA with errors and non-local control constructs. Moreover, all the
results can be re-established for a call-by-value variant of lA, but, for the Post-
ponement Theorem to hold, the functional system becomes more complex and
must include "bubbling" reductions for imperative operations [4, ch. 5).
The Strong-Normalization and Postponement results cannot carry over to
languages with higher-typed or untyped references. As a consequence, these
results do not hold for the calculi of several programming languages that mix
functional and imperative features, i.e., RUSSELL [3), SCHEME (Lisp) [2, 7,8, 11,
12), and ML [22). A recently discovered alternative to mixing functional and
fully imperative languages is the addition of a weakened form of assignment
to functional languages [9, 15]. None of these languages or calculi is compa-
rable to lA with respect to (imperative) expressive power [5). We suspect that
most of these languages satisfy postponement and strong-normalization the-
orems, but it is not clear whether this is relevant given the weakness of their
assignment statements.
Acknowledgement
We are grateful to Robert Cartwright, Ian Mason, and John Gateley for com-
ments on an early draft.
References
[I] Henk P. Barendregt. The Lambda Calculus: Its Syntax and Semantics, revised
edition. Studies in Logic and the Foundations of Mathematics, Volume 103. North-
Holland, Amsterdam, 1984.
[2] Eric Crank and Matthias Felleisen. Parameter-passing and the lambda-calculus.
In Proceedings of the ACM Symposium on Principles of Programming Languages,
pages 233-245,1991.
[3] Alan Demers and James Donahue. Making variables abstract: an equational the-
ory for RUSSELL. In Proceedings of the ACM Symposium on Principles of Program-
ming Languages, pages 59-72, 1983.
[8] Matthias Felleisen and Robert Hieb. The revised report on the syntactic theo-
ries of sequential control and state. Theoretical Computer Science, 102:347-408,
1992. Preliminary version appeared as Technical Report 100, Rice University,
Computer Science Department, 1989.
[9] juan C. Guzman and Paul Hudak. Single-threaded polymorphic lambda-calculus.
In Proceedings of the Symposium on Logic in Computer Science, pages 333-345,
1990.
[10] Arthur Franklin Lent. The category of functors from state shapes to bottomless
CPO's is adequate for block structure. Master's thesis, Massachusetts Institute of
Technology, 1992.
[II] Ian A. Mason and Carolyn Talcott. Equivalence in functional programming lan-
guages with effects. Journal of Functional Programming, 1(3):287-327, july 1991.
Preliminary version in Proceedings of the International Conference on Automata,
Languages and Programming, pages 574-588. Lecture Notes in Computer Sci-
ence, 372. Springer Verlag,
[12] Ian A. Mason and Carolyn Talcott. Inferring the equivalence of functional pro-
grams that mutate data. Theoretical Computer Science, 105(2):167-215, 1992.
Preliminary version in Proceedings of the Symposium on Logic in Computer Sci-
ence, pages 284-293,1989.
[13] Peter Naur, ed. Revised report on the algorithmic language ALGOL 60. Communi-
cations of the ACM, 6(1):1-17,1963. See Chapter 1.
[14] G.D. Plotkin. Call-by-name, call-by-value, and the lambda-calculus. Theoretical
Computer Science, 1:125-159, 1975.
[15] Uday S. Reddy, V. Swarup, and E. Ireland. Assignments for applicative languages.
In Proceedings of the Conference on Functional Programming and Computer Ar-
chitecture, pages 192-214. Lecture Notes in Computer Science 523. Springer Ver-
lag, Berlin, 1991. See Chapter 9.
[16] john C. Reynolds. The essence of ALGOL. In de Bakker and van Vliet, editors,
Algorithmic Languages, pages 345-372. North-Holland, Amsterdam, 1981. See
Chapter 3.
[17] john C. Reynolds. Preliminary design of the programming language FORSYTHE.
Technical Report 159, Carnegie Mellon University, Computer Science Depart-
ment, 1988. See Chapter 8.
[18] john C. Reynolds. Replacing complexity with generality: The programming lan-
guage FORSYTHE. Carnegie Mellon University, Computer Science Department,
April 1991. See Chapter 8.
[19] H. S0ndergard and P. Sestoft. Referential transparency, definiteness and unfold-
ability. Acta Informatica, 27:505-517, 1990.
[20] D. van Daalen. The Language Theory of AUTOMAlli. PhD thesis, Eindhoven Uni-
versity, 1980.
[21] Philip Wadler. Comprehending monads. In Proceedings of the ACM Symposium
on Lisp and Functional Programming, pages 61-78,1990.
[22] Andrew Wright and Matthias Felleisen. A syntactic approach to type soundness.
Information and Computation, 115(1):38-94, 1995. Preliminary version appeared
as Technical Report 160, Rice University, Computer Science Department, 1991.
118 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL
Proof: We show the following, and apply diagram chasing: if L - Pfix M and L - p N,
then there exists K such that M Ip K and N =-pftx K. We proceed by cases on the
tlfix redex r in L == C[r] -Pfix C[e] == M.
1. r == «Ax.LI) L2), e == [L2/X]LI. By cases on the reduction L -p N, noting that
neither LI nor L2 is in an evaluation context, hence any p reduction in them
must be local:
(a) L -p C[«Ax.NI> L2)] == N because LI -p NI. Take K == C[[L2/X]Nd.
Clearly, N -pftxK. Also, M Ip K by Lemma 23.
(b) L -p C[«Ax.LI) N2)] == N because L2 -p N2. Take K == C[[N2/X]LI].
Clearly, N -pflxK. Also, M Ip KbyLemma 23.
(c) L -pNbecause C[z] -pN. TakeK == N.
(d) L -p C'[r] == Nbecause C[z] -p C'[z]. Take K == C'[e].
2. r == (rec LI>, e == (LI (rec Lt». By cases on the reduction L -p N, noting that
LI is not in an evaluation context, hence any p reduction in LI must be local.
(a) L -p C[(rec NI)] == N because LI -p NI. Take K == C[(NI (rec NI»].
(b) L - p N because C[z] - p N. Take K == N.
(c) L -p C'[r] == Nbecause C[z] -p C'[z]. Take K == C'[e]. I
Lemma 23 IfM IpM' andN Ip N' then [N/x]M Ip [N' /x]M'.
Proof: By induction on the proof of M I p M' and case analysis on the last step in the
proof:
1. M == M'. By induction on the number of free x's in M.
2. M == C[r] -p C[e] == M', where (r,e) E p. All we must show is that for each
imperative redex r, and its corresponding contractum e, [N/x]r Ip [N' /x]e.
By cases on the kind of redex r:
(a) r == (op 'n') 'm') -p 'n opm' == e. Then, [N /x]r == r I p e == [N' /x]e.
(b) r == new(y, 'n'). v -p v == e, where v == 'n' or skip. Then, [N /x]r ==
r I p e == [N' /x]e.
(c) r == new(y, 'n').E[!y] -p new(y, 'n').E['n'] == e. By Lemma 24 and
induction on the number of free x's in E:
[N/x]r new(y, 'n').([N/x]E)[!y]
Ip new(y, 'n'). ([N' /x]E)['n']
[N' /x]e
3. M", C[MdIpM' '" C'[Mil. because CIpC' andMI I p Mi. By the induction
hypotheSis,
[N/x]C[zJ I p [N' /x]C'[zJ
and
[N /XJMI 1 p [N' /xJMi .
Hence [N /xJM 1 p [N' /xJM'. I
Lemma 24 For all evaluation contexts E, [N /xJE is an evaluation context.
Proof: By induction on the structure of E. I
All other cases follow similarly, with either an application of the induction hypothesis
or inspection of the types of sub-expressions in the contractum based on the types of
sub-expressions in the redex. The only complicated case is that of the p-redex, which
requires an additional lemma showing that substitution respects the type system. I
Lemma 26 IfIT[x/TJI> M: T' and IT I> N: T, then IT I> [N /xJM: T'.
(b) Nl '" (op rn'). Apply the induction hypothesis to N2, which is of type into
Either N2 '" r m , or N2 E t2. The first case is impossible, since N would
not be in normal form. In the second case, since N2 is in an evaluation
context, we have that N E t2 C tl.
(c) Nl '" (op Ni), where Ni E t2. Since Ni is in an evaluation context, we
have that N E t2 C tl.
(d) Nl '" .\y. Ni. Impossible, since N would not be in normal form.
6. N '" new(x,Nd.N2. Apply the induction hypothesis to Nlo which is of type
into
(a) Nl '" r n ,. Apply the induction hypothesis to N2, which is of type ~. If
N2 '" r n, or skip, we have a contradiction with the factthat Nis in normal
form, since pop could be be applied. If N2 E t2, then either x captures the
free Xi in N2, contradicting the assumption that N is in normal form, or
N E t2, since N2 is in an evaluation context.
(b) Nl E t2. Since Nl is in an evaluation context, we have that N E t2 C tl.
7. N", !Nl. Apply the induction hypothesis to Nl, which is of type int ref.
(a) Nl '" XI. N E t2.
(b) Nl E t2. Since Nl is in an evaluation context, N E t2.
8. N", Nl := N2. Apply the induction hypothesis to Nl, which is of type int ref.
(a) Nl '" XI. Apply the induction hypothesis to N2, which is of type into
i. N2 '" r n,. N E t2.
ii. N2 E t2. Since N2 is in an evaluation context, N E t2.
(b) Nl E t2. Since Nl is in an evaluation context, N E t2.
9. N", skip. N E tl.
10. N", if Nl N2 N3. Apply the induction hypothesis to Nl, which is of type int
and is in an evaluation context. Either N E t2, or there is a contradiction with
the fact that N is in normal form.
11. N '" begin Nl N2. Apply the induction hypothesis to Nl, which is of type
eomm and is in an evaluation context. I
Lemma 28 For all evaluation contexts E if: [ ]. ifTT I> E[M] : T then T = o.
Proof: By simple case analysis of the structure of an evaluation context E. I
Appendix C Postponement
Lemma 29 If LIp M - fJfix N then 3M' such thatL - fJfix M' 1 p N.
Proof: LIp M '" C[P] -fJfixN '" C[P'], where (P,P') E 13fix. Cases onP:
1. P", «.\X.Ml) M2), P' '" [M2/X]Ml. Since a p reduction cannot create a 13-redex,
we must have that L '" C'[«.\x.Ld L2)], where C' IP C. In addition, since
neither Ll nor L2 is in an evaluation context, we must have that Li Ip Mi, for
i = 1,2. Take M' '" C'[[L2/X]Ll]. Clearly, L -fJfixM'. Also, M' IP N by
Lemma 23.
2. P", (reeMd,P' '" (Ml (reeMl». Since ap reduction cannot create afix-redex,
L '" C' [ (ree L 1) ], where C' 1 pC. Again, since L 1 is not in an evaluation context,
we must have that Ll1pMl. Take M' '" C'[(LI (ree Ll»]. Clearly, L -fJfixM'.
Also, M' IP N, by rule 3 in the definition of Ip· I
122 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL
Proof: Let N be the (3-normal-form of [M];. We must show four things: N contains
no rec-expressions, N contains no "-expressions, all applications in N are of the form
( (op t) t), and the only place where op may appear is in the function position of such
an application.
That there are no rec-expressions in [M]; is obvious. No reduction introduces a
new rec-expression, hence there are no rec-expressions in N.
We prove that N contains no "-expressions by contradiction. Assume not, let L
be the leftmost "-expression. We consider the immediate surrounding context of L. It
cannot be empty since a program must be of type into Since L is of - type it can only
occur in one of three positions: body of "-expression, function position of application,
or argument position of application. The first case contradicts that L is the leftmost
such sub-expression. The second and third cases contradict Lemma 31.
The final condition follows from the fact that the only possible remaining expres-
sions of - type are op and (op t), and the only place where they may occur is in the
function position of an application. I
Lemma 31 For any closed application expression M that does not contain any
rec-expressions, if M is in (3-normal-form, the function position of M is either op or
(op Ml). Hence, the argument position is of type into
Proof: By induction on the length of the reduction sequence M - PM', where M' is
the (3-normal-form of M:
1. M is in normal form. Then N looks like M with some n's replaced by other
expressions. Reduce all of these expressions in N to normal form, producing
a term N' which satisfies M !;;; N'. N' must be in normal form, since any redex
remaining would also be a redex in M.
2. M == C[«"x.Md M2)] -tl C[[M2/X]Ml] == Mil -p
M'. Then it must be the
case that N == C' [«"x. Nd N2)], where M; !;;; N; and C !;;; C'. We have that N - tl
C'[[N2/ X ]Nd == Nil. By Lemma 34, Mil!;;; Nil, and the induction hypothesis
may be applied. I
Lemma 34 If M !;;; M', N !;;; N', then [N /x]M !;;; [N' /x]M'.
Unfortunately this theorem is not true. There are several problems. First, the post-
ponement property does not imply that there is any relationship between the length
of the reduction L -:l 2
r N and the length of the reduction L M -:1 -:2
N. In order
to solve this problem, we introduce a stronger notion of postponement, which places
a lower bound on the length of the generated reduction.
Definition 36 rlr2 satisfies the strong-postponement property with respect to r2, if
e -~lr2 M" implies there exists M' such that e -::. M' -~2 M", and m + n ~ 1.
Problems can also arise when rl or r2 allows arbitrary length (not infinite) reduc-
tions for a given term. At first glance, arbitrarily long reductions starting from a single
term might appear to directly contradict strong-normalization; however, in general,
this is not the case. As an example, consider the following notion of reduction over
the language ~*, with alphabet ~ = {O, I, 2},
r = {(O, In) I n ~ I} u {(I, 2)}
Notice that r is strongly-normalizing, but for any term containing a 0, there are reduc-
tions of arbitrary length.
Intuitively, for more "standard" reduction systems such as /3, p, and ia, strong
normalization coincides with bounded reduction length. In comparing these systems
with r, we notice that they share a property that r does not have: only a finite number
of reductions are applicable to any given term. The notion r does not satisfy this
finite-branching property, because for a term t containing a 0, there are an infinite
number of terms t' such that t - r t' _ After introducing some terminology, we will
provide a simple criterion for strongly-normalizing systems that prohibit arbitrary-
length reductions starting with a given term.
Definition 37 Let r be a notion of reduction.
• The set of terms reachable in i steps from expression M using notion of reduc-
tion r is defined by:
R~(M) t![ {M' 1M _ r i M'}
• The set of terms reachable from M is defined by:
Rr(M) ==
df .
UR~(M)
i~O
• r satisfies the finite reachability property if for all expressions M, Rr (M) is finite.
• r satisfies the finite branching property if for all expressions M, RI (M) is finite_
• r satisfies the bounded reduction length property if for all expressions M, there
exists a j, such that Ui>jR~(M) = 0. For an expression M, we denote the
smallest such j by Ilr(M).
With some technical lemmas, we can show that in the presence of finite branching,
strong-normalization exactly corresponds to bounded reduction length. Then we can
prove the following theorem.
124 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL
Contents
1 Introduction 125
2 Syntax 127
3 Equivalences 132
4 Universal Specifications and Their Inference 135
5 Leftside Noninterference Decomposition 138
6 Rightside Noninterference Decomposition 143
7 Assignment and Simple Variable Declaration 145
8 let Commands and Function Procedures 147
9 Recursion 149
10 letrec Commands and Proper Procedures 151
11 Some Questionable Rules of Inference 154
References 156
1 Introduction1
Specification logic is a new formal system for program proving that is appli-
cable to programming languages, such as ALGOL, whose procedure mecha-
nism can be described by the copy rule. The starting point of its development
is the recognition that, in the presence of an ALGoL-like procedure mecha-
nism, specifications, such as the Hoare triple {P} S {Q} [Hoare, 1969), must be
regarded as predicates about environments (in the sense of Landin [Landin,
1965; Landin, 1966)). The logic provides additional kinds of specifications de-
scribing an interference relation (#) between variables and other entities, and
permits speCifications to be compounded using the operations of implication
(~), conjunction (&), and universal quantification (V). The result is a system
in which one can infer universal speCifications, i.e. specifications that hold in
all environments.
To see why the introduction of procedures makes a specification depend
upon an environment (which is a mapping from the free identifiers of the spec-
ification into appropriate meanings), it is enough to consider any nontrivial
Hoare triple involving a procedure call. For example, the truth of
{x = 3}x := double(x}{x = 6}
clearly depends upon the meaning into which double is mapped by the relevant
environment.
First appeared in D. Neel, editor, Tools and Notions for Program Construction, pages 121-161.
© Cambridge University Press 1982 (reprinted with the permission of Cambridge University
Press).
1 Editors' note: the author has added material to the first part of this section for publication in
this volume.
126 Chapter 6. IDEALIZED ALeoL and its Specification Logic
which specifies that, if the meaning of y does not interfere with the meaning
of x, then assigning 4 to y will preserve the assertion x = 3. Another example
is
{(Vn) double(n) = n + n} & gvinteger(x) => {x = 3}x:= double(x){x = 6} ,
which specifies that, if the meaning of double is a function procedure that
doubles any integer, and if the meaning of x is a "good variable" (a technical
condition described in Section 7), then executing x := double(x) in a state
where x = 3 will produce a state where x = 6.
It is important to distinguish specifications from assertions, which are the
first and last members of Hoare triples (Le. the phrases enclosed in braces).
While the truth of a specification depends only upon an environment, which
maps identifiers into their meanings, the truth of an assertion depends upon
both an environment and a storage state, which maps locations into values.
Although proof methods for procedures have been the subject of consid-
erable research [Hoare, 1971; Hoare and Wirth, 1973; Gries and Levin, 1980;
London et al., 1978], this work has focused on call by reference and call by
value, and had led to extremely complex inference rules that are incapable
of dealing with interference, call by name, statement parameters, or higher-
order procedures. In contrast, specification logic is both simpler and more
general. It assumes that the procedure mechanism is based upon a typed
lambda calculus that encompasses the logic for program proving as well as
the programming language.
In [Reynolds, 1981a], a version of specification logic is given for the sub-
language of ALGOL W [Wirth and Hoare, 19661 that represents a refinement of
ALGOL 60. In this report, we develop specification logic for a subset of the lan-
guage described in [Reynolds, 1981b1, which is an idealization of ALGOL that
makes the underlying (typed) lambda calculus explicit.
The advantage is that this language desugars well, Le. many of its con-
structs can be defined as abbreviations in terms of a simpler "base" language.
As a consequence, one can begin with simple inference rules for the base lan-
guage and then derive more complex rules for the abbreviationally defined
constructs. In particular, the complex aspects of inference that arise from
John C. Reynolds 127
2 Syntax
The programming language we consider is basically a sub language of that de-
scribed in [Reynolds, 1981bj, with the following restrictions:
(1) Recursive definition is permitted for commands, acceptors, and proper
procedures, but not for expressions or function procedures.
This limitation is an inevitable (though regrettable) consequence of using the
predicate calculus for assertions, along with the vital assumption that any ex-
pression of the programming language can occur as a term in such assertions.
Recursive definition of expressions or function procedures would impose a do-
main structure upon their meanings that would be incompatible with the free
use of quantifiers, because of their lack of continuity. In other words, we can-
not deal with nonterminating expressions and function procedures because
we cannot deal with nonterminating assertions.
Since nontermination is meaningless for certain phrase types, we must also
abandon the phrase type univ(ersal).
(2) Multiple declarations (including mutual recursion), case statements, ar-
rays, and labels are not treated.
These topics are simply omitted for brevity; none of them appear to raise
fundamental difficulties in specification logic. (Arrays, labels, and also for
statements are treated in [Reynolds, 1981aj.)
(3) Products and sums of data types are not treated.
Here our experience is too limited to say much; we have yet to explore the
treatment of these topics in specification logic.
The following productions describe the various kinds of type we will use:
(data type) ::= integer I real I Boolean
(ordinary phrase type) ::= (data type) exp
I (data type) ace
I (data type) (data type) var I comm
I (ordinary phrase type) - (ordinary phrase type)
(recursive phrase type) ::= (data type) ace I comm
I (ordinary phrase type) - (recursive phrase type)
128 Chapter 6. IDEAliZED ALeoL and its Specification Logic
A data type denotes a set of values appropriate for some kind of variable or
expression, while a phrase type denotes a set of meanings appropriate for
some kind of phrase. Among phrase types in general, bindable phrase types
are those that can be denoted by identifiers, ordinary phrase types are those
that can be denoted by identifiers that are bound by lambda expressions, and
recursive phrase types are those for which recursive definition is permitted.
(Notice that variables are classified separately by the data types they accept
and produce, i.e. T1 T2 var means T1-accepting T2-producing variable. The
rationale for this complication is given in [Reynolds, 1981b].)
Throughout this paper we will use the following metavariables to range
over various kinds of type:
T: data types
p: recursive phrase types
0: ordinary phrase types
00: bindable phrase types
y: phrase types.
For both data and phrase types, there is a partial ordering called the
subtype relation. For data types, it is the least partial ordering such that
integer ::s; real. For phrase types, it is the least partial ordering such that:
T ::s; T' implies T exp ::s; T' exp
T' ::s; T implies T ace ::s; T' ace
When y 5 y' we say that y is a subtype of y'; the intent is that there is an
implicit conversion from the meanings for y to the meanings for y', so that
any phrase of type y can be used in any context requiring a phrase of type y'.
A type assignment is a function from a finite set of identifiers to bindable
phrase types. Throughout this paper, we use the metavariable L to range over
identifiers and the metavariable rr to range over type assignments.
The subtype relation is extended to type assignments as follows:
rr 5 rr' if and only if dom(rr') !; dom(rr) and (V L E dom(rr'») rr(L) 5 rr' (L)
where dom(rr) denotes the domain of the function rr.
To describe the syntax of both our programming language and the lan-
guage of specifications, we will use phrase class names of the form (y, rr),
where y is a phrase type and rr is a type assignment. The intent is that (y, rr)
denotes the set of phrases P such that
(1) The identifiers occurring free in P belong to the domain of rr.
(2) When its free identifiers are given the bindable phrase types indicated by
rr, P has phrase type y.
We will only describe abstract syntax, ignoring considerations of paren-
thesization and precedence, and using production schemas containing the
metavariables T, p, B, W, y, L, and rr (with occasional sub- and superscripts)
whose ranges have been described above. First, we describe the "generic" part
of the language, i.e. constructs that are applicable to a variety of phrase types:
(y', rr) ::= (y, rr) when y 5 y'
(B, rr) ::= if (Boolean exp, rr) then (B, rr) else (B, rr)
In the fourth schema [rr I 1: Ih] denotes the type assignment such that
dom([rr I t: ell) = dom(rr) U {t}
[rr I 1: ell (t) = e1
[rr It: ed (t') = rr(t') when t' !- t.
Later we will use the following extensions of this notation:
[rr Itl:e1 I·· ·It,,:e,,] = [ ... [rr Itl:ed··· It,,:e,,]
[tl:e1 I·· ·It,,:e,,] = [[] Itl:e1 I·· ·It,,:en ],
where [ ] is the type assignment with empty domain.
The fifth and sixth schemas describe the least-fixed-point operator ree and
the least-element constant ~, both of which are limited to recursive phrase
types.
The seventh schema describes a generic conditional construction that is
applicable to all ordinary phrase types.
The last two schemas introduce Landin's notation [Landin, 1965; Landin,
1966] for nonrecursive and recursive declarations (limited to recursive phrase
types in the latter case). For simplicity, we avoid the multiple declarations
considered in [Reynolds, 1981b].
Notice that, quite aside from our neglect of parenthesization and prece-
dence, this syntax is ambiguous with regard to phrase types. For example,
if (Boolean exp, rr) then (real real var, rr) else (real real var, rr)
can be derived from (real exp, rr) by either
(real exp, rr)
if
n~
(Boolean exp, rr) then (real exp, rr) else (real exp, rr)
I I
(real real var, rr) (real real var, rr)
or
(real exp, rr)
I
(real real var, rr)
if
n~
(Boolean exp, rr) then (real real var, rr) else (real real var, rr)
John C. Reynolds 131
Similarly, (real exp - comm, rr) «integer exp, rr» can be derived from
(comm, rr) by either
(comm, rr)
/I~
(real exp - comm, rr) (real exp, rr)
I
(integer exp, rr)
or
(comm, rr)
/I~
(integer exp - comm, rr) ( (integer exp, rr)
I
(real exp - comm, rr)
We assume that this kind of ambiguity does not lead to ambiguous meaning.
Next we give more specific production schemas describing expressions and
commands. The schemas for expressions are obviously incomplete; the inclu-
sion of additional constants and operations would be straightforward though
uninteresting.
(integer exp, rr) ::= 0 I 1 I (integer exp, rr) + (integer exp, rr)
(real exp, rr) ::= 0.5 I (real exp, rr) + (real exp, rr)
(Boolean exp, rr) ::= true I false I (T exp, rr) = (T exp, rr)
I (Boolean exp, rr) and (Boolean exp, rr)
I ('ltL) (Booleanexp, [rr I L:integerexp»
I (3L) (Boolean exp, [rr I L:integer exp])
(comm, rr) ::= skip I (T acc, rr) := (T exp, rr)
I (comm, rr); (comm, rr)
I while (Boolean exp, rr) do (comm, rr)
I new T var L in (comm, [rr I L:T T var])
Notice that we permit Boolean expressions to contain quantifiers, at least
over the set of integers. This will permit us to simplify our presentation by
equating assertions with Boolean expressions. Of course, one cannot actually
implement the evaluation of quantifiers, but semantically, quantified expres-
sions have exactly the same kind of meaning as other logical expressions. It
should be noted, however, that this view is tenable only because we have pro-
hibited recursive functions and other constructs that would impose on the
meaning of expressions a domain structure for which quantification would
not be a continuous operation.
132 Chapter 6. IDEALIZED ALGOL and its Specification Logic
3 Equivalences
Basically, two phrases are equivalent if they possess the same meaning in ev-
ery environment or, more abstractly, if they possess the same semantics. How-
ever, this concept is complicated by the possibility that a phrase may belong
to more than one phrase class (;y, TT), and possess a distinct semantics for
each phrase class to which it belongs.
For this reason we must qualify equivalences with phrase class names.
When P and Q belong to (;y, TT) and have the same semantics for (;y, TT), we
write P =y,IT Q and say that P and Q are equivalent for (;y, TT).
Suppose TT' ~ TT and ;y ~ ;y'. If P =y,IT Q holds then P =y',IT' Q will also
hold. We will call this kind of deduction "type broadening."
John C. Reynolds 133
denotes the phrase in (y, rr) obtained by substituting each Qi for the free
occurrences of li in P, with appropriate renaming.
To express the validity of eta reduction, suppose P E (8 1 - 8 2 , rr) and
1 ([ dom(rr) (so that 1 does not occur free in P). Then
(3.2)
Further equivalences describe the operator ree, the constant ..l., and the
conditional construction. If P E (p - p, rr) then
ree P =P.TT P(ree P) . (3.3)
(if P then C else C'); C" =comm,1T if P then (C; C") else (C'; C") (3.15)
There are also obvious equivalences describing the conjunction and impli-
cation of specifications. Suppose S, S' ,S" E (spec, rr). Then
(S & S') & S" =spec.1T S & (S' & S") (3.19)
S => (S' => S") =spec.1T S & S' => S" (3.26)
S => S' & S" =spec,1T (S => S') & (S => S"). (3.27)
that describe the mathematics of data types. When the above equivalence
holds, we say that P E (Boolean exp, rr) is a mathematical fact for rr. For
example, k > 4 implies k ~ 5 is a mathematical fact for [k: integer exp].
John C. Reynolds 135
(4.4) Truth.
{true} :: rr .
(4.5) Adding Assumptions. If
then
and
then
SI ~ S3 :: rr.
Since they are so similar to ordinary logic, we will often omit the details of
applying these rules, as well as applying (4.1) for equivalences (3.17) to (3.27).
Two more rules deal with quantification:
(4.7) Quantifier lntroduction. Suppose SI E (spec, rr), S2 E (spec, [rr I L:W])
and L rt dom(rr). If
then
SI ~ (VL:W)S2 :: rr.
(4.8) Quantifier Removal. Suppose S E (spec, [rr I Ll: WI I··· IL,,: w,,]), and
PI E (WI, rr), ... ,P" E (w", rr), where Lb ... , L" are distinct identifiers.
Then
(V Ll: wd ... (V L,,: w,,)S ~ SI11 ..... ln-Pl •...•Pn :: rr .
From these rules we can derive the fact that universality is preserved by sub-
stitution:
(4.9) Free Substitution. Suppose S E (spec, [rr I Ll: WI I··· I L,,: W,,]),
PI E (WI. rr), ... , P" E (w", rr), Lb ... , L" are distinct, and
S :: [rr I Ll:Wl 1···1 L,,:W,,].
From (4.5),
{true} ~S :: [rr I Ll:Wl 1···1 L,,:Wn] ,
and by repeated application of (4.7),
{true} ~ (V Ln: wn)S :: [rr I Ll: WI I· .. I Ln-l: w,,-d
The derivation of this rule typifies the form of such derivations that will be
used in most of this report: it simply consists of a statement of the rule with
an appropriate argument inserted between premisses and conclusion.
Next, we derive a rule that permits any mathematical fact to be used as a
static assertion:
(4.10) Mathematical Fact Introduction. Suppose P is a mathematical fact for
7T. Then applying equivalence (3.28) to {true} :: 7T gives the conclusion
{P} :: 7T.
(4.20) Constancy.
c#p&{q}c{r} ~{qandp}c{randp}:: 7T.
The occurrence of # in the rule of constancy raises the question of how one
can infer noninterference specifications, which will be considered in the next
two sections.
138 Chapter 6. IDEAUZED ALGOL and its Specification Logic
P#E=>recP#E" IT.
(Notice that p :::; commlike holds for all recursive phrase types.)
(5.6) If 0 :::; commlike, B E (Boolean exp, IT), PI, P2 E (0, IT), and
E E (explike, IT), then
PI # E & P2 # E => if B then PI else P2 # E :: IT.
(5.9) If B E (Boolean exp, IT), C E (comm, IT), and E E (explike, IT) then
We now want to derive a general inference rule that subsumes all of the
previous rules and equivalences for leftside noninterference decomposition.
As a preliminary we must define, for any command-like phrase C, a subset
fcornmlike(C) of the identifiers occurring free in C. Let C E (commlike, IT).
Then fcornmlike(C) ~ {t I t E dom(rr) and rr(t) ::; eommlike} is the set of
identifiers such that:
(a) If C is an identifier t then
fcornmlike (C) = {L}
(b) If C is CdC2) then there must be one or more pairs eI. 2 of phrase e
e e
types such that 2 ::; eommlike, C I E WI - 2 , IT), and C2 E WI, rr).
(i) If there is any such el for which e l i eommlike then
fcornmlike ( C) = fcornmlike ( C Il
(ii) Otherwise,
fcornmlike(C) = fcornmlike(CIl U f cornmlike(C2).
(j) If C is CI := C2 then
The proof of this rule is considerably more complex than that of the derived
rules encountered previously. To begin with, we will assume that no identifier
bound in C belongs to dom(rr); this restriction can always be met by renam-
ing the bound identifiers of C, which changes neither the meaning of C nor
Fcommlike(C). With this restriction, the proof is by structural induction on C,
with cases that parallel the definition of Fcommlike:
(a) If C is an identifier L then Fcommlike (C) = {t}, so that the specification to
be proved is
L#E=L#E::rr,
which follows from the fact that any specification implies itself.
(b) If C is C1(CZ) then there must be one or more pairs 8 1 ,8z of phrase
types such that 8 z ::; commlike, C1 E (8 1 - 8 z ,rr), and Cz E (8 1 ,rr).
(i) If there is any such 8 1 for which 8 1 f;. commlike then Fcommlike(C1)
= Fcommlike(C), so that the induction hypothesis gives
Thus
and the result follows from the defining equivalence (3.8) for let.
(h) If C is letrec L: p be C1 in C2 then Fcommllke (C) = ( Fcommllke (CIl U
Fcommllke(C2») - {L} = Fcommllke(.\L:p.CIl U Fcommllke(M:p.C2) =
Fcommllke(M:p.C2)(rec '\L:p.CIl). Then the argument for (b), (c), and
(d) shows that
LI # E & ... & Ln #E ~ (M:p. C2)(rec M:p.CIl # E :: 7T,
and the result follows from the defining equivalence (3.9) for letrec.
(i) If C is skip, then the result follows from rule (5.7) by an argument similar
to (e).
(j) If C is CI := C2 then Fcomrnlike(CI) = Fcommllke(C), so that the induction
hypothesis gives
LI # E & ... & Ln # E ~ CI # E :: 7T •
(6.5) If C E (commlike, IT) and EloE2 E (integer exp, IT) (or (real exp, IT) or
(Boolean exp, IT}) then
C # El & C # E2 => C # El + E2 :: IT,
C # El & C # E2 => C # El = E2 :: IT,
C # El & C # E2 => C # El and E2 :: IT.
(6.6) If C E (commlike, IT), E E (Boolean exp, [IT I t:integer expJ), and
t rt dom(IT), then
(V"t:integer exp)(C # t => C # E) => C # ('Itt) E :: IT.
fexplike(E) = {}.
(g) If E is El + E2, El = E2, or E1 and E2, then
fexplike(E) = fexplike(Erl U fexplike(E2).
John C. Reynolds 145
since fexplike(i\c T exp.P) = {Ll,"" Ln}. Then equivalence (7.1) gives the
conclusion
gvT(V)&V#Ll&",&V#L n = {Pll-E}V:=E{Pll-V}:: rr.
In the special case where V = L, this rule reduces to the simple assignment
rule (R24) in [Reynolds, 19S1a), which says that Hoare-like reasoning about
an assignment to L requires L to be a good variable that does not interfere
with any other identifier with a free expression-like occurrence in P. In the
simplest case, where L is declared to be a simple variable, these assumptions
will be assured by its declaration. Simple variable declarations are described
by the following family of axioms:
146 Chapter 6. IDEAlizED ALGOL and its Specification Logic
From this axiom we can derive a less abstract rule akin to (R25) in [Reynolds,
1981a]:
(7.4) Simple Variable Declaration Rule. Suppose S E (spec, rr), E1 ,. .. ,
Em E (explike, rr), C1. ... , Cn E (commlike, rr), P, Q E (Boolean exp, rr),
BE (comm, [rr I 1:T T var]), and L rt dom(rr) are such that
S & gvT(L) & L # E1 & ... & L # Em & C1 # L & ... & Cn # L
=>{P}B{Q}:: [rrIL:TTvar].
p,q-P,Q
h - At:T T var.B
to obtain
(V 1: T T var) (gvT(t) & L # E1 & ... & L # Em & C1 # L & ... & Cn # L
=> {P} (AL: T T var.B)(t) {Q})
=>{P} new T var L in (At:T T var.B)(L) {Q} :: rr.
Then, after beta-reducing the two redexes and using modus ponens, we
obtain the conclusion
S => {P} new T var Lin B {Q} :: rr.
At this point, the reader may wonder if it is possible for a variable not to
be good. An example is provided by the conditional variable
ifm=lthenmelsen,
where m and n are good integer variables. For a state where m = 1 and n = 2,
the assignment
(if m = 1 then m else n) := 3
John C. Reynolds 147
and that
Let M be .\II: eI •... ALn: en. Bproe E «(), rr'). When M is substituted for L
in Sproe, there is no renaming since LI. ... ,Ln and Le do not occur free in
M, and Bproe and L~, ... , L;;' remain unchanged since they do not contain
free occurrences of L. Moreover, M(Ld ... Un) beta-reduces to Bproe.
Thus Sproe' {-M E (spec, rr') is
(V LI: ed ... (V Ln: en) {Bproe = Bprocl & (V Le: commlike)
(Le # L~ & . . . & Le # L;;' = Le # ALI: (}I •... .\In: en. Bproe) ,
which is universal for rr' since Bproe = Bproe is a mathematical fact and
(b)
Then from (a) and (b) the rule (8.1) for let commands can be used to
infer the conclusion
S = {P} let Lbe ALI: e l •... ALn: en. Bproe in B {Q} :: rr.
John C. Reynolds 149
9 Recursion
To encompass recursion, we must give basic rules for the operator ree and the
constant 1.. The behavior of 1. is described by
(9.1) Nontermination of 1.. If P, Q E (Boolean exp, rr) then
{P} 1. comm {Q} :: rr,
along with equivalences (3.4) and (3.5), and inference rule (5.5).
Before giving a rule for the recursion operator ree, we must define, for
any specification S, two subsets of the identifiers occurring free in S. If
S E (spec, rr), we define F-(S) and F+(S) ~ dom(rr) by
F-({P} C {Q}) = {} F+({P} C {Q}) = Fcommlike(C)
F-( {P}) = {} F+( {P}) = {}
F_(C#E) = {} F+(C#E) = Fcommlike(C)
F-(S & S') = F-(S) u F-(S') F+(S &S') = F+(S) u F+(S')
F-(S = S') = F+(S) u F-(S') F+(S = S') = F-(S) u F+(S')
F-W'/t:€J)S) = F-(S) - {L} F+((YdJ)S) = (F+(S) u F-(S») - {L}
These sets reflect the semantic concept of continuity:
(1) If L rt F- (S) then the meaning of S, ordered by true r;; false, is continuous
in the meaning of L.
51 ~ 52 :: IT.
From (3.25)
(52 ~ 53) ~ (52 ~ 53) .. IT,
so by (3.26) and (3.20)
52 ~«52 ~53) ~53)" IT,
It will also simplify the argument to assume, for the duration of this example,
that the values of integer expressions range over the natural numbers (exclud-
ing negative numbers). Thus x + 1 = 0 implies false is a mathematical fact for
[x: integer exp].
Let
F = AP: integer exp ~ comm. AX: integer expo
if x = 0 then skip else p(x - 1)
and
IT = [p:integer exp ~ comm I x: integer exp] .
Then
{false}skip{false} :: IT (4.17)
{x + 1 = O}skip{false} :: IT (4.13)
SI P -J.lntegerexp-comm :: [ ) (4.3)
Now consider the recursion rule, taking p to be integer exp - comm, rr to be
the empty type assignment [ ), L to be p, and Sand F as above. In the absence
of the restriction p rt f-(S), one could infer
Sip-reeF:: [)
But this is false, since in fact rec F always terminates.
From the recursion rule (9.2), replacing S by Sproc and F by ?I.L: p.M, we
get
(Sprocl,-.Lp) & (V(:p) (Sproc => (Sprocl,-(.\t:P.M)(L))
=> (Sproc 1,-ree .\t:P.M) :: rr ,
Let M be ALI: 0 1 •••• ALn: On. Bproc E (p, [rr' I t: p]). When M is substi-
tuted for l in Sproc, there is no renaming since Ll, ... ,In, L~, ... , lk' and Le
do not occur free in M, and Spa, Pproc , Qproc, L~, ... , l~ remain unchanged
since they do not contain free occurrences of L. Moreover, MUd· .. (In)
beta-reduces to Bproc. Thus Sproc II-M E (spec, [rr' I l: p]) is
('tfll:Od··· ('1ln:On)('1l~:Oi)··· ('ILk: Ok)
(Spa = {Pproc } Bproc {Qprocl)
& ('1 Le: explike)
(L~' # le & ... & l~ # le = All: 0 1 •..• Aln: On. Bproc # le) .
which are consequences of (3.4), (9.1), and (5.5). Finally from (f), (e), and
(b), broadening the type assignments to replace rr' by rr, taking S to be
SI & S2, and noting that ( rt f- (Sprod, rule (10.1) for letrec commands
gives the conclusion
Clearly there are environments in which both k # k and {true} skip {false} are
false, so this specification is not universal in a conventional semantics.
On the other hand, the need for a rule of this sort is illustrated by an
attempt to show that
let iterate be '\p: integer exp - comm.
new integer var k in
(k:= 0; while k < 100 do (k:= k + 1; p(k»)
in (s:= 0; iterate(,\k:integer exp.s:= s + (k»)
will set s to I}2? (0.
It is a straightforward exercise to show
(Y k: integer exp)
(p(k) # k ~ {w(k - 1) and 1:;; k :;; 100} p(k) {w(k)})
~{w(O)} new integer var kin· .. {w(lOO)}
:: [p: integer exp - comm I w: integer exp - Boolean exp]
which leads to an application of rule (10.2) with Sproc =
(Yp:integer exp - comm)(Yw:integer exp - Boolean exp)
( ( Y k: integer exp)
(p(k) # k ~ {w(k - 1) and 1 :;; k :;; 100} p(k) {w(k)})
~{w(O)} iterate(p) {w(lOO)})
& (Y e: explike) iterate # e .
Then by removing quantifiers, with the substitution
p - ,\k:integer exp.s:= s + (k)
where IT is a type assignment that maps s into real real var, ( into
integer exp - real exp, and iterate into (integer exp - comm) - comm.
By the rule for assignment and well-known mathematical facts about sum-
mation, we can obtain
156 Chapter 6. IDEAliZED ALGOL and its Specification Logic
References
GRIES, D. AND LEVIN, G. 1980. Assignment and procedure call proof rules. ACM Trans.
on Programming Languages and Systems, 2(4):564-579.
HOARE, C. A. R. 1969. An axiomatic basis for computer programming. Comm. ACM,
12(10):576-580 and 583.
HOARE, C. A. R. 1971. Procedures and parameters: an axiomatic approach. In Sym-
posium on Semantics of Algorithmic Languages, E. Engeler, editor, volume 188 of
Lecture Notes in Mathematics. Springer-Verlag, Berlin, pages 102-116.
HOARE, C. A. R. AND WIRTH, N. 1973. An axiomatic definition of the programming
language PASCAL. Acta Informatica, 2(4):335-355.
LANDIN, P. J. 1965. A correspondence between ALGOL 60 and Church's lambda-
notation. Comm. ACM, 8(2,3):89-101 and 158-165.
LANDIN, P. J. 1966. A A-calculus approach. In Advances in Programming and Non-
Numerical Computation, 1. Fox, editor, Oxford. Pergamon Press, pages 97-141.
LONDON, R. 1., GUTTAG, J. V., HORNING, J. J., LAMpSON, B. W., MITCHELL, J. G., AND
POPEK, G. J. 1978. Proof rules for the programming language EUCLID. Acta Infor-
matica, 10(1):1-26.
REYNOLDS, J. C. 1981 a. The Craft of Programming. Prentice-Hail International, London.
REYNOLDS, J. C. 1981b. The essence of ALGOL. In Algorithmic Languages, Proceedings
of the International Symposium on Algorithmic Languages, J. W. de Bakker and
J. c. van Vliet, editors. North-Holland, Amsterdam, pages 345-372. See Chapter 3.
WIRTH, N. AND HOARE, C. A. R. 1966. A contribution to the development of ALGOL.
Comm. ACM, 9(6):413-432.
Chapter 7
Towards Fully Abstract Semantics for Local
Variables: Preliminary Report
Albert R. Meyer and Kurt Sieber
The Store Model of Halpern-Meyer-Trakhtenbrot is shown-after suitable
repair-to be a fully abstract model for a limited fragment of ALGOL in
which procedures do not take procedure parameters. A simple counter-
example involving a parameter of program type shows that the model is
not fully abstract in general. Previous proof systems for reasoning about
procedures are typically sound for the HMT store model, so it follows
that theorems about the counter-example are independent of such proof
systems. Based on a generalization of standard cpo·based models to
structures called locally complete partial orders (lcpo's), improved models
and stronger proof rules are developed to handle such examples.
Contents
1 Introduction 157
2 The Usual CPO Based Models 159
3 Halpern-Meyer-Trakhtenbrot Store Models 160
4 The Invariant-Preserving Model 163
5 Conclusion 164
References 165
Appendix: Locally Complete CPO Models 166
1 Introduction
Some unexpected problems in the semantics and logic of block-structured
local variables have been identified by Halpern, Meyer, and Trakhtenbrot
[10, 281. The usual cpo-based models for stores and programs do not sat-
isfactorily model the stack discipline of blocks in ALGOL-like languages. The
simplest example involves a trivial block which calls a parameterless proce-
dure identifier P.
Example 1 The block below is replaceable simply by the call P.
begin
new x;
P; % P is declared elsewhere
end
It is easy to argue informally that the block in Example 1 acts the same as P.
Namely, since ALGOL-like languages mandate static scope for local variables,
it follows that P has no access to the local variable x, so allocating x and then
deallocating it if and when the call to P returns, can have no influence on the
call to P.
First appeared in Conference Record of the Fifteenth Annual ACM Symposium on Prindples of
Programming Languages, pages 191-203. San Diego. California, 1988. ACM, New York. © 1988
Association for Computing Machinery, reprinted by permission.
158 Chapter 7. Towards Fully Abstract Semantics for Local Variables
begin
new x;
x:= 0;
P; % P is declared elsewhere
if contents(x) = 0 then diverge fi
end
To verify Example 2, we note that the definition of ALGoL-like languages in
[10, 28] implies that the call of P has side-effects on the store only, viz., no
input/output effects, and no goto's or other transfers of control. This is es-
sentially the same language Reynolds has called the "essence" of ALGOL [22]
without goto's or jumps. In particular, the only way the call of P in the block
can fail to return is by diverging. If the call does return, then since the con-
tents of x equals zero immediately before the call, static scope again implies
that the contents will still be zero when the call returns, so the conditional
test will succeed causing divergence in any case.
Note that these arguments implicitly presuppose that P is a call to a de-
clared procedure. That is, the arguments really show that if C[ . ] is any closed
ALGoL-like program context such that [ . ] is a "hole" within the scope of a dec-
laration of P, then C[Block 1] has exactly the same effect on the store as C[P],
and likewise C[Block 2] has exactly the same effect as C[diverge]. We say
that the block in Example 1 and P are observationally congruent wrt ALGOL-
like contexts; likewise the block in Example 2 is observationally congruent to
diverge.
On the other hand, if P was a call of an independently compiled "library"
program-even one originally written in ALGOL-which did not share the mem-
ory management mechanisms of the ALGOL compiler used on Blocks 1 and 2,
then the call might detect changes on the stack of variables like x, and might
even alter the contents of stack variables, making the behaviour of the blocks
unpredictable. Thus, we have not shown that the Block 1 is semantically equiV-
alent to P, even when the values of P range only over ALGoL-like procedures.
Indeed, the congruences of Examples 1 and 2 are not semantical equiva-
lences in the standard denotational semantics for ALGoL-like languages using
"marked" stores [12, 9]. In such semantics, Block 1 and P are only equivalent
on stores in which the locations "accessible" to P are correctly marked as in
use, but certainly not on incorrectly marked stores.
The problem which motivates this paper is to provide mathematical justi-
fication for the informal but convincing proofs of observational congruences
like the two above. Following [10], we approach the problem by trying to con-
struct semantical models of ALGoL-like languages in which semantical equiV-
alence is a good guide to observational congruence. An ideal situation occurs
when the mathematical semantics is fully abstract, i.e., semantic equivalence
Albert R. Meyer and Kurt Sieber 159
for two distinguished elements .L '" 0 '" 1 '" .L and all v E Val.1.
Theorem 2 The continuous store model for the language FROG, without the
new local-variable declaration and with an additional II v combinator is com-
putationally adequate and fully abstract.
Note that Example 2 makes it clear that the marked store model is still not
fully abstract even with the addition of II v. Thus, Theorems 1 and 2 confirm
that local variables are a source of difficulty in this approach. We remark that
although Theorem 1 has nearly the status of a folk theorem in domain the-
ory, we know of no published proof; our own proof follows the computability
method applied to the functional language peF in [20]. Our proof of Theo-
rem 2 again applies the results of [20] about definability of "finite" elements
together with some folk theorems connecting Clarke's restricted ALGoL-like
language L4 [2, 3, 7] and higher-order recursive function schemes [4, 8].
The argument for equivalence of the blocks goes briefly as follows. Let
q E (Loc.L x Loc.L) ...E.. Prog be the meaning of the procedure identifier Q.
The definition of local variable allocation in the HMf model implies that
x and y can be bound in the body of either block to distinct locations
lx, ly if:. support(q). By definition of support, q cannot recognize locations
not in its support, treating them in a uniform way (cf. the Appendix), so
the store transformations q(ix,ly) and q(ly,lx) agree on all stores s with
s(lx) = s(ly) whose restrictions to support(q) u {lx,ly} are the same. Since
contents(lx) = contents(ly) = 0 when the block bodies begin execution-and
stack discipline specifies that the contents are restored to their original values
on de allocation-it follows that both blocks define the same store transforma-
tion as q(lx, ly) restricted to support(q).
The HMf store model was claimed to be computationally adequate, but
not necessarily fully abstract. Its successful handling of Examples 1-3 is a
consequence of the following general result about the "first-order" ALGOL-like
sublanguage without goto's and jumps, in which procedure parameters are
restricted to be of type Val and Loc (essentially the language considered in [6]).
Theorem 3
The HMT store model is computationally adequate for all ALGOL-like lan-
guage features other than goto's and jumps. It is fully abstract wrt to the
"first-order" sublanguage with an additional II v combinator.
We remark here that we have been generous in our references to the HMf
store model described in [10], since in fact the construction sketched there
contains a serious technical error-noted independently by the second author
and A. Stoughton. In the Appendix, we repair this error, and moreover develop
a methodology for constructing improved models based on the notion of lo-
cally complete partial orders (lcpo's). Thus, Theorem 3 refers to the corrected
HMT store model.
We now consider some second-order examples.
begin
newx;newy;
*
procedure Twice; begin y := 2 contents(y) end;
x:= 0; y:= 0;
Q (Twice); % Q is declared elsewhere
if contents(x) = 0 then diverge fi
end
162 Chapter 7. Towards Fully Abstract Semantics for Local Variables
Two additional reasoning principles about support, which hold in the HMf
model (cf the Appendix), arise in handling this example. First, in reasoning
about program text in the scope of a local-variable declaration new x, we may
assume that the value of x is any convenient location not in the support of
(the values 00 each of the free identifiers in the scope of the declaration. Sec-
ond, we always have support(Q(P») £; support(P) u support(Q). Now clearly,
support(Twice) = {y}. Since x is free in the scope of the new y declaration,
the first principle applied to y implies that x and y denote different locations,
so x ri support(Twice). Since Q is free, x ri support(Q). By the second princi-
ple, we may now assume x ri support ( Q (TWice»). Hence, we may reason about
the call Q (TWice) in Example 4 exactly as we did for the call P in the divergent
block of Example 2.
Unfortunately the HMf model does not handle all examples with second-
order procedures, as the following elegant counter-example pointed out to us
by A. Stoughton makes clear:
Example 5 The block below always diverges.
begin
new x;
procedure Add2 ; % Add2 is the ability to add 2 to x
begin x := contents(x) + 2 end
x:= 0;
Q (Add2); % Q is declared elsewhere
if contents(x) mod 2 = 0 then diverge fi
end
The block in Example 5 does not diverge identically in HMf because Q might
denote an element q E Prog -.£. Prog such that q(p) is a program which sets to
one all locations writable by p. Such a q exists in the HMf model because it is
continuous (in the HMf sense, cf the Appendix) and has empty support. How-
ever, Block 5 is observationally equivalent to diverge: Q has no independent
access to the local variable x, so the only ability the program Q (Add2) has
relative to x is the ability to increment its contents by two. Since contents(x) is
an even integer, namely zero, before execution of this program, it will still be
even if and when the program terminates, so the conditional test will succeed
and cause divergence. Thus we have
Lemma 1 Block 5 is observationally congruent to diverge, but not equal to di-
verge in the HMT store model.
Hence:
Theorem 4 The HMT model is not fully abstract even for FROG programs whose
procedure calls take parameters only of program type.
This failure of full abstraction for the HMT store model is particularly in-
teresting precisely because the model is a good one. In particular, the various
rules and systems proposed in the literature for reasoning about procedures
in ALGoL-like languages are all sound for the HMf model (insofar as they are
sound at all, cf [14]). It follows that the divergence of Block 5 (and perhaps
Albert R. Meyer and Kurt Sieber 163
Block 4 too) is independent of the theorems provable from other proof sys-
tems in the literature including [28,10,25,17,16,11,241. Reynolds'specifi-
cation logic [21, 23] is shown in [26, 27] to be intuitionistically sound using a
functor-category semantics; it is not yet clear how the semantics and logic of
[27] handle these examples.
The idea is that since P has no independent access to x, and since its actual pa-
rameters in Example 7 do not enable P to read contents(x), the procedure calls
P(Add_I) and P(Add2) differ only in their effect on x. Since x is deallocated
on block exit, the two blocks are observationally equivalent. Nevertheless,
Thus, still stronger proof principles than preservation of invariants are needed
to formalize this last observational congruence argument. The reader may
care to invent one.
5 Conclusion
We have seen a series of simple examples illustrating how to reason about
block structured variables. Most of these principles have never been stated in
the literature, let alone been proved sound. To establish soundness we con-
structed a series of models for ALGoL-like languages. The formal machinery
for constructing the models based on lcpo's is sketched in the Appendix. It
merits detailed discussion which we have had to forego here. The best of
our models is still not fully abstract for PASCAL-like sublanguages, but we are
working on a proof that our methods will extend to this case. We see no reason
why our approach should not extend to the full range of ALGOL-like features,
but it would be premature to conjecture that full abstraction can be achieved
this way.
Oles and Reynolds [22, 18, 19] have also developed models of ALGOL-like
languages using a categorical framework. They do not consider computa-
tional adequacy or full abstraction as explicit issues. Tennent has informed us
in private communication that his version [27] of the Reynolds-Oles functor-
category semantics correctly handles Examples 1 and 2. The comparison be-
tween their approach and ours has yet to be worked out. Actually our ap-
proach can also be seen from a category theoretic viewpoint-an lcpo is a
functor from a partially ordered index set to the category of cpo's, and the
locally continuous functions are similar to, but not exactly, natural transfor-
mations between such functors-but thus far we have not found this viewpoint
advantageous.
Albert R. Meyer and Kurt Sieber 165
References
[1] G. Berry, P.-L. Curien, and J.-j. Levy. Full abstraction for sequential languages:
the state of the art. In M. Nivat and J. C. Reynolds, editors, Algebraic Methods
in Semantics, pages 89-132. Cambridge University Press, Cambridge, England,
1985.
[2] E. M. Clarke, Jr. Programming language constructs for which it is impossible to
obtain good Hoare-like axiom systems. J. ACM, 26(1):129-147, 1979.
[3] E. M. Clarke, Jr., S. M. German, and j. Halpern. On effective axiomatizations of
Hoare logics. ]. ACM, 30:612-636, 1983.
[4] W. Damm. The 10- and OI-hierarchies. Theoretical Computer Science, 20:95-207,
1982.
[5] W. Damm and E. Fehr. A schematological approach to the analysis of the proce-
dure concept in ALGOL-like languages. In Cinquieme Colloque sur les Arbres en
Algebre et en Programmation, pages 130-134, lille, France, 1980.
[8] A. Goerdt. Hoare logic for lambda terms as basis of Hoare logic for imperative
languages. In Proceedings, Symposium on Logic in Computer Science, pages 293-
299, Ithaca, New York, 1987. IEEE Computer Society Press.
[17] E. Olderog. Hoare's logic for programs with procedures: what has been achieved?
In E. M. Clarke, Jr. and D. Kozen, editors, Logics of Programs 1983, pages 383-
395, volume 164 of Lecture Notes in Computer Science, Pittsburgh, PA, 1983.
Springer-Verlag, Berlin, 1984.
[18] F. J. Oles. A Category-Theoretic Approach to the Semantics of Programming Lan-
guages. Ph.D. thesis, Syracuse University, Syracuse, N.Y., 1982. See Chapter 11.
[19] F.1- Oles. Type algebras, functor categories and block structure. In M. Nivat and
J. C. Reynolds, editors, Algebraic Methods in Semantics, pages 543-573. Cam-
bridge University Press, Cambridge, England, 1985. See Chapter 11.
[20] G. D. Plotkin. LCF considered as a programming language. Theoretical Computer
Science, 5:223-255, 1977.
[21] J. C. Reynolds. The Craft of Programming. Prentice-Hall International, London,
1981.
[22] J. C. Reynolds. The essence of ALGOL. In 1- W. de Bakker and 1- C. van Vliet, edi-
tors, Algorithmic Languages, Proceedings of the International Symposium on Al-
gorithmic Languages, pages 345-372, Amsterdam, October 1981. North·Holland,
Amsterdam. See Chapter 3.
[23] 1- c. Reynolds. IDEALIZED ALGOL and its specification logic. In D. Neel, editor,
Tools and Notions for Program Construction, pages 121-161, Nice, France, De-
cember 1981. Cambridge University Press, Cambridge, 1982. See Chapter 6.
[24] R. 1. Schwartz. An axiomatic treatment of ALGOL 68 routines. In H. Maurer,
editor, Proceedings 6th International Colloquium on Automata, Languages and
Programming, volume 71 of Lecture Notes in Computer SCience, pages 530-545.
Springer-Verlag, Berlin, 1979.
[25] K. Sieber. A partial correctness logic for procedures (in an ALGoL-like language).
In R. Parikh, editor, Logics of Programs 1985, pages 320-342, volume 193 of
Lecture Notes in Computer SCience, Brooklyn, N.Y., 1985. Springer-Verlag, Berlin.
[26] R. D. Tennent. Semantical analysis of specification logic (preliminary report). In
R. Parikh, editor, Logics of Programs 1985, pages 373-386, volume 193 of Lecture
Notes in Computer Science, Brooklyn, N.Y., 1985. Springer-Verlag, Berlin.
[27] R. D. Tennent. Semantical analysis of specification logic. Information and Com-
putation, 85(2):135-162, 1990. See Chapter 13.
[28] B. A. Trakhtenbrot, 1- Y. Halpern, and A. R. Meyer. From denotational to op-
erational and axiomatic semantics for ALGOL-like languages: an overview. In
E. M. Clarke, Jr. and D. Kozen, editors, LogiCS of Programs 1983, pages 474-500,
volume 164 of Lecture Notes in Computer Science, Pittsburgh, PA, 1983. Springer-
Verlag, Berlin, 1984.
Definition 2 Let D and E be I-lcpo's. For f: D..!.. E, let fi denote the restriction of f to
Di, and define
Then D .is E = UiEI(D .is E)i is called the set of locally continuous functions from D
toE.
(D!!!. Eli = {f E (D ..!s Eli I (Rf - Rf)(f, ... ,f) whenever i E down(k)}.
Then D !!!. E = UiEI(D !!!. E)i is called the set of relation-preserving functions from
D toE.
Definition 7 Let I = 'Pfin(Loc). Let K = Perm(Loc) , and for every /1 E K, let nil = 2
and down(/1) = {L I L s;; Fix(/1)}. Then the HMT Store Model is defined by ground-type
lcpo's
Dial Vall.,
DfoC L U {..LLocl,
DLLocexp {
f:Stores~Locl.
r I (V s·f(s) E L U {..LLod) /\
VSloS2.S1 =Ls2~f(sll =f(S2)
}
,
By Theorem 6 this defines a model of the simply typed ,\-calculus. It turns out that for
every element d E DT there is a smallest set L such that d E Dr. This set L is called the
support of d. Theorem 6 then implies that all pure ,\-terms have meanings with empty
support and Lemma 6 implies that support(ddd2») s;; suPPOrt(dl) U support(d2) al-
ways holds.
The definition of the model captures several aspects of support mentioned earlier.
In particular for every element d of type T in the model, R~ (d, d) holds by Lemma 6
whenever support(d) s;; Fix(/1). This expresses the "uniformity" of d on locations
outside its support, which was important in Example 3 and is crucial for defining the
semantics of the New combinator.
Finally, the "intended" interpretations, namely, interpretations which guarantee
computational adequacy, must be proved to exist in the model for all the ALGOL con-
stants. An example is the combinator Assign of type Loc ~ Valexp ~ Frog, for inter-
preting assignment, whose intended meaning is the function
Proof sketch for full·abstraction part of Theorem 3: Adapt the ideas of [20] to 10'
cally continuous models. Every element of a local cpo DI (where T is first·order) is the
lub of a directed set of finite elements in DI ' and these finite elements are definable by
closed ALGOL-like terms. Local continuity then implies that two semantically different
phrases can be distinguished by choosing definable objects for their free procedures.
This means that they can be distinguished by a program context. I
A further application of Theorem 6 leads to the Invariant-Preserving Model. For
every L <; Loe, let Pred(L) be the set of predicates on stores which only depend on L,
namely
Pred(L) = {7T: Stores'!' {true, false} I 'it Sl, S2 E Stores.s1 =L S2 ~ (7T(Stl '" 7T(S2»)}.
Definition 8 Let 1= P/'in(Loe) and K = Perm(Loe) u {(L,m I LEI and n <; Pred(L)}.
For /1 E K, let nil = 2 and down(/1) = {L I L <; Fix(/1)} as in the HMT model (Def-
inition 7). For (L,m E K, let n(L.TIl = 1 and down(L,m = {L' I L n L' = 0}. The
Invariant-Preserving Model is then defined by the same ground type lcpo's as the HMT
model, relationally structured by
Rt as in Definition 7, for all ground types y,
Rff.h) (l) iff I if:. L,
R'Xfi) (f) iff 'its. (rr(s) /\ f(s) "* 1.) ~ 7T(f(s»), i.e., every 7T E n is an
invariant of f,
RrL.m '" true, for the other ground types y.
Again we get a model of the simply typed "-calculus, in which all ALGOL constants can
be given their intended interpretations and in which support(d) can be defined as the
smallest set L such that dEDI. It has the additional property that RTr.m (d) holds
whenever L n support(d) = 0. A particular instance of this property is:
Let g be of type Prog ~ Prog and f be of type Prog. If 7T E
Pred(Loe - support(g») is an invariant of f, then 7T is also an invariant of
g(f).
This is the reasoning principle which we have applied to Example 5.
We get only half-full abstraction in Theorem 5 because, in contrast to the first-
order sublanguage, an element dEDI (where T is a PASCAL procedure type) is not
necessarily equal to a lub of definable elements in DI but is only bounded above by
such a lub.
Part III
LANGUAGE DESIGN
Chapter 8
Design of the Programming Language FORSYTHE
John C. Reynolds
Contents
1 Introduction 173
2 From ALGOL to FORSYTHE: An Evolution of Types 175
3 Types and the Subtype Relation 181
4 The Semantics of Types 184
5 Phrases and their Typings 190
6 Predefined Identifiers 198
7 Syntactic Sugar 202
8 Reduction Rules 202
9 Examples of Procedures 204
10 Escapes and Completions 206
11 Sequences and Arrays 208
12 Input and Output 211
13 Data Abstraction with Objects 212
14 Other Publications Related to FORSYTHE 219
15 Conclusions and Future Research 219
References 221
A Lexical Structure 223
B Concrete Syntax 225
C Type Checking 227
1 Introduction
In retrospect, it is clear that ALGOL 60 [2, 3] was an heroic and surprisingly
successful attempt to design a programming language from first principles.
Its creation gave a formidable impetus to the development and use of the-
ory in language design and implementation, which has borne rich fruit in the
intervening thirty-six years. Most of this work has led to languages that are
quite different than ALGOL 60, but there has been a continuing thread of con-
cern with languages that retain the essential character of the original language
[4, 5]. We feel that research in this direction has reached the point where it
is desirable to design a modern ALGOL-like language that is as uniform and
general as possible.
©1996 John C. Reynolds. First appeared as Report CMU-CS-96-146, Computer Science Depart-
ment, Carnegie Mellon University, June 28, 1996. Research supported by National Science Foun-
dation Grant CCR-9409997.
174 Chapter 8. Design of the Programming Language FORSITHE
FP LEAP
Call by Value
Call by Name
MIRANDA
SASL HASKELL
PONDER
ALGOL 60
FORSYTHE
First, it is a typed language. It has long been understood that imposing a type
discipline can yield major improvements in compile-time error detection and
in the effiCiency of run-time data representations. However, type systems that
are flexible enough to support sophisticated programming techniques are a
much more recent development.
Second, FORSYTHE has imperative features (i.e. assignment and control
flow) as well as a powerful procedure mechanism. Like all such languages,
it suffers from the problems of aliasing and interference. However, we believe
that imperative programming is a fundamental paradigm that should not be
ignored in programming language design.
John C. Reynolds 175
Finally, FORSYTHE uses call by name rather than call by value. For purely
functional languages this is merely a distinction between orders of evaluation,
but for languages with imperative features it is a fundamental dichotomy in
the way that the imperative and functional aspects are linked; one is tempted
to speak of ALGoL-like versus ISwIM-like languages.
In any event, the following basic operational view, which is implicit in
ALGOL 60, underlies FORSYTHE and distinguishes it from such languages as
ISWIM [6], ALGOL 68 [7], SCHEME [8], and ML [9]: The programming language
is a typed lambda calculus with a primitive type comm(and), such that terms
of this type, when reduced to normal form, are commands in the simple im-
perative language. Thus a program, which must be a term of type comm,
is executed in two phases. First the program is reduced to normal form. (In
ALGOL jargon, the copy rule is repeatedly applied to eliminate procedure calls.)
Then the resulting simple imperative program is executed:
Reduction of lambda expressions (copy rule)
1normal form
Execution of commands in the simple imperative language
The only complication is that, in the presence of recursion, the reduction
phase may go on forever, producing an infinite or partial "normal form". Nev-
ertheless, such an infinite term can still be viewed as a simple imperative pro-
gram; operationally, one simply implements the two phases as coroutines.
Even in this more general situation, the above diagram still describes an
essential restriction on the flow of information: Nothing that happens in the
second phase ever affects anything that happens in the first phase. Thus
FORSYTHE inherits the basic property of the lambda calculus that meaning
does not depend upon the order or timing of reductions. Indeed, reduction
rules can be viewed as equations satisfied by the language.
In contrast, consider the situation in an ISwIM-like language such as
SCHEME or ML that provides assignable function variables. If f is such a vari-
able, then the effect of reducing f (... ) will depend upon when the reduction
occurs relative to the sequence of assignments to f that are executed in the
imperative phase. In this situation, the procedure mechanism is completely
stripped of its functional character.
capture this characteristic by distinguishing two kinds of type (as in [5) and
[10»:
• A data type denotes a set of values appropriate to a variable or expres-
sion.
/
realvar
1
intexp
boolexp eharexp
1 1
boolvar eharvar
1
intvar
However, there is an unpleasant asymmetry here. It can be remedied by
distinguishing, in addition to expressions which can be evaluated but not as-
signed to, acceptors which can be assigned to but not evaluated. Then, for
example, we can write
procedure peculiar(x); intaee x; x := 0
to indicate that peculiar assigns to its parameter but never evaluates it.
Clearly, intvar ::5 intaec, and similarly for the other data types. Moreover,
realaee ::5 intace, since an acceptor that can accept any real number can accept
any integer. Thus the subtype relation is
intaee realexp
+Xr
boolaec boolexp eharaee charexp
\/
boolvar
\/eharvar
realvar intvar
However, there is a further problem. In FORSYTHE, the conditional con-
struction is generalized from expressions and commands to arbitrary phrase
types; in particular one can construct conditional variables. Thus if p is a
boolean expression, n is an integer variable, and x is a real variable, one can
write
if p then n else x
on either side of an assignment command. But when this construction occurs
on the right of an assignment, it must be regarded as a real expreSSion, since
p might be false, while when it occurs on the left of an aSSignment, it must be
regarded as an integer acceptor, since p might be true. Thus the construction
is an int(eger accepting), real (producing) var(iable), which fits into the subtype
relation as follows:
intaee realexp
I~
realaee
/1
(int, real) var
intexp
1/
realvar
~I
intvar
178 Chapter 8. Design of the Programming Language FORSYIHE
/
intexp - intexp
~
realexp - realexp
~
realexp - intexp
/
We have already seen that comm(and) must be introduced as a primi-
tive phrase type. It is also useful to introduce a subtype of comm called
compI( etion):
comm
compi
Essentially, a completion is a special type of command, such as a goto com-
mand, that never returns control.
The advantage of distinguishing completions is that control structure can
be made more evident. For example, in
procedure sqroot(x, y, error); intexp x; intvar y; compi error;
begin if x < 0 then error; C end,
specifying error to be a completion makes it evident that C will never be exe-
cuted when x < o.
As mentioned earlier, FORSYTHE has a type constructor for named prod-
ucts. The basic idea is that the phrase type
(Ll: 01, ••• , Ln: On)
180 Chapter 8. Design of the Programming Language FORSYTHE
is possessed by objects with fields named by the distinct identifiers tl, ... , tn,
in which the field named tk has type 8k. Note that the meaning of this phrase
type is independent of the order of the tk: 8k pairs. We use the term "object"
rather than "record" since fields need not be variables. For example, one could
have a field of type intvar - comm that could be called as a proper procedure,
but not assigned to. (Roughly speaking, objects are more like class members
in SIMULA 67 [11) than like records in ALGOL W [4).)
Clearly, the product constructor should be monotone:
If n ;:: 0 and 81 :<;; 8i and ... and 8 n :<;; 8~ then
(tl:81, ... ,tn:8n):<;; (tl:8i,···,tn:8~).
In fact, a richer subtype relationship is desirable, in which objects can be con-
verted by "forgetting" fields, so that an object can be used in a context requir-
ing a subset of its fields. This relationship (which is closely related to "multiple
inheritance" in object-oriented programming [12)) is expressed by
If n ;:: m ;:: 0 and 8 1 :<;; 8i and ... and 8 m :<;; 8;" then
{tl:8}, ... ,t n :8n ):<;; (tl: 8 i,···,tm:8;").
For example,
partno: int cost: real
~/
(partno: int, cost: real)
I
(partno: intvar, cost: realvar)
At this point, we have summarized the type structure of FORSYTHE <then
called "IDEALIZED ALGOL") as it appeared in about 1981 [5). Since then, the lan-
guage has been generalized, and considerably simplified, by the introduction
of intersection types [13, 14, 15).
(At the outset, a caution must be sounded that this use of the word "inter-
section" can be misleading. If one thinks of types as standing for sets, than
the intersection of two types need not stand for the intersection of the two
corresponding sets. In earlier papers, we used the term "conjunctive type",
but this was equally misleading in other contexts, and never became widely
accepted.)
The basic idea is to introduce a type constructor &, with the interpretation
that a phrase has type 81 & 82 if and only if it has both type 81 and type 82.
This interpretation leads to the subtype laws
8 1 & 82:<;; 81
8 1 & 82:<;; 82
If 8 :<;; 8 1 and 8 :<;; 82 then 8 :<;; 81 & 82 ,
which assert that 81 & 82 is a greatest lower bound of 81 and 82. (Note that
the introduction of the intersection operation makes the subtype relation a
preorder rather than a partial order, since one can have distinct types, such as
John C. Reynolds 181
8 1 & 82 and 82 & 8 1 , each of which is a subtype of the other. In this situation,
we will say that the types are equivalent.)
We will see that intersection types provide the ability to define procedures
with more than one type. For example
procedure poly(x); x x x + 2
can be given the type (intexp - intexp) & (realexp - realexp). At present,
however, the main point is that intersection can be used to simplify the struc-
ture of types.
First, the various types of variables can be regarded as intersections of
expressions and acceptors. For example, intvar is intexp & intacc, realvar is
realexp & realacc, and (int, real) var is realexp & intacc.
Second, a product type with more than one field can be regarded as an
intersection of product types with single fields. Thus, instead of
one writes
tl: 8 1 & ... & tn: 8n .
Note that the field-forgetting relationship becomes a consequence of 8 1 & 82 :s;
8;.
A final simplification concerns acceptors. The meaning of a 8 acceptor a
(for any data type 8) is completely determined by the meanings of the com-
mands a := e for all 8 expressions e. Thus a has the same kind of meaning
as a procedure of type 8exp - comm. As a consequence, we can regard 8acc
as an abbreviation for 8exp - comm, and a := e as an abbreviation for a(e).
(As discussed in Section 8, this treatment of assignment as procedure call is a
controversial generalization of the usual concept of assignment.)
The subtype relation ~prim for primitive types is the partial order
value
/I~ comm
real bool char
campI
I
int
For types, ~ is the least preorder such that
8 ~ns
8 1 & 82 ~ 8 1
8 1 & 8 2 ~ 82
If 8 ~ 81 and 8 ~ 82 then 8 ~ 8 1 & 82
If P ~prim p' then p ~ p'
If 8 ~ 8' then L: 8 ~ L: 8'
If 8;' ~ 81 and 82 ~ 8; then 81 - 82 ~ 8;' - 8;
L: 81 & L: 8 2 ~ L: (8 1 & 82)
(8 - 8 1) & (8 - 82) ~ 8 - (8 1 & 8 2)
ns ~ t:ns
ns~8-ns.
We write 8 "" 8', and say that 8 and 8' are equivalent, when 8 ~ 8' and
8' ~ 8. The first four relationships establish that ns is a greatest type and that
81 & 82 is a greatest lower bound of 8 1 and 82. Note that we say "a" rather
than "the"; neither greatest types nor greatest lower bounds are unique, since
we have a preorder rather than a partial order, However, any greatest type
must be equivalent to ns, and any greatest lower bound of 81 and 82 must be
equivalent to 81 & 8 2.
The fact that ns is a greatest type and & is a greatest lower bound operator
has the following consequences:
81 & (82 & 83) "" (81 & 82) & 83
8&os",,8
ns&8"" 8
81 & 82 "" 82 & 81
8&8",,8
If 8 1 ~ 8;' and 8 2 ~ 8; then 8 1 & 82 ~ 8;' & 8;
8 ~ 81 & 82 iff 8 ~ 8 1 and 8 ~ 82 .
The next three relationships in the definition of ~ assert that primitive
types are related by ~prim, that the object-type constructor is monotone,
and that - is antimonotone in its first operand and monotone in its second
John C. Reynolds 183
Ouns"'ns
0 1 u (02 & 03) '" (0 1 U 02) & (0 1 u 03)
P U L:O '" ns
P U (01 - 02) '" ns
L:Ol U (02 - 03) '" ns
PI U P2 '" PI Uprim P2 when PI Uprim P2 exists
PI U P2 '" ns when PI Uprim P2 does not exist
t: 01 U t: 02 '" L: (01 U 02)
Ll: 0 1 U L2: 02 '" ns when Ll 01= L2
(01 - OJ) U (02 - O2) '" (01 & 02) - (Oi U O2) .
The types int, real, bool, char, value, comm, compI, and ns are actually
predefined type identifiers (whose meaning can be redefined by the lettype
definition to be discussed later, but which take on standard meanings outside
of such redefinitions). Additional predefined type identifiers are provided to
abbreviate various commonly occurring nonprimitive types. As discussed in
the previous section, when 8 is any of the character sequences int, real, bool,
or char that denote data types,
8acc def 8 - comm
(e.g. intacc def int - comm), and
8var def 8 & 8acc .
There are also abbreviations for commonly occurring types of sequences.
In general, a sequence s of element type 0 and length n is an entity of type
(int - 0) & len: int such that the value of s.len is n and the application s i is
well-defined for all integers i such that 0 ~ i < n. (Of course, the proviso on
definedness is not implied by the type of the sequence.)
The following type identifiers are predefined to abbreviate specific types
184 Chapter 8. Design of the Programming Language FORSYTHE
of sequences:
8seq def (int - 8) & len: int
8accseq def (int - 8acc) & len: int
8varseq def (int - 8var) & len: int
commseq def (int - comm) & len: int
complseq def (int - compl) & len: int .
For instance, a 8varseq, in ALGOL terminology, is a one-dimensional 8 array
with a lower bound of zero and an upper bound one less than its length. A
8seq is a similar entity whose elements can be evaluated but not assigned to,
and a 8accseq is a similar entity whose elements can be assigned to but not
evaluated. For example, charseq is the type of string constants.
For each type 9, we write [9] for the object (e.g. set) denoted by 9. When-
ever 9:$ 9', we write [9 :$ 9'] for the implicit conversion morphism (e.g. func-
tion) from [9] to [9']. Two requirements are imposed on these implicit con-
version morphisms:
• For all types 9, the conversion from [9] to [9] must be an identity:
[9:$ 9] = 1[0] •
• Whenever 9 :$ 9' and 9' :$ 9", the composition of [9:$ 9'] with
[9' :$ 9"] must equal [9 :$ 9"], i.e. the diagram
[9] [9:$ 9'~ [9']
[9~~ [19'~9"1
[9"]
must commute.
These requirements coincide with a basic concept of category theory: [-]
must be a functor from the preordered set of types Mewed as a category) to
the semantic category.
The above requirements determine the semantics of equivalence. When
9 "" 9', the diagrams
[9] [9:$ 9'~ [9']
[(}]
the inner diamond must commute and, for all (} such that (} :::; fJ 1 & (}2,
the two triangles must commute.
s
the inner diamond must commute and, for all objects sand morphisms
f1 and f2 that make the outer diamond commute, there must be a unique
morphism from s to [91 & 92] that makes the two triangles commute.
Clearly, this strengthening is something of a leap of faith. Thus it is reas-
suring that our definition coincides with a standard concept of category
theory: we have defined [91 & 92] to be the pullback of [91], [92], and
[91 U 92] (which is unique up to isomorphism).
For sets or domains, the pullback is
For example,
[intvar] = [int & (int - comm)] "" [int] x [int - comm]
[L:9 1 & (92 - ( 3)] "" [L:9 1 ] x [92 - 93] "" [91] x [92 - 93]
and, when L1 '* L2,
"" [91].
For example,
[int & real] "" [int]
[compi & comm] "" [compl] .
• If [91] and [92] are subsets of [91 u 92], and [9 1 :$; 91 u 92] and
[92 :$; 91 u 92] are identity injections, then
[91 & 92] "" { (X1,X2) I Xl E [91] and X2 E [92] and Xl = X2}
[9 1 ]
fI
---'....:.----. [9;']
lO.&O,Sy/ ~sOiUO'1
[91 & 92] [9;' u 9;]
[O'&O'S~ ASOiUO,1
[~]
f2
. [~]
190 Chapter 8. Design of the Programming Language FORSYTHE
[real] f2 • [real]
On the other hand,
[(int - int) & (char - char)] ~ [int - int] x [char - char] ,
since in this case the hexagonal constraint on fl and f2 is vacuously true
because [int & char] is the empty set.
• Intersection
TT I- P : 9 1 & 92
Then there are rules for typing identifiers, applications (procedure calls), and
conditional phrases:
• Identifiers
TT I- L: TT(L) when L E dom TT
• Applications
TT I- PI : 9 - 9'
TT I- P2 : 9
TT I- PI P2 : 9'
• Conditionals
TT I- PI : bool
TT I- P2 : 9
TT I- P3 : 9
TT I- if PI then P2 else P3 : 9
Notice that the conditional construction is applicable to arbitrary types.
Next we consider abstractions (sometimes called lambda expressions),
which are used to denote procedures. Here there are two cases, depending
upon whether the type of the argument to the procedure is indicated explic-
itly. In the explicit case we have:
• Abstractions (with explicit typing)
[TT I L: 9j ] I- P : 9'
Here the abstraction provides no explicit constraints on the type (). For exam-
ple, for the abstraction i\x. x x x + 2, one can use this rule to infer either of the
types int - int or real - real. More vividly, for the abstraction i\x. x, one can
infer any type of the form () - ().
At this point, one might ask why one would ever use explicit typing. Sen-
sible answers are to make the program more readable, or to insure that a pro-
cedure has the typing one expects, rather than just some typing that makes
the overall program type-correct. But a more stringent answer is that it has
been proven that there is no algorithm that can typecheck an arbitrary im-
plicitly typed program in the intersection type discipline [13, 14, ISJ. Thus
the FORSYTHE implementation requires some explicit type information to be
provided. The exact nature of this requirement is described in Appendix C.
Next there are constructions for denoting objects and selecting their fields:
• Object Construction
7TI-P:(}
7T I- (l == p) : (t: ())
• Field Selection
7T I- P : (t: ())
7T I- p.l : ()
The first of these forms denotes objects with only a single field; objects with
several fields can be denoted by the merge construction, which will be de-
scribed later. Note the role of the connective ==, which is always used to con-
nect identifiers with their meanings.
Then comes a long list of rules describing various types of constants and
expressions:
• Constants
7T I- (nat const) : int
• Arithmetic Expressions
7T I- P : int 7T I- p: real
7T I- +p: int 7T I- +P : real
7T I- -p: int 7T I- -P : real
7T I- PI : int
7T I- P2 : int 7T I- PI : real
7T I- P2 : real
7T I- PI + P2 : int
7T I- PI - P2 : int 7T I- PI + P2 : real
7T I- PI X P2 : int 7T I- PI - P2 : real
7T I- PI -:- P2 : int 7T I- PI X P2 : real
7T I- PI rem P2 : int 7T I- PIiP2 : real
7T I- PI ** P2 : int
7T I- PI : real
7T I- P2 : int
7T I- PI t P2 : real
• Relations
7T I- PI : real 7T I- PI : char
7T I- P2 : real 7T I- P2 : char
7T I- PI : bool
7T I- PI = P2 : bool 7T I- PI = P2 : bool P2 : bool
7T I- PI * P2 : bool 7T I- PI * P2 : bool
7T I-
• Sequential Composition
TT I- PI: comm TT I- PI: comm
TT I- P2 : comm TT I- P2 : compl
TT I- PI ; P2 : comm TT I- PI ; P2 : compl
TT I- P" : 0"
[TT 111:011 ... 11,,:0,,] I- p: 0
TT I- let 11 :; PI. ... , 1" :; P" inp : 0
For example, in place of the ALGOL block
begin procedure p(x); 0 x; Bproc; Bend,
one can write
let P :; AX: O. Bproc inB .
Such definitions are not limited to procedures. One can write
letx :; 3inB,
which will have exactly the same meaning as the phrase obtained from B by
substituting 3 for x. Note, however, that this is not a variable declaration; x
John C. Reynolds 195
has the type int (the type of 3) and cannot be assigned to within B. Moreover.
if y is an integer variable then
letx == yinB
has the same meaning as the phrase obtained from B by substituting y for x.
i.e. x is defined to be an alias of y.
Definitions can also be explicitly typed. indeed one can mix implicit and ex-
plicit typing in the same let-construction. To describe this situation. we adopt
the convention that. when an inference rule contains the notation {... }? it
stands for two rules. obtained (i) by deleting the notation. and (li) by replacing
it by the contents of the braces. (When the notation occurs n times. the rule
stands for the 2 n rules obtained by taking all possible combinations.) Using
this notation. we have a general rule that includes the previous one as a special
case.
• Nonrecursive Definitions
IT I- Pn : en
[IT ILl: e1 I ... I Ln: en ] I- P : e
In this form, the recursively defined identifiers and their types must be listed
before the definitions themselves, so that the reader (and compiler) knows that
these identifiers have been rebound, and what their types are, before reading
any of the Pi.
To keep the above rule simple, we have assumed that the two lists in a
recursive definition define the identifiers lI. ... , In in the same order. In fact,
however, the order of the items in each of the lists is arbitrary. However,
for both the recursive and nonrecursive definitions, lI. .. . , In must be distinct
identifiers.
Next we consider a construction for intersecting or "merging" meanings.
Suppose PI has type OI. P2 has type 02, and 0 1 u 02 "" ns, so that [01 & 02] ::::
[01 ] X [02]. One might hope to write PI. P2 to denote a meaning of type
0 1 & 02.
Unfortunately, this conflicts with the behavior of subtypes, since PI and P2
might have types 0i and O2 such that 0i ~ 01 and O2 ~ 02 but 0i u O2 i= ns.
For example, although (a == 3,b == 4) and b == 5 respectively have types a:int
and b: int, whose least upper bound is ns, the phrase
(a==3,b==4),b==5
would be ambiguous.
Our solution to this problem is to permit PI, P2 only when P2 is an abstrac-
tion or an object construction, whose meaning then overwrites all components
of the meaning of PI that have procedural types, or all object types with the
same field name. The inference rules are:
• Merging
[TT I L: Oi ] I- P2 : 0'
TT I- PI : P
John C. Reynolds 197
7T f- P2 : e
7T f- PI : P
7T f- (PI, L == P2) : P
7T f- PI : e - e'
7T f- (PI. L == P2) : e - e'
7T f- PI : ((1: e)
when L '* Ll
7T f- (PI, L == P2) : (Ll: e)
Next, we introduce a construction for defining a sequence by giving a list
of its elements:
• Sequences
7T f- Po : e
when n;;:: 1
7T f- Pn-l : e
where (p / Ll, ••• ,Ln - el,il"'" en,in ) denotes the result of simultaneously sub-
stituting el,il' .•. , en,i n for the free occurrences (as type identifiers) of Ll, ... , Ln
in type expressions within p. (As with the definitions described earlier, LI. ••• ,
Ln must be distinct identifiers.)
As a simple example,
lettype t == intin'\x: t. '\y: t. x x Y + 2
19B Chapter B. Design of the Programming Language FORSITHE
will have type int - int - into Notice that this is a transparent, rather than
opaque, form of type definition; e.g. within its scope, t is equivalent to int,
rather than being an abstract type represented by integers (which would make
the above example ill-typed).
Using the alternative operator in this construction provides another way
to define procedures with multiple types. For example,
Iettype t == int I realin.\x: t . .\y: t. x x Y + 2
will have both type int - int - int and real - real - real. (The use of the
alternative operator in type definitions was suggested by Benjamin Pierce [IB].)
The same string of characters can be used as both an ordinary identifier
and as a type identifier without interference. A change in its binding as an
ordinary identifier has no effect on its meaning as a type identifier, and vice-
versa.
6 Predefined Identifiers
In place of various constants, FORSYTHE provides predefined (ordinary) iden-
tifiers, which may be redefined by the user, but which take on standard types
and meanings outside of these bindings. In describing these identifiers, we
simply state the type of their unbound occurrences, e.g. we write true: bool as
an abbreviation for the inference rule
7T f- true: bool when true It dom 7T •
In the first place, there are the usual boolean constants, a skip command
that leaves the state unchanged, and a standard phrase of type ns:
true: bool false: bool
skip: conun null: ns .
(Of course, there are many other nonsense phrases-phrases whose only types
are equivalent to ns-which are all too easy to write, but null is the only such
phrase that will not activate a warning message from the compiler. The point is
that there are contexts in which null is sensible, for example as the denotation
of an object with no fields.)
The remaining predefined identifiers denote built-in procedures. Four of
these procedures serve to declare variables. For 8 = int, real, bool, or char:
new8var: 8-
(8var - conun) - conun& (8var - compl) - compI
& (8var - int) - int & (8var - real) - real
& (8var - bool) - bool & (8var - char) - char) .
The application new8var init p causes a new 8 variable to be added to the
state of the computation; this variable is initialized to the value init, then the
procedure p is applied to the variable, and finally the new variable is removed
from the state of the computation when the call of p is completed (or when
John C. Reynolds 199
and to convert character sequences in decimal notation into integers and real
numbers:
charseq_to_real : charseq - real.
The last two procedures ignore nondigits, except for leading minus signs and
(in the case of charseq_to1eal) the first occurrence of a decimal pOint.
John C. Reynolds 201
One might expect the procedure that converts integers into their decimal
representations to have the type int - charseq, but this would be unsuitable
since charseq is not a data type. Instead, there is another declarator:
int-to_charseq : int -
(charseq - comm) - comm& (charseq - compI) - compI
& (charseq - int) - int & (charseq - real) - real
& (charseq - booI) - bool & (charseq - char) - char) .
The application int-to_charseq n p converts the integer n to a character se-
quence giving its decimal representation, and applies the procedure p to this
character sequence.
The conversion of real numbers to a decimal representation is conSider-
ably more complex, for several reasons: One must deal with both a fraction
and exponent, the digit-length of the fraction must be specified, and there
is no universally accepted notation. The conversion is implemented by the
declarator
reaLto_charseq : real - int -
( charseq - int - comm) - comm
& (charseq - int - compl) - compI
& (charseq - int - int) - int & (charseq - int - real) - real
& (charseq - int - bool) - bool & (charseq - int - char) - char) .
Let r be a positive nonzero real number and f x 10l( be a closest approxi-
mation to r such that x is an integer, 0.1 ::; f < 1.0, and f has a decimal
representation containing d digits (to the right of the decimal point). Then
reaLto_charseq r d p applies the procedure p to the digit sequence represent-
ing f (excluding the decimal point) and the integer x. (Notice that x, as well as
f, can depend upon d when r is slightly less than a power of ten.)
Clearly, if FORSYTHE grows beyond the experimental stage, it will be nec-
essary for the predefined identifiers to provide richer capabilities than are
described above. To do this in an "upward compatible" manner, one can ob-
viously add new predefined identifiers. But the type system provides another,
more interesting possibility: One can lower the type of an existing predefined
identifier to a subtype of its original type, and give a new meaning to the iden-
tifier, prOviding the implicit coercion induced by the subtype relation maps
the new meaning back into the old one.
For example, one might change the type of newoutchannel to
newoutchannel : charseq -
( « characc & flush: comm) - comm) - comm
& «characc & flush: comm) - compI) - compI) .
As before, the application newoutchannel s p would open the file s and ap-
ply the procedure p to an output channel c, and each time that p assigned a
character to c, the character would be output to the file. But now p could also
execute the command c. flush, which might flush the output buffer.
202 Chapter 8. Design of the Programming Language FORSYTHE
7 Syntactic Sugar
Several abbreviations are provided to avoid repeating type information (or the
absence thereof) when several identifiers range over the same type. In types,
lI, ... ,In: () abbreviates lI: () & ... & In: ().
In abstractions
All, ... ,In: (}I 1 ••• 1(}k. P abbreviates
All: (}I 1.. ·1 (}k • ••• Al n : (}I I· .. 1(}k. P
and
All, ... ,In. P abbreviates All .... Al n • p.
In recursive definitions
lI. ... ,In: () abbreviates lI: (), ... ,In: ().
8 Reduction Rules
As mentioned in the introduction, an operational way of describing FORSYTHE
is to say that a program is a phrase of type eomm, in an enriched typed lambda
calculus, that is executed by first reducing the phrase to normal form (more
precisely, to a possibly infinite or partial normal form) and then executing
John C. Reynolds 203
the normal form, which will be a program in the simple imperative language.
Although we will not pursue this view in detail, it is useful to list some of the
reduction rules, which preserve the meanings of programs and thus provide
insight into their semantics. (We will ignore types in these rules, since they
play no role in the process of reduction.)
First there is the lambda-calculus rule of J3-reduction:
(AL. pdp2 ~ (pIlL - P2)
where (PIlL - P2) denotes the result of substituting P2 for the free occur-
rences of L (except as a type identifier or field name) in Pl.
Then there is a rule for selecting fields:
(L == p).L ~ p,
two rules for conditionals:
(if PI thenp2elsep3)P4 ~ if PI thenp2P4elseP3P4
(if PI thenp2 elsep3).L ~ if PI thenp2.Lelsep3.L,
a rule for nonrecursive definitions:
letLI == PI, ... ,Ln == Pninp ~ (pILl ... ,Ln - PI, ... ,Pn),
and a rule for the fixed-point operator:
recp ~ p(recp).
In addition, there are a number of rules dealing with the merging operation:
(PI, AL. P2) P3 ~ (AL. P2)P3
(PI, L == P2)P3 ~ PI P3
(PI, AL. P2).L' ~ PI.L'
(PI, L == P2).L ~ (L == P2).L
(PI. L == P2).L' ~ PI.L' when L '* L' .
(It should be noted that these rules are not complete; in particular, it is not
clear how to provide rules for reducing merges in contexts that require primi-
tive types.)
The reduction rules make it clear that call by name pervades FORSYTHE.
For example, if Pc is any phrase that does not contain free occurrences of L,
and PI and P2 are any phrases, then
(At. Pc)PI ~ Pc
let L == PI in Pc ~ Pc
(LI == PI, L2 == P2).LI ~ PI
(LI == PI, L2 == P2).L2 ~ P2
hold even when PI or P2 denote nonterminating computations.
Moreover, call by name even characterizes the assignment operation, since
assignments are abbreviations for procedure calls. For example, assuming that
x is an ordinary integer variable (e.g. declared using newintvar),
(Ay. x := 3) := P and (Ay. x := Y + Y) := P
204 Chapter 8. Design of the Programming Language FORSYTHE
would evaluate the expression p zero and two times respectively. This is prob-
ably the most controversial design decision in FORSYTHE, since it makes the
language, so to speak, more ALGoL-like than ALGOL itself. It may degrade the
effiCiency with which the language can be implemented but, as demonstrated
in Sections 11 and 13, it leads to some interesting programming techniques.
9 Examples of Procedures
In this and the next four sections, we provide a variety of examples of
FORSYTHE programs. Many of these examples are translations of ALGOL W pro-
grams given in [10), which the reader may wish to compare with the present
versions.
To define a proper procedure that sets its second parameter to the factorial
of its first parameter, we define fact to be the obvious command, abstracted
on an integer expression n and an integer variable f:
let fact:int - intvar - comm == An. Af.
newintvar 0 Ak. (f := 1 ; while k =1= n do (k := k + 1 ; f := k x f)
Here we have specified the necessary types in the nonrecursive definition, but
instead we could have specified them in the abstractions:
let fact == An:int. Af:intvar.
newintvar 0 Ak. (f := 1 ; while k =1= n do (k := k + 1 ; f := k x f)
(Although FORSYTHE supports either method of specifying the types of nonre-
cursive procedures, in these examples we will usually give types in definitions
rather than in abstractions, since this approach is more readable, and in some
cases gives more efficient typechecking.)
This procedure has the usual shortcoming of call by name: It will repeat-
edly evaluate the expression n. To remedy this defect, we replace n by a local
variable (also called n) that is initialized to the input parameter n. Notice that
this is equivalent to the definition of call by value in ALGOL 60.
let fact:int - intvar - comm == An..\f.
newintvar nAn.
newintvar 0 Ak.
(f := 1 ; while k =1= n do (k := k + 1 ; f := k x f)
We can also modify this procedure to obtain the effect of calling f by result
(as in ALGOL W [4». We replace f by a local variable, and then assign the final
value of this local variable to the parameter f, which now has type intacc, since
it is never evaluated by the procedure.
let fact:int - intacc - comm == An. Af.
newintvar nAn. newintvar 1 Mocalf.
(newintvar 0 Ak.
while k =1= n do (k := k + 1 ; localf:= k x localn;
f := localf)
John C. Reynolds 205
b must be called by name to permit its repeated evaluation. (Both multiply and
repeat are such simple procedures that it is obviously worthwhile to compile
them inline.)
Repeated evaluation is also crucial to the following program, where the
call of the procedure sum sets s to 2:7=a X(i) x Y(i) by repeatedly evaluating
XU) x Y(i> while increasing the variable i:
=
letinline sum:intvar - int - comm i\i. Ae.
begin s := 0; i := a-I; while i < b do (i := i + 1; s := s + e) end
insumi (X(i) x Y(i))
This way of using call by name, known as "Jensen's device", was illustrated in
the original ALGOL 60 Report [2, 3] by the exemplary procedure Innerproduct.
206 Chapter 8. Design of the Programming Language FORSYTHE
Finally, we give two higher-order procedures that are akin to the for com-
mand:
letinline for:int - int - (int - comm) - comm == i\l. AU. Ab.
newintvar(l- 1) Ak. newintvar U AU.
while k < U do (k := k + 1; b k),
fordown:int - int - (int - comm) - comm == i\l. AU. Ab.
newintvar(u + 1) Ak. newintvar I i\l.
while k > I do (k := k - 1 ; b k)
in for 09 Ai. s := s + XU) x Y(i)
Notice that, in these procedures, since the procedure b takes a parameter of
type int, the application b k cannot change the value of k. Moreover, although
this application can change the values of the parameters I and u, the interval
iterated over is always determined by the initial values of these parameters.
Even though the procedures sum, for, and fordown are moderately com-
plex, we have used letinline to define them, since they evaluate some of their
parameters repeatedly. When a procedure is defined by letinline, not only are
its calls compiled into inline code, but also the execution or call of the param-
eters of the procedure. In particular, the expressions i and X(i) x Y(i) in the
call of sum will be executed inline, and the procedure Ai. s := s + X(i) x Y(i)
in the call of for will be called inline.
This makes sense because the final assignment fin := local, really means
fin local, which will be a completion when fin has type int - compI. One can
even use intersection to give newintvarres both its conventional type and this
variant in a single definition:
letinline newintvarres:int - (intacc - (intvar - comm) - comm
& (int - compi) - (intvar - comm) - compl) ==
i\init. i\fin. i\b. newintvar init i\local. (b local; fin:= local)
We can also define a procedure slice that, given an array and two integers,
yields a subsegment of the array with new bounds. The simplest definition
is
Ietinline slice: (int - int) - int - int - intarray
& (int - intacc) - int - int - intaccarray) ==
i\X. M. AU. (X,ll == I, ul == u)
A safer alternative, which checks applications of the array against the new
bounds, is
John C. Reynolds 209
(Giving X the type int - intvar indicates that exchange does not evaluate
bounds; e.g. it would also be applicable to an integer variable sequence.) Then
the sort procedure is:
let maxsort:intvararray - comm == AX.
newintvar X.II Aa. newintvar X.ul Ab.
while a ~ b do newintvar 0 Aj.
(max(slice X a b) j; exchange X j b; b := b - 1)
be avoided by taking advantage of the fact that assignments are really applica-
tions. By substituting the definition of newintvarres into the definition of max
and reducing, we find that the definition of max is equivalent to
letinline max:intarray - intacc - comm == "AX. "Aj.
newintvar X.lI "Aa. newintvar X.ul "Ab.
newintvar a Mocal.
(while a < b do
(a := a + 1 ; if X a > X local then local:= a else skip);
j:= local)
where j := local is syntactic sugar for j local. Thus the second parameter
of max can be any procedure of type int - comm, i.e. any proper procedure
accepting an integer; the effect of max will be to apply this procedure to the
subscript of the maximum of X.
To avoid the spurious initialization, we make this parameter a procedure
that carries out the appropriate exchange, dispenSing with the variable j en-
tirely:
let maxsort: intvararray - comm == AX.
newintvar X.ll "Aa. newintvar X.ul "Ab.
while as b do (max(slice X a b) "Aj. exchange X j b; b := b - 1)
A similar use of "generalized call by result" occurs in the following defini-
tion of quicksort:
letinline partition:intvararray - int - intacc - comm == AX. Ar. "Ap.
newintvar X.ll "Ac. newintvar X.ul "Ad. newintvar r "Ar.
(while c s d do
if Xc s r then c := c + 1 else
if X d > r then d := d - 1 else
(exchange X c d;c:= c + l;d:= d -1);
pc)
in
letrec quicksort: intvararray - comm
where quicksort == AX.
newintvar X.ll "Aa. newintvar X.ul Ab.
ifa<bthen
if X a > X b then exchange X a b else skip;
partition(sliceX (a + 1) (b -1») (X a + X b) + 2) "Ac.
(quicksort(slice X a (c - 1» ; quicksort(slice X c b»)
else skip
As a further example of the power of declarators, we can define the type of
triangular arrays of real variables, along with an appropriate declarator (which,
to keep the example simple, initializes the array elements to zero):
John C. Reynolds 211
in
escape Adone. loop
readint done Am.
readint (writereal m; oc:= #\n; done) An.
if n = 0 then writecharseq 'division by zero\n' else
(writereal(mjn) ; oc:= #\n) .
Here repeat refers to the procedure defined in Section 9, while #0, #9, #.,
and #\n are character constants denoting the digits 0 and 9, the decimal point,
and the new-line character.
The procedure readint uses a local character variable sequence to store
the digit sequence being read, so that the number of digits is limited (to
29 in this example) by the length of the sequence. In fact, it is possible to
avoid this limitation by programming readint recursively and using charac-
ter sequences that are procedural functions rather than values of a variable
sequence:
Ietrec readint: compi - intacc - comm,
readintl : charseq - intacc - comm
where
readint == Anonumber. Aa.
iC(AC. newcharvar C AC.
if is_digit C then readintl (M. c, len == 1) a else
readint non umber a,
eof == nonumber),
readintl == AS. Aa. newintvar s.len M. escape Ae.
iC(AC. newcharvar C AC.
if is_digit C
then readintl (M. if i = I then C else s i, len == 1+ 1) a
else a := charseq_to_int s,
eof == a := charseq_tojnt s; e) .
Here readintl s a reads digits until encountering a nondigit, appends these
digits on the right of the sequence s, converts the resulting sequence into an
integer, and assigns the integer to a.
Unfortunately, however, this version of readint is neither perspicuous nor
efficient-and is not recommended as good programming style.
Here newset is a declarator that creates an object of type set, initialized to the
empty set. Thus
newset : (set - comm) - comm .
Actually, we could give newset the more general type
(set - comm) - comm & (set - compl) - compI,
which would allow reachable to have the more general type
node - (node - set) - (set - comm) - comm& (set - compl) - compI).
This generality, however, is unnecessary for our example and would distract
from our argument. Thus, in this section, we will limit our declarators to the
case where their calls are commands.
Next, we refine the reachability procedure to provide greater flexibility for
the representation of sets. In place of the object type set, we introduce differ-
ent object types for the different sets used in the program:
• setg for the sets produced by applYing g,
• sen for the set t,
• setu for the set u.
The basic idea is to limit the fields of each of these object types to those
procedures that are actually needed by our program. However, even greater
flexibility is gained by taking advantage of the fact that the sets t and U are
declared at the same time, and that U is always a subset of t. For this purpose,
we introduce a "double declarator",
newdoubleset : (sett - setu - comm) - comm
such that newdoubleset .\t: sett. AU: setu. C executes C after binding both t
and u to new (initially empty) sets. Morever, to enforce the invariant u ~ t, we
will eliminate the operation t. insertnew and redefine u. insertnew to insert its
argument (which must not already belong to t) into both u and t.
Thus we have
lettype setg == (iter: (node - comm) - comm),
sett == (member: node - bool & iter: (node - comm) - comm) ,
setu == (insertnew: node - comm
& pick: comm - (node - comm) - comm)
in
let reachable: node - (node - setg) - (sen - comm) - comm ==
AX. Ag. Ap. newdoubleset ,\to AU.
(u.insertnew x;
escape AOUt. loop u.pick out Ay. (g y) .iter AZ.
if - t.member Z then u.insertnew Z else skip;
p t)
Notice that we have retained the iter field for objects of type sett, even though
this procedure is never used in our program. The reason is that the result of
John C. Reynolds 215
reachable is an object of type sett, for which the user of reachable may need
an iteration procedure.
Now we can define the representation of t and u by programming
newdoubleset. Within this declarator, we represent t by a characteristic vector
c, which is a boolean variable array that is indexed by nodes, i.e. a procedure
of type node - boolvar, such that
t = {y I y : node 1\ C Y = true} .
We also represent both t and u by a node variable sequence w that (with the
help of two integer variables a and b) enumerates the members of these sets
without duplication. Specifically,
t={wkIO:s;k<b},
u={wkla:s;k<b}.
Thus we have
letinline newdoubleset: (sett - setu - comm) - comm == ~p.
newboolvarnodea"ay(~n. false) ~c.
newnodevarseq N (~k. dummynode) ~ w.
References
[I] Reynolds, J. C. Preliminary Design of the Programming Language FORSYTHE. Re-
port no. CMU-CS-88-159, Carnegie Mellon University, Computer Science Depart-
ment, June 21, 1988.
[2] Naur, P. et al. Report on the algorithmic language ALGOL 60. Communications of
the ACM, vol. 3 (1960), pp. 299-314.
[3] Naur, P. et al. Revised report on the algorithmic language ALGOL 60. Communi-
cations of the ACM, vol. 6 (1963), pp. 1-17. See Chapter l.
[11] Dahl, 0.-]., Myhrhaug, B., and Nygaard, K. SIMUlA 67 Common Base Language.
Publication, no. S-2, Norwegian Computing Center, Oslo, Norway, May 1968.
[I2] Cardelli, 1. A semantics of multiple inheritance. In Semantics of Data Types, in-
ternational Symposium, Sophia-Antipolis, France, June 27-29, edited by G. Kahn,
D. B. MacQueen, and G. D. Plotkin. Lecture Notes in Computer Science, vol. 173,
Springer-Verlag, Berlin, 1984, pp. 51-67.
[13] Coppo, M., Dezani-Ciancaglini, M., and Venneri, B. Functional characters of solv-
able terms. Zeitschrift fUr Mathematische Logik und Grundlagen der Mathematik,
vol. 27 (1981), pp. 45-58.
[14] Coppo, M., Dezani-Ciancaglini, M., Honsell, F., and Longo, G. Extended type struc-
tures and ruter lambda models. In Logic Colloquium '82, Florence, Italy, August
23-28,1982, edited by G. Lolli, G. Longo, and A. Marcja. Studies in Logic and the
Foundations ofMathematics, vol. 112, North-Holland, Amsterdam, 1984, pp. 241-
262.
[15] Hindley, J. R. Types with intersection: an introduction. Formal Aspects of Com-
puting, vol. 4 (1992), pp. 470-486.
[28) O'Hearn, P. W., Power, A. J., Takeyama, M., and Tennent, R. D. Syntactic control of
interference revisited. In Mathematical Foundations of Programming Semantics,
Eleventh Annual Conference, Tulane University, New Orleans, Louisiana, edited
by S. Brookes, M. Main, A. Melton, and M. Mislove. Electronic Notes in Theoreti-
cal Computer Science, vol. 1, Elsevier Science (http://www.elsevier.nl).
1995. See Chapter 18.
• An identifier, i.e. (id), which is a sequence of one or more letters, digits, and
underscore symbols that begins with a letter and is not a keyword.
In this report, keywords, and also identifiers that are used as type identifiers,
are typeset in boldface, but no such font distinction is made in the language
actually read by the computer.
• A natural-number constant, i.e. (nat const), which is a sequence of one or more
digits.
• A real-number constant, i.e. (real const), which is a sequence of digits and deci-
mal points beginning with a digit and containing exactly one decimal point; this
sequence may optionally be followed by a scale factor, which consists of the
letter E or e, an optional + or - sign, and a natural-number constant.
• A character constant, i.e. (char const) , which is the character #, followed by a
character item, which is one of the following:
224 Chapter 8. Design of the Programming Language FORSYTHE
- a character other than the backslash \, the newline symbol, or the tab
symbol,
- \ \, denoting the backslash,
- \n, denoting a newline symbol,
- \ t, denoting a tab symbol,
- \', denoting the quotation mark ' ,
- a backslash, followed by three digits (which are interpreted as an ASCn
code in octal representation).
• A string constant, i.e. (string const), which is a sequence of zero or more string
items, enclosed in the single quotation marks • and ',where a string item is
one of the following:
- a character other than the backslash \, the newline symbol, the tab sym-
bol, or the quotation mark "
- \ \, denoting the backslash,
- \n, denoting a newline symbol,
- \ t, denoting a tab symbol,
- \', denoting the quotation mark "
- a backslash, followed by three digits (which are interpreted as an ASCn
code in octal representation),
- the backslash, followed by a blank, tab, or newline, followed by a sequence
of zero or more characters other than a backslash, followed by a back-
slash.
The last form of string item has no effect on the meaning of the string con-
stant. It is included to allow such constants to run over more than one line of a
FORSYTHE program.
A number of the symbols used in this report (including the concrete syntax given
in Appendix B) are not available in ASCH. They must be translated into lexemes as
follows:
publication ascii publication ascii
-> :S <=
- ~ >=
.\ \
A A and
x * v or
div ~ implies
oF = iff
(p 0) ::= (id) I (nat const) I (real const) I (char const) I (string const)
I «p 16» I begin(p 16) end
(hp 0) ::= if(p 16) tben(p 16) else(p 13)
I while(p 16) do(p 13)
Iloop(p 13)
I A(id list) : (alt type). (p 13)
I A(id list). (p 13)
I ree: (type 1). (p 13)
I let (let list) in(p 13) Iletinline(let list) in(p 13)
Iletrec(letrec list) where(where list) in(p 13)
Ilettype(lettype list) in(p 13)
(p 1) ::= (p 0) I (p 1).(id)
(hp 1) ::= (hp 0)
(p 2) ::= (p 1) I (p 2)(p 1) I error(p 1)
I seq( (seq list» I seq begin(seq list) end
(hp 2) ::= (hp 1) I (p 2) (hp 1) I error(hp 1)
John C. Reynolds 227
The nonterminal symbols (Pn) and (seq list n ) are indexed by nonnegative integers
and infinity, with 00 ± 1 = 00 and 0 -1 = O. It is assumed that syntactic sugar (excepting
definitional forms) has been eliminated as in Section 7, and that (type), (alt type),
(lettype list), and (letrec list) are defined as in Appendix B.
(program) ::= (Po)
(unary op) ::= + 1- I -
(binary op) ::= II ** I x 1/17 I rem I + I - I = I *' I ,,; I < I ~ I > I A I v I ~ I = I;
(let list) ::= (id) == (Poo) I (id) : (type) == (Po)
I (id) == (Poo), (let list) I (id) : (type) == (Po), (let list)
(where list) ::= (id) == (Po) I (id) == (Po), (where list)
(seqlist n ) ::= (Pn-1) I (Pn-1), (seqlist n )
(Pn) ::= (id) I (nat const) I (real const) I (char const) I (string const)
I if(po) then(Pn) else(Pn)
I while(po) do(po) Iloop(po)
I '\(id): (alt type). (Pn-1) I (Pn),,\(id): (alt type). (Pn-1)
I ree : (type). (Po)
I let (let list) in(Pn) Iletinline(let list) in(Pn)
Iletrec(letrec list) where(where list) in(p")
Ilettype(lettype list) in(p")
I (Pn).(id)
I (Pn+1)(PO)
I seq «seq list n»
I (unaryop)(po) I (Po)(binaryop)(po)
I (id) == (Pn) I (Pn), (id) == (p,,)
(Po) ::= '\(id). (Po) I (Po),'\(id). (Po) I error(po)
When the typechecker examines a phrase occurrence described by the non terminal
(p,,), it is given a goal describing a set of potential simple types of the phrase that are
relevant to the typing of the enclosing program. When n = 0 this set is finite; when
n = 00 it is the set of all simple types. Very roughly speaking, when n is nonzero and
finite, it describes a set of procedural types whose first n arguments are arbitrary. The
final production displayed above shows that certain phrases, especially abstractions
without type information, are only permitted in contexts where the goal describes a
finite set.
To make this sketchy description more precise, we first define a simple type to be a
type with no occurrence of & except on the left of one or more arrows. More formally,
W ::= P I 0 ~ wit: w (simple types)
To within equivalence, every type is an intersection of simple types. To express
this fact, we define the function s, which maps types into finite sets of simple types,
as follows:
s P = {p}
s (e ~ Of) = { 0 - w I w E S Of }
sit: 0) = {1: W I w E sO}
sns= 0
S(e1 & 02) = SOl us 02 ,
and we define the function &, which maps finite sets of types into types, by
John C. Reynolds 229
&0 =08
&{O} = 0
&{01, ... , On, On+d = 01 & (&{02, ... , On+l}) when n ~ 1.
(Strictly speaking, this definition only makes sense if one imposes some ordering on
the types O}, ... , 011 +1. But this ordering can be arbitrary, since & is commutative with
respect to the equivalence of types.)
It is easy to see, by induction on the structure of simple types, that s W = {w} for
any w. Moreover, it can be shown that, for any type 0 and any set (T of simple types,
&(sO) ",0 and s(&u)=u.
It can also be shown that
050' if and only if (Vw' E s 0')(3w E s 0) wsw' ,
and that wsw' if and only if one of the following conditions holds:
1. There are primitive types p and p' such that
w = p and w' = p' and p Sprim p' .
2. There are types 01 and 0i and simple types W2 and W2 such that
w = 01 - W2 and w' = 0i - w2 and 0i 5 01 and W2 5 w2 .
3. There are an identifier L and simple types WI and wI. such that
w = L:Wl and w' = L:wi and WI 5 wi.
These properties lead directly to an algorithm for computing the predicate 0 5 0'.
As remarked earlier, a goalis an entity denoting a set of simple types. The Simplest
kind of goal is a type 0, which denotes the set so. But we also need goals that denote
certain infinite sets. Thus we define
y ::= 0 I TIt> Y I L: y (goals)
and we extend the function s to map the new goals into the sets they represent:
sT=s
s( t> y) = { 0 - w I 0 E ~ and w E s y}
s(t: y) = {L: w I w E S y} ,
where S denotes the set of all simple types and ~ denotes the set of all types.
Finally, we define the typechecking function, te, which maps a type assignment,
phrase, and goal into a type. Within equivalence,
te(1T, p, y) '" &{ w Iw E S Y and 1T I- p: w} .
Thus te( 1T, p, y) will be a greatest lower bound of the set of simple types w that belong
to the set denoted by the goal y and also satisfy the typing 1T I- P : w. When y = T
there is no contextual information, corresponding to bottom-up typechecking. At the
other extreme, top-down checking is also encompassed: The typing 1T I- P : 0 is valid
if and only if te(1T,p, 0) 5 O. (In fact, this subtype relation will hold if and only if
the equivalence te(1T, p, 0) '" 0 holds, since the opposite sUbtyping 0 5 te( 1T, p, 0) will
always hold.)
Now we can give a precise deScription of the indexing in the abstract grammar at
the beginning of this appendix: The nonterminal (p",,) describes those occurrences of
phrases that will be typechecked with a goal containing T, while the nonterminal (Pn)
describes those occurrences that will be typechecked with a goal that does not contain
T, but contains n occurrences of t>.
230 Chapter B. Design of the Programming Language FORSITHE
It is important to realize that, even though some goals are also types, goals playa
different role than types, and are therefore a different kind of entity. Specifically, the
equivalence relation on types is inappropriate for goals, since the function tc does not
map equivalent goals into equivalent types. For instance, int "" int & real, but (for any
type assignment 1T),
tC(1T,O.5,int) = ns and tC(1T, O.5,int & real) = real,
which are not equivalent types.
The solution to this problem is to adopt a different equivalence relation ~ for
goals:
y ~ y' iff
('ltw E S y)(3w' E S y') 00 "" 00' and ('ltw' E S y')(3w E S y) 00 "" 00' .
For this relation, one can show that, if y ~ y' then tC(1T,p, y) "" tC(1T, p, y').
Under certain circumstances, the typechecker can require time that is exponential
in the length of its input. This can happen because a single call tc( 1T, p, y) can cause
more than one recursive call for the same subpbrase of p under any of the following
circumstances:
1. P is a Iettype declaration containing an alternative type construction with sev-
eral alternatives,
2. p is an expliCitly typed abstraction containing an alternative type construction
with several alternatives,
3. p is an implicitly typed abstraction and y is an intersection of several procedu-
ral types.
One can expect the programmer to be aware of what is happening in the first two
cases, since the multiple alternatives would occur explicitly in the program. The last
case, however, can be more insideous and subtle. For instance, consider the call
tc( 1T, let C == newintvar 0 Ax. Bin· .. , comm) .
But in fact, the typechecker will take advantage of the equivalence ~ for goals to
replace the goal
intvar - comm & intvar - compl & intvar - int &
intvar - real & intvar - bool & intvar - char
by the equivalent goal
intvar - (comm & compl & int & real & bool & char) ,
which leads to the single call
tc([ 1T I x:intvarl,B,comm&compl&int & real & bool & char) .
Although a full discussion of the subject is beyond the scope of this report, this
example illustrates how a careful choice of canonical forms for types and goals can
enhance the efficiency of typechecking. Among the programs in this report, the only
implicitly typed abstractions whose goals necessitate checking their body more than
once are:
1. In Section 10, the binding of fin in the final definition of newintvarres,
2. In Section 11, the bindings of b in the initial definition of newintvararray and
the definition of newtrivararray,
3. In Section 11, the bindings of Xin the definitions of slice and slicecheck.
Further experience will be needed, however, before we can be confident that our
typechecker is reasonably efficient in practice. As an illustration of how close we
are to the edge of disaster, notice that we have avoided the temptation of giving the
declarator newintvar 0 the type
(intvar - comm) - comm & (intvar - compl) - compl &
(int - int) - int & (int - real) - real &
(int - bool) - bool & (int - char) - char,
which makes explicit the fact that local integer variables cannot be assigned within
expressions. With this choice of type, the typechecking call
tC(1T, newintvar 0 AX. B, T)
would lead to two calls for B:
fC([ 1T I x: intvar 1, B, comm & compl)
fC([ 1T I x:int ],B, int & real & boot & char) ,
so that typechecking would become exponential in the number of nested variable dec-
larations.
Despite this cautionary example, we hope that our typechecker, perhaps with fur-
ther refinements, will be reasonably efficient in normal practice. It should be noted,
however, that worst-case inefficiency is inevitable. In fact, it can be shown that any
typechecker for FORSYTHE (or any other language using intersection types) is PSPACE-
hard.
The proof is obtained by reducing the problem of evaluating quantified Boolean
expressions, which is known to be PSPACE-complete [29], to the type inference prob-
lem. The reduction is obtained by translating a quantified Boolean expression B into a
FORSYTHE phrase B* as follows:
232 Chapter 8. Design of the Programming Language FORSYTIfE
To obtain completely explicit typing, one can annotate the abstractions here as follows:
x,y: int Ins
p,q: tlf
h:f-flt-flf-tlt-t.
This makes it clear that our lower bound applies even to the typechecking of programs
with completely explicit type information.
Chapter 9
Assignments for Applicative Languages
Vipin Swarup, Uday S. Reddy, and Evan Ireland
We propose a theoretical framework for adding assignments and dy-
namic data to functional languages without violating their semantic prop-
erties. Our approach differs from semi-functional languages like SCHEME
and ML in that values of expressions remain static and side-effect-free.
A new form of abstraction called observer is designed to encapsulate
state-oriented computation from the remaining purely applicative com-
putation. The type system ensures that observers are combined linearly,
allowing an implementation in terms of a global store. The utility of
this extension is in manipulating shared dynamic data embedded in data
structures. Evaluation of well-typed programs is Church-Rosser. Thus,
programs produce the same results whether an eager or lazy evaluation
order is used (assuming termination).
Contents
1 Introduction 235
2 Imperative Lambda Calculus 238
3 D..C as a Programming Language 242
4 Discussion of D..C 247
5 D..C Revisited 248
6 Semantics of ILC 251
7 Extended Example: Unification 258
8 Related Work 260
9 Conclusion 262
Appendix: Possible-World Semantics 263
References 268
1 Introduction
Functional languages are popular among computer scientists because of their
strong support of modularity. They possess two powerful glues, higher-order
functions and laziness, that permit programs to be modularized in new, use-
ful ways. Hughes [Hug90] convincingly argues that "... lazy evaluation is too
important to be relegated to second-class Citizenship. It is perhaps the most
powerful glue functional programmers possess. One should not obstruct ac-
cess to such a vital tool." However, side-effects are incompatible with laziness:
programming with them requires knowledge of global context, defeating the
very modularity that lazy evaluation is designed to enhance.
Pure functional languages have nice properties that make them easy to
reason about. For instance, + is commutative, = is reflexive, and most other
familiar mathematical properties hold of the computational operators. This
This chapter is a revision of a paper in R. J. M. Hughes, editor, Functional Programming and Com-
puter Architecture, volume 523 of Lecture Notes in Computer SCience, pages 193-214. Springer-
Verlag, Berlin, 1991.
236 Chapter 9. Assignments for Applicative Languages
that take into account this type structure as well as some subtle issues in
modelling variable allocation.
2.1 Types
Let f3 represent the primitive types of ILC These may include the natural num-
bers, characters, strings etc. The syntax of ILC types is as follows:
T •• - f3 I TI X T2 I TI - T2 (Applicative types)
o .. - T I Ref 0 I 01 X O2 I 01 - 02 (Mutable types)
W o lObs T I WI X W2 I WI - W2 (Imperative types)
The type system is stratified into three layers. The applicative layer T contains
the types of the simply typed lambda calculus (extended with pairs). These
applicative types include the primitive types f3 and are closed under product
and function space constructions. Note that we use the term "applicative" to
refer to the classical values manipulated in lambda calculus; semantically, all
three layers of ILC are applicative.
The mutable layer 0 extends the applicative layer with objects called refer-
ences. References are typed values that refer (Le., point) to values of a partic-
ular type. (Ref 0) denotes the type of references that refer to values of type O.
References are used to construct a mutable world (called a store) that is used
for imperative programming. The world itself is mutable and goes through
states. The mutable layer includes all applicative types and is closed under the
type constructors x, - and Ref. Note that references can point to other refer-
ences, thereby permitting linked data structures. Tuples of references denote
mutable records, while reference-returning functions denote mutable arrays.
Finally, the world of the mutable layer needs to be manipulated. In ILC,
we take the pOSition that the only manipulation needed for states is obser-
vation, (Le., inspection). Consider the fact that in the typed lambda calculus,
environments are implicitly extended and observed, but are never expliCitly
manipulated. Similarly, in ILC, states are implicitly extended and observed (via
the use of references), but are never explicitly manipulated. Thus, in a sense,
the world exists only to be observed. A state differs from an environment in
that it may be mutated while being observed; the mutation is restricted to the
observation and is not visible to expressions outside the observation.
Observation of the state is accommodated in the observer layer w. This
layer includes all applicative and mutable types. In addition, it includes a new
type constructor denoted "Obs". A value of type Obs T is called an observer.
Such a value observes a state, possibly modifying it in the process, and returns
Vipin Swarup, Uday S. Reddy, and Evan Ireland 239
2.2 Terms
The abstract syntax for "preterms" (before type checking) is as follows:
e .. = k Ix I v IlI.xw.e I el(e2) I (eloe2) I e.ll e.21 rec(e)
I run e I return e
Ilettef vO := e in e'
I get xO ¢= e in e'
I el := e2 ; e'
where k stands for constants, x and v for variables, and e for terms.
The terms of ILC use two countable sets of variables: conventional vari-
ables, denoted by x and y, and reference variables, denoted by v and w. (We
use the letter z to denote either kind of variable.) Conventional variables are
the usual variables of the typed lambda calculus. Reference variables are a
new set of variables that have all the properties of conventional variables,
and, further the property that distinct reference variables within a term al-
ways denote distinct references. This property permits us to reason about the
equality of references by treating reference variables as essentially constants.
New references are always introduced by binding them to reference variables
via the lettef construct. Conventional variables of Ref types can also denote
references, but there is no requirement that distinct variables denote distinct
references. In the formal presentation, we assume separate name spaces for
conventional variables and reference variables. In the examples, however, we
make no distinction because the context of a variable determines whether it is
a conventional variable or a reference variable.
Figure 1 presents the context sensitive type syntax of lLC terms. The syn-
tax is expressed as inference rules for judgments of the form (f I- e: 00), where
e is a preterm, 00 is a type, and f is a finite set of typing assumptions of the
forms (z: 00), unique for each variable z. If a judgment (f I- e: 00) has a type
240 Chapter 9. Assignments for Applicative Languages
Weakening
rf-e:w
r,r' f-e:w
--intro --elim
r,x:w f- e: 00' r f- el : 00 - 00' r f- e2 : 00
r f- (Ax w • e) : 00 _ 00'
x-intro x-elim
r f- el : WI r f- e2 : 002 r f- e: WI x 002
fori = 1,2
r f- e.i: 00/
Obs-intro Obs-elim
rf-t:ObST
rf-U:T
r f- return(u) : Obs T
r f- run(t) : T
(if r has only T types)
Creation
r, v: Ref 0 f- e: 0 r, v: Ref 0 f- t : Obs T
r f- (letref viJ := e in t) : Obs T
Dereference
r f- e : Ref 0 r,x:o f- t: ObST
r f- (get xiJ =e in t) : Obs T
Assignment
r f- el : Ref 0 r f- e2 : 0 r f- t : Obs T
r f- (el := e2 ; t) : Obs T
of its input state. It is guaranteed to return the same value in every initial
state, including the empty state. All its state-manipulation is local and it can
be viewed from the outside as just an applicative value. The construct
run(t)
runs the observer t starting from an empty initial state to obtain its returned
value (rule Obs-Elim). The Weakening rule allows one to discharge typing as-
sumptions for variables that do not occur free in t so that the rule Obs-Elim
becomes applicable.
We have seen that there are no reference constants in the language. All
references have to be explicitly allocated and bound to a reference variable.
This is done by the letref construct:
letref vO := e in t
Such a term is an observer of the same type as t (rule Creation). When used
to observe a state, it extends the state by creating a new reference, extends
the environment by binding v to the reference, initializes the reference to the
value of e in the extended environment, and finally observes the value of t in
the extended environment and state. The term e can refer to the newly created
reference, allowing "circular" data structures.
The mutable world of references may be inspected by dereferencing a ref-
erence, i.e., by inspecting the value that the reference points to, or, using al-
ternate terminology, by inspecting the reference's content. If e is a reference-
valued expression of type Ref (}, then a term of the form
get xO <= e in t
binds x to the content of e, and denotes the value of t in the extended envi-
ronment. Here, t must be an observer of type Obs T, and the entire term is
again an observer of type Obs T (rule Dereference).
Finally, the content of a reference may be modified via assignment ob-
servers of the form
el := e2; t
where el is of type Ref (} and e2 is of type (}, for some (} (rule Assignment).
When used to observe a state, an assignment observer modifies the reference
el to refer to e2, and observes the value of t in the modified state. Note that
"el := e2" is not a term by itself as in ALGOL-like languages. The state is
modified for the observer t, and the entire construct is again an observer.
The lifetime of a mutable world (i.e., a collection of references) is limited to
its observation. So, the creation of v and the modification of el are observable
only within the bodies t of the creation and assignment observers respectively,
and there are no side effects produced by the observers. If there are no free
occurrences of reference variables or other state-dependent variables in an ob-
server term, then the term is a trivial observer that is independent of any state.
Such an observer can be coerced to an applicative term (rule Obs-elim). Con-
versely, every applicative term (every term of a T type) is trivially an observer
(rule Obs-intro).
It is important to note that all the primitive constructions on observers
(get, letref and assignment) involve exactly one subterm of an observer type.
242 Chapter 9. Assignments for Applicative Languages
p:= x * y;
get z ¢= n in
n:= z -1; c
3.2 Examples
For our examples, we assume that ILC is enhanced with user-defined type con-
structors and record types (drawn from standard ML [MIH90]). We also as-
sume that ILC is enhanced with explicit parametric polymorphism with types
ranging over the universe of applicative (T) types. Implicit polymorphism is
problematic in the presence of references and assignments [Tof88]-explicit
polymorphism does not suffer from these problems. In our examples, we erase
explicit type quantification and type application, and leave it to the reader to
fill in the missing information.
We also assume primitives such as case, let, letrec and if for all types.
244 Chapter 9. Assignments for Applicative Languages
Example 1: Factorial
This trivial example is meant to provide an initial feel for the language, and
illustrate how imperative observers can be embedded in applicative expres-
sions. (Note that equivalent programs can be written within the applicative
style.)
factorial: int - int
Am: into run(
letref n := min
letref acc:= 1 in
letrec loop: Obs int =
if (nl :5 1) thenreturn(accl)
*
else acc:= nl accl;
n:= nl-1;
loop
in loop)
The observer term computing the result of factorial has no free references
or state-dependent variables, and so has the applicative type into This means
that factorial can be freely embedded in applicative expressions even though
it contains imperative subcomputations.
Example 2: Points
We implement a point object that hides its internal state and exports opera-
tions. Let Point be the type of objects that represent movable planar points.
Point = {x_coord: (Real- Obs T) - Obs T,
y_coord: (Real- Obs T) - Obs T,
move: (Real x Real) - Obs T - Obs T,
equal: Point - (Bool - Obs T) - Obs T}
The function mkpoint implements objects of type Point.
mkpoint: Real x Real - (Point - Obs T) - Obs T
= A(x, y). Ak.
letref xc: Real := x in
letref yc: Real: = y in
k( {x_coord = Ak. k(xc1),
y_coord = Ak. k (yc I),
move = A(dx,dy).Ac.xc:= xcl+ dx; yc:= ycl+ dy; c,
equal = A{x_coord,y_coord, move, equal}. Ak.
x_coord Ax.
y_coord Ay.
k(x = xcI and y = ycl)
})
Note, first of all, that the mkpoint operation cannot simply yield a value of
type Point because mkpoint(x, y) should be an "action" that creates a new
point with an internal state. Neither can it yield a result of type Obs Point
because the point is not the end result of the computation; we need further
computation where the point object is used. Therefore, mkpoint is defined to
Vipin Swarup, Uday S. Reddy, and Evan Ireland 245
accept a point-observer function k and pass it the newly created pOint. This is
similar to the continuation-passing style of programming. Observers here play
the role of continuations. 2 Note that each operation in the object is similarly
defined in the continuation-passing style.
This example demonstrates that state-encapsulating closures are available
in ILC, albeit in the continuation-passing style. Such closures are also rep-
resentable in semi-functional languages like SCHEME and STANDARD ML, but
usually involve side-effects.
3.3 Monadic style
The reader might feel that the continuation-passing style used in this example
is somewhat awkward. Help is available from the monad notation suggested
by Moggi [Mog9I] and Wadler [Wad92].
Postulate a type constructor Cmd 00 with the interpretation:
Cmdoo = 'VT.(oo - Obs T) - Obs T
where the type quantification is over applicative types. The idea is that
Cmd 00 denotes commands with oo-typed results and such commands have
a sequential-composition operator. The combinators on commands are:
result oo-Cmdoo Ax.AT.Ak.k [T] x
I> Cmdoo - (00 - Cmdoo') - Cmdoo'
A(c,f}.AT.Ak.c [T] Ax.f x [T] k
do CmdT-ObsT Ac.c [T] Ax.return(x)
ref (J - Cmd Ref (J Ax.AT.Ak.letref v B := x in k [T] v
deref Ref (J - Cmd (J Ar.AT. Ak.get yB <= rink [T] y
assign Ref (J - (J - Cmd unit Ar.Ax.AT.Ak.r:= x; k [T] 0
The result and I> combinators form the unit and the "Kleisli composition"
operators for the Cmd monad. The do operator converts a command to an
observer and the rest of the primitives are the counterparts of letref, get and
:= for commands.
Using these primitives, the point object of Example 2 can be rewritten as in
Figure 2. Note that there are no "continuation" parameters to mkpoint or the
point operations. They have been absorbed into the Cmd type constructor.
A significant obstacle to carrying out this plan in an ordinary programming
language is that it seems to require full polymorphism whereas most program-
ming languages provide only restricted polymorphism of the Hindley-Milner
type system. An alternative is to devise a programming language where com-
mands rather than observers are taken to be the primitive notion. After the
present work was published, Odersky et al. [ORH93, C094, Rab96] have car-
ried out this plan and showed that the entire theory of ILC can be restated in
the framework of commands. (Their work also formulates an untyped calculus
called Avar.) We refer the interested reader to their work. For the purposes of
2Technically speaking, observers are not continuations because they return values. But, they
can be thought of as continuations in the imperative sublanguage so that the "answers· produced
can then be consumed in the applicative sublanguage.
246 Chapter 9. Assignments for Applicative Languages
this paper, we feel that the observer notation provides a certain clarity in the
theory.
3.4 Expressiveness
From a practical point of view, the language ILC has an important limitation in
expressiveness: values of imperative types are not storable in references. This
precludes the building of dynamic data structures containing "objects" such
as the point objects of Example 2.
This restriction is deliberately imposed on ILC for theoretical purposes. If
imperative values were storable, it becomes possible to define nonterminating
computations (without using explicit recursion). For example, the term
letrefv: Obsint := (vI) in vt
creates a reference v with an initial value (an observer) that reads v self-
referentially. Any attempt to execute this observer will go on forever. In
fact, a recursion combinator can be defined using this form of self-reference;
cf. [RC85).
While the theoretical tools for analyzing such self-reference are available,
in terms of reflexive domains [GS90), we believe that they would take the focus
away from the type structure of ILe. Thus, for theoretical purposes, we keep
Vipin Swarup, Uday S. Reddy, and Evan Ireland 247
Applicative types still form a separate layer; see, for instance, [Rab96] where
this approach is taken.
4 Discussion of ILC
The motivation behind ILC's type system is threefold. First, we wish to exclude
imperative terms that "export" their local effects. Consider the unchecked
preterm (lettef v := 0 in v). TIlis term, if well-typed, would export the locally
created reference v outside its scope resulting in a dangling pointer. Closures
that capture references are prohibited for the same reason-they export state
information beyond the local scope. The type system prohibits such terms by
requiring the value returned by an observer to be applicative and hence free
of state information. (Recall that observer types are of the form Obs T where
T is an applicative type).
Second, we wish to ensure that the imperative sublanguage can be im-
plemented efficiently without causing side-effects. Consider the unchecked
preterm
v := 0; (v:= 2 ; get x <= v in x) + (get x <= v in x»)
In a language with a global store and global assignments (e.g., ML or SCHEME),
the value of the term depends on the order of evaluation of +'s arguments.
Further, the term has the side-effect of changing the value of the global refer-
ence v to 2. On the other hand, if assignments are interpreted to have local
effects, then the value of the term would be 2 regardless of the order of evalu-
ation, and the term would not have any side-effects. However, the state can no
longer be implemented by a (global) store. The state needs to be copied and
passed to each argument of +, making the language quite inefficient.
The type system of ILC excludes such terms from the language by requiring
that all state manipulations be performed in a sequential fashion. Well-typed
terms of ILC do not require the state to be copied, and hence the state can
be implemented by a (global) store. The only legal way to express the above
example in ILC, is to sequentialize its assignments. For example,
v := 0; get x <= v in (v := 2 ; get y <= v in (y + x»
is a well-typed term.
ILC distinguishes between state-dependent observers and applicative val-
ues. Both observers and values can be passed to functions and returned as
results-it is not necessary to evaluate an observer to a value before passing
it. In fact, an observer of the form (get x <= e in t) is in a kind of head normal
248 Chapter 9. Assignments for Applicative Languages
5 ILC Revisited
As noted in Section 2.1, the naive type system for ILC fails to satisfy the substi-
tution property. The essential problem is that we can have terms of applicative
types that have free variables of non-applicative types, a simple example being
z: int x Obsint r- z.l: int
Notice that the non-applicative information of the free variable z (its second
component) is never used. In fact, our intuition suggests that, in an applicative
term, only applicative information of its free variables can be used, even if
those free variables are of non-applicative types. The intuition must eventually
be supported by a semantic model that has this property. But, the first task is
to devise a type system that incorporates this principle to solve the problem
with substitution.
A problem similar to ours was encountered by Reynolds [Rey78] in devising
rules for a system of Syntactic Control of Interference. It was solved roughly
ten years later by Reynolds [Rey89] using conjunctive types and by O'Hearn et
al. [OP1T95] using a zoned type system. Both of the solutions are based on a
principle similar to the above. We devise a new type system for ILC using the
framework of [OP1T95] and call it "ILC Revisited" (ILCR).
Vipin Swarup, Uday S. Reddy, and Evan Ireland 249
A second problem with the naive type system is that it is overly restrictive.
Consider the function:
while: Cmd BooI- Cmd unit - Cmd unit
while b c = b [> AX.
if X then c [> A_. (while b c)
else result 0
One might think of using this function to define the factorial function of Ex-
ample 1 (and many others like it). But, this is not permitted by the naive type
system. For factorial to be of type int - int, we must use the Obs-elim rule for
the result of factorial. But, while, being of an imperative type, cannot occur
free in the result expression. So, Obs-elim is not applicable.
Our intuition suggests that, even though while is of an imperative type,
it is essentially state-independent. It merely runs the argument commands
some number of times to make a new command but, other than this, it does
not have any state effects of its own. This is witnessed by the fact that it does
not have any free variables of imperative types. This suggests that we need a
"promotion" rule similar to Obs-elim for all types, not only Obs-types.
Our solution is to add a new type constructor App 00 with the interpreta-
tion that values of this type are built with free variables of applicative types
only. Since such values are state-independent, they may be regarded as ap-
plicative values.
The ILCR type system consists of a single universe of types, given by
00 ::= P I Ref w lObs 00 I WI X 002 I WI - 002 lApp 00
A subclass of these types are designated as applicative types and another sub-
class as mutable types:
T ::= PI Tl X T2 I WI - T2 lApp 00 (Applicative types)
() ::= P I Ref () I ()1 X ()2 I WI - ()2 (Mutable types)
Note that a function type is regarded as applicative if its result type is applica-
tive, irrespective of the input type. This corresponds to the intuition that an
applicatively typed computation can only use applicative information from its
inputs. A similar consideration applies to functions with mutable result types.
The typing judgments of ILCR take the form II I r f- e: 00 where the typing
context is split into two zones using "I". The zone denoted by II is called the
applicative zone and that denoted by r is called the unrestricted zone. Both
of the zones can contain any kind of types. But, the variables in the applica-
tive zone must be used only in applicative-typed subterms of e ("applicatively
used" in e). For example, the judgment
z:int x Refint I f- return(z.l) : Obsint (1)
satisfies this criterion because z is used in the subterm z.l of type into The
termrun(return(z.l») will thus be allowed, even though z is not of an applica-
tive type. On the other hand, the judgment
z: int x Refint I f- get x <= z.2 in return(x) : Obs int (2)
250 Chapter 9. Assignments for Applicative Languages
Co-promotion Co-dereliction
II I z: 00, f f- e : T II, z: 00 Iff- e : 00'
II, z: 00 Iff- e : T II I z: 00, f f- e : 00'
Promotion Dereliction
III f-e:w IIlff-e:Appw
III f-e:Appw IIlff-e:w
Obs-elim
II Iff- t :App (ObST)
II Iff- run(t) : T
The consequents can be derived by renaming all the applicative free variables
of t, applying the semi-linear substitution rules (with f1 and f2 empty) and,
finally, enough contraction steps to identify all the renamed variables.
6 Semantics of ILC
We present the operational and denotational semantics of ILC, and sketch the
proofs of several important properties including soundness, strong normal-
ization and confluence.
252 Chapter 9. Assignments for Applicative Languages
Let us focus on the sublanguage of ILC without recursion and the reduction
rules (1-9). This sublanguage is strongly normalizing, i.e., the evaluation of a
well-typed recursion-free term always terminates. Conceptually, its signifi-
cance is that all terms are "meaningful"; there are no undefined terms [Pra70).
Its pragmatic implication is that nontermination is limited to explicit recur-
sion. In particular, no self-application is possible. As mentioned in Sec-
tion 3.4, strong normalization is ensured in ILC by making imperative values
non-storable.
Theorem 5 (Strong Normalization) Let (II I r I- t: 00) be a recursion-free term.
Then there is no infinite reduction sequence t - t1 - t2 - ... of ILC terms.
The proof of this theorem is quite elaborate and uses twin induction on types
and terms, along the classical lines of [Tai67, GLT89). The proof may be found
in [Swa92) for the naive type system of Section 2. It can be adapted to the ILCR
type system.
The Church-Rosser property for the reduction system may be established
as follows. Let V be a countably infinite set of reference variables. Treat the
reduction rules (5) and (8) as schematic rules representing an infinite set of
rules, one for each v E V. Similarly, the rules (6) and (9) may be treated as
being schematic for an infinite set of rules, one for each distinct pair v, w E V.
The resulting reduction system has no "critical overlaps", i.e., no left hand side
has a common instance with a subterm of another left hand side, unless the
subterm is a metavariable. So, it follows that the reduction system (1-9) is
locally confluent. By Newman's Lemma, we have
Theorem 6 (Confluence) If (II I r I- r: 00) is a term, and ifr ...!.. Sl and r ...!.. S2,
then there is a term (II I r I- t: 00) such that Sl ...!.. t and S2 ...!.. t.
The result can be extended to the language with recursion. The reduction
system (1-10) still has no critical overlaps. Further, it is left-linear, i.e., there
are no repeated occurrences of meta-variables on any left hand side. Hence,
by Huet [Hue80}, Lemma 3.3, we have
Theorem 7 (Confluence) The reduction system (1-10) is confluent.
This property, which is equivalent to the Church-Rosser property, gives ev-
idence of the side-effect-freedom of ILC. If there were side effects, then the
evaluation of a subexpression would affect the meaning of its context, and the
normal forms would be dependent on the evaluation order.
The independence of results from evaluation order means, in particu-
lar, that call-by-value and call-by-name evaluations produce the same results
(modulo termination). This observation must be interpreted carefully. In
the lambda-calculus setting, the distinction between call-by-value and call-
by-name refers to when the arguments to a function are evaluated. We are
using these terms in the same sense. However, in the imperative program-
ming framework, the terms call-by-value and call-by-name are used to make
a different distinction-the question of when the arguments to a function are
observed. In the terminology of ILC, this involves a coercion from a type Obs T
to a type T. Such a coercion involves change of semantics. ILC permits no such
coercion. Thus, in the imperative-programming sense, parameter passing in
ILC is call-by-name.
254 Chapter 9. Assignments for AppJicative Languages
For modelling lLC, we need to equip cpo's with two projections. Define
an imperative domain to be a triple D = (D, £XD,I1D) where D is a cpo and
(XD, I1D: D - D are projections such that £XD !;;; I1D. (Note that we use the same
letter ambiguously for the imperative domain as well as its underlying cpo.)
We call (XD and I1D the applicative projection and mutable projection of D, re-
spectively. The idea is that, for any xED, £xv(x) identifies the "applicative
information" of x and I1D(X) identifies the "mutable information" (information
concerned only with building mutable store). A projection-reflecting {unction
between imperative domains must reflect both the projections. Lemma 8 car-
ries over to imperative domains by treating the two projections "in parallel."
An imperative domain D is called a mutable domain if its mutable projec-
tion is the identity, i.e., I1D = idD. It is called an applicative domain if £XD = idD
(and, hence, I1D = idD).5
The types of lLC are interpreted as imperative domains. For every type 00
we associate an imperative domain (D w , £Xw , I1w).
For each primitive type f3 (which is assumed to be applicative), choose a
flat cpo D{3 and associate identity projections: £x{3 = 11{3 = idD~.
For every type 00, choose a countable set Locw whose elements are thought
of as "locations." For convenience, assume that any two such sets Locw
and Locw' are disjoint (whenever 00 '* 00'). The set of all locations is
Loc = Uw Locw . The cpo DRefw is the flat cpo (Locw).l with associated projec-
tions (XRef w = T and I1Ref w = id. So, a location has only mutable information,
and no applicative information.
State is the set of partial mappings a from Loc to Uw I1w(Dw) with the
constraint that, whenever £x E Locw , a«X) E I1w(D w ). State is made into a cpo
by associating the partial order
a!;;; a' = dom(a) = dom(a') and 'Ill E dom(a).a(l)!;;; a'(l)
5Both of them form Cartesian closed subcategories of the category of imperative domains.
256 Chapter 9. Assignments for Applicative Languages
Note that all the variables in II get applicative bindings in T1. The imperative
domain of such environments for (II I n, ordered pointwise, is denoted DllIf.
The projections OCnlf and J.lnlf are defined pointwise.
The meaning of a term, denoted [II I r I- e: 00], is a projection-reflecting
function from Dnlf to Dw. Such meanings are defined in Figure 5 by induc-
tion on the type derivation of a term. More precisely, for every derivation 'I'
of a term (II I r I- e: 00) there is a meaning ['1'] of type Dnlf - Dw. How-
ever, we will see shortly that the meaning of the term is independent of the
derivation '1'. The semantics is parameterized by a function Const that in-
terprets each constant k: T as an element of DT , and an allocation function
allocw:83(Loc) - Locw such that allocw(X) f/:. X for all X ~ Loc. Intuitively,
one expects the meaning of every program to be independent of the allocation
function. This fact will have to be demonstrated eventually.
The semantics of the typed lambda-calculus part of ILC is traditional. The
meanings of run and return are appropriate coercions. The meaning of letref
is defined to choose a new location using the alloce function. The continuous
function Str (for "strictify") used in the meaning of get and assignment is
defined as follows:
Str:D-E-E
Lemma 9 (Type soundness) For every derivation 'I' of a term (II I r I- e: 00),
the meaning ['1'] is a projection-reflecting {unction of type Dnlf - Dw.
The proof is carried out by induction on the structure of '1'. The tedious ver-
ification can be eased by using categorical notation and the follOwing facts.
The meaning of the typed lambda-calculus part of the language follows the
CCC structure. If A is an applicative domain, any continuous function A - D
is projection-reflecting. Also, any continuous function to DObs T is projection-
reflecting.
Theorem 10 (Coherence) If 'I' and '1" are any two derivations of a term
(II I r I- e:oo), then ['1'] = ['1"].
The proof is similar to that in [OP1T95j. The details depend on the fact that
the interpretations of all the structural rules are natural. The essential content
of the proof is the fact that Co-promotion and Co-dereliction on the one hand,
and Promotion and Dereliction on the other, are inverses of each other.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 257
Categorical remarks Unlike the model of O'Hearn et al. [OP1T95), our model
is not bireflective. Instead, we have separate reflector and co-reflector func-
tors to the subcategory of applicative domains. The reflector L is given by
L(D,O<D,/JD) = (O<D(D),id,id) and L(f:D - E} = O<E 0 (f t O<D(D»). The co-
reflector R is given by R (D, O<D, /JD) = (D, id, id) and R (f: D - E) = f. The for-
mer is used for modelling the applicative zones and the latter for modelling
App types. This modelling of structural rules is the same as in the model of
[Red96), where the reflector picks out the passive subspace of an object space
(a projective sub domain).
The naive denotational semantics described above is not entirely satisfac-
tory, as noted in [HMTS3). The meaning of letref yO := e in t in environment 11
must be an observer that allocates a "new location" for y and continues with t.
Our semantics ensures that the "new location" is new in that it is not defined
in the initial state 0-. However, the so-called new location could already be in
use in 11 or even in 0- (as part of the value of some other variable or location).
So, it is hardly new. The follOwing example illustrates the problem concretely.
Let 11 denote the environment {z ~ Io}. What is the value of
[letref y := 1 in z:= 0; get y = y in return(y)] 11 ?
One would expect that it returns 1 irrespective of the initial state. But, consider
its action on the empty state 0-0. Since dom(o-o) = 0, aIIoco(dom(o-o») can
very well be 10• If it is 10 then, according to the semantics, the observer returns
0, not I! The new reference interferes with the locations already in use.
This does not mean that the naive denotational semantics is "wrong." The
meanings of complete programs (with no free variables) are expected to give
correct results. However, since reference variables do not necessarily denote
unique locations, the practical value of the semantics is limited. In particular,
the reduction rules (6) and (9) are not equivalences in the semantic model. 6
In the Appendix, we refine the naive denotational semantics using the pio-
neering ideas of Reynolds and Oles [ReySl, OleS2).
8 Related Work
In this section, we compare our research with related work. We organize this
comparison based on the broad approach taken by the related work.
Linearity
Substantial research has been devoted to determining when values of pure
functional languages can be modified destructively rather than by copying.
Guzman and Hudak [GH90J propose a typed extension of functional lan-
guages called single-threaded lambda calculus that can express the sequenc-
ing constraints required for the in-place update of array-like data struc-
tures. Wadler [Wad90bJ proposes a similar solution using types motivated
by Girard's Linear Logic and, later, shows the two approaches to be equiva-
lent [Wad91J.
These approaches differ radically from ours in that they do not treat refer-
ences as values. Programming is still done in the functional style (that is, using
our T types). Shared updates cannot be expressed, and pointers (references to
references) and objects (mutable data structures with function components)
are absent. Although it is possible to represent references as indices into an ar-
ray called the store, the result is a low-level "FORTRAN-style" of programming,
and it is not apparent how references to distinct types can be accommodated.
Continuation-based effects
Our approach to incorporating state changes is closely related to (and in-
spired by) continuation-based input/output methods used in functional lan-
Vipin Swarup, Uday S. Reddy, and Evan Ireland 261
guages [HJW92 , Kar81, Per90, MH]. The early proposal of HAsKELL incor-
porated continuation-based I/O as a primitive mechanism, but HASKELL ver-
sion 1.0 defines it in terms of stream-based I/O [HJW92, HS88]. Our Obs types
are a generalization of the HASKELL type Dialog. In ILC, Dialog can be defined
as Obs unit where unit is a one-element type.
ALGoL-like languages
In a series of papers [Rey81, Rey82], Reynolds describes a language framework
called IDEALIZED ALGOL which is later developed into the programming lan-
guage FORSYTHE [Rey88]. The operational semantics of FORSYTHE is defined in
two layers: the reduction semantics of the typed lambda calculus, and a state
transition semantics. The former expands procedure calls to (potentially infi-
nite) normal forms, while the latter executes the commands that occur in the
normal forms. FORSYTHE is based on the principle that the lambda-calculus
layer is independent of the state-transition layer. In particular, references to
functions are not permitted because assignments to such references would
affect ~-expansion. In contrast, our operational semantics involves a single
unified reduction system that includes both ~-expansion and command exe-
cution. The FORSYTHE restrictions meant for keeping the two apart are relaxed
in ILe.
Notwithstanding these differences, it must be noted that the manner in
which functions and state effects are combined in ILC is the same as in ALGOL-
like languages. There is a particular type constructor Obs in which all state
effects are contained (like the comm type in ALGOL-like languages), and state
effects do not interfere with functional computation. Our work borrows much
from the tradition of ALGOL-like languages. In particular, the ILCR type system
is directly based on the SCIR type system of [OPTT95] and the possible-world
semantics is based on the insights of [Rey81, Ole82, Ten90, OT92].
Equational axiomatizations
In a recent paper, Hoare et al. [HHJ+87] present an equational calculus for
a simple imperative language without procedures. The equations can be
oriented as reduction rules and used to normalize recursion-free command
phrases. Our work is inspired, in part, by this equational calculus.
Felleisen et al. [FF89, FH92] give reduction calculi for untyped SCHEME-
like languages with side effects. Our reduction system bears some degree of
similarity to these calculi. Note, however, that our reduction rules have no
variables standing for "contexts." This improvement owes in part to careful
language design. Mason and Talcott [MT91, MT92] give equational calculi ax-
iomatizing observational equivalence. We have not investigated this issue, but
note that many of their axioms (restated in our setting) hold in the possible-
world semantics of the Appendix.
Effect systems
An effect system in the sense of Gifford and Lucassen [GL86] is a type system
that describes the state effects that expressions can have. When expressions
do not have effects or have limited kinds of effects, reasoning principles and
262 Chapter 9. Assignments for Applicative Languages
transformations become applicable. Our goals are different from theirs in that
we would like to retain the functional reasoning principles and program trans-
formations even when effects are present, not merely when they are absent.
Following the original publication of this work, Odersky et al. [ORH93] defined
an untyped calculus called "-var based on essentially the same principles. A
useful improvement of theirs is the use of value-returning commands rather
than observers for expressing state effects. This leads to a programming style
similar to that in Section 3.3. Peyton Jones and Launchbury [PL95] defined a
type system for a similar language using what appear to be "possible-world"
notions. They call commands "state threads." Our type Cmd w is represented
in their setting by a type ST X w, where the additional parameter X represents
the world that the command manipulates. In contrast to the ILC type system,
their system does not make a distinction between imperative and applicative
computations. Instead, parametric polymorphism is used to ensure that state
effects of promoted terms are purely local. Rabin [Rab96] combines the use of
parametric polymorphism with an imperative-applicative distinction to devise
a type system for "-var.
9 Conclusion
We have presented a formal basis for adding mutable references and assign-
ments to applicative languages without violating the principles of "referential
transparency". This is achieved through a rich type system that distinguishes
between state-dependent and state-independent expressions and sequential-
izes modifications to the state. The language possesses the desired properties
of applicative languages such as strong normalization and confluence. At the
same time, it allows the efficient encoding of state-oriented algorithms and
linked data structures.
We envisage that this work forms the beginning of a systematic and dis-
ciplined integration of functional and imperative programming paradigms.
Their differing strengths are orthogonal, not conflicting.
Further work remains to be done regarding the approach presented here.
The issues of type inference and polymorphism over mutable and imperative
types must be investigated; cf. [HR96J. Equational calculi must be found for
supporting formal reasoning. Interference control and effect systems must
be investigated for making formal reasoning clean and practical. Finally, the
issues of efficient implementation need to be addressed; cf. [SK96J.
identifies the information that can be defined using applicative computations (without
accessing any locations). For example, the observer return(O) is defined using onlyap-
plicative computations (without accessing any locations). However, it cannot be used
in applicative computations. (In fact, no observer can be used in applicative compu-
tations, as per the syntax of the language.) We must require, however, that the two
notions of applicative are compatible. Thus, for all functors used in interpreting ILC,
the condition
DeF[X] !; F(zerox) (3)
must be satisfied, i.e., any information that may be used in applicative computations
must necessarily be applicative information. 7
An imperative functor (F, DeF,JlF) is called an "applicative functor" if F(X) =
F(zerox)(F(X» and DeF = JlF = idF. Applicative functors are world-independent in
the sense that F(X) :! F(0) for all worlds X.
Lemma 11 The category of imperative functors with projection-reflecting natural
transformations is Cartesian closed. There is a fixed point operator fixF: (F '* F) ....:.. F
for each imperative functor F.
This result is adapted from [Ole82]. The products are given by
(F, DeF, JlF) x (G, DeG, JlG) = (F x G, DeF X DeG, JlF x JlG) .
The "function space" functor F '* G may be informally described as
(F '* G)(X) = V'Y - x. [F(Y) - G(Y)]pr
resembling a form of "bounded quantification" [CW86]. An element cp E (F '* G)(X)
gives a function F(Y) - G(Y) for all worlds Y such that there is a morphism
j:X - Y. More formally, (F '* G)(X) is a family of projection-reflecting func-
tions cp = {cp[j]:F(Y) - G(Y)}j:x-y, monotone in j, such that, for every morphism
k: Y - Y', the following square commutes:
cp[j]
F(Y) ---'--':....-...... G(Y)
F(k) 1 lG(k)
(4)
ME x. {'.L'(1,), if IE dom(i)
otherwise
with projections OCRef w [X] = T and PRef w [X] = id.
For specifying states, we define a functor STATE: World - Cpo. For each world X,
STATE(X) is the cpo of type·respecting maps from X to Uw Pw [X](Fw (X»). If i:X -X'
is a world morphism, STATE (i): STATE (X) - STATE(X') is a continuous function given
by
S (.)( )'1 X' {Fw (i) (O'(i R (1)), if IE range(i) n Locw
TATE' 0'. E - .L, if I rt. range(i)
To see the action of the STATE functor on morphisms, consider worlds Xo
{lo:RefRefint} and Xl = {lo:RefRefint, It: Ref int}, with i:XI - Xo being the re-
striction. Then STATE(i) maps the Xl-state {Io - It, It - 2} to the Xo-state {lo - .L}.
For observer types Obs 00, the interpretation is stated informally as:
FObsw(X) = 'v'~Y - x. [STATE(Y) - ocw[X](Fw(Y»]
More formally, FObs w (X) is a family of continuous functions
{cp[j]:STATE(Y) - ocw[Y](Fw(Y»)}j:x-y
such that, for all morphisms k: Y - Y' ,
Fw(k) 0 cp[j] d cp[k 0 j] 0 STATE(k) (6)
The reason the relation is d rather than equality is that, if k is a restriction that makes
some locations inaccessible, then the observer becomes less defined in world Y'. How-
ever, if k is a pure expansion (a total function), then the relation is forced to be equality.
(Use the fact that kR 0 k = idy). These fanlilies are ordered pointwise and have least
elements. The action of FObs w on morphisms is
FObsw(i):CP - {CP[j' 0 i]}j':x'-y
We associate the projections ocObs w [X] = PObs w [X] = T to make an imperative func-
tor.
The interpretation of App w is given by
FAppw(X) Fw (zerox) (Fw (X»)
FAppw(i) Fw(i) t FAppw(X)
with projections OCApp w = PApp w = id. This is a constant functor (up to isomorphism)
because Fw (zerox)(Fw (X») is isomorphic to Fw(0).
If X is a world, an X -environment 17 for (ll I f) is a map from variables in (ll I f)
to values such that
• for all (z:w) En, I1(Z) E ocw[X](Fw(X»),
['\xw.e]x T/ [j:X - Y]
= '\d E Fw (Y). [e]y (jl'/[x - d])
[ele2]x1'/
= ([edx 1'/) [idx] ([e2]x 1'/)
[return(t)]x 1'/ [j:X - Y]
= '\U.jT([t]x 1'/)
[run(t)Jx T/
= [t]xl'/(idx]u.l
[letref v O := e in t]x 1'/ [j: X - Y]
= '\u. (ey)R([t1x+9 (exl'/[v -Ix]) [j+o] (eyu[ly - rOd]»)
where Ix = alloco(X), Iy = alloco(Y),
and d = [eJx+9 (exl'/[v -Ix])
[get x O <= e in t]x 1'/ [j:X - Y]
= '\u.Str (/) ([t]y (jl'/[x - u(j/)]) [jl u)
where I = [e]x 1'/
[el := e2 ; t]x 1'/ [j:X - Y]
= t\u.Str (/) ([t]x 1'/ [j] (u[jl - jd)))
where I = [edx 1'/ and d = [e2]x 1'/
[IT ! r f- e: 00 Jx: Fnlr(X) - Fw (X) such that it commutes with the action on world-
morphisms i: X - X', as in the diagram:
[e]x
Fnlr(X) ---.::--..::.:..:--+-. Fw (X)
(7)
Fnlr(O! !Fw(i)
We also use the following notation: X+o for Xu {alloco (X)} , ex:X - X+o for the
evident inclusion map, and j+o:X+o - y+o for the evident extension of j:X - Y to
include the new location.
All the meanings in Figure 7 are parameterized by a world X. The meanings of
observer terms are, in addition, parameterized by a world morphism j:X - Y (as
required by the interpretation FObs T). The world X may be thought of as the set of
locations available when the observer is defined and, the world Y as the set of locations
when the observer is executed. All the locations used in '1 are guaranteed to be in X
and all the locations used in the state parameter u are guaranteed to be in Y. This is
the main difference between the possible-world semantics and the naive denotational
semantics. Meanings are explidtly parameterized by sets of locations currently in use.
The meaning of letref v O := e in t in world X is defined to allocate a new loca-
tion Ix that is not X. Since '1 is an X-environment, Ix could not have been used in
'1. Similarly, since u is a Y-state, the new location Iy could not have been used in u.
Secondly, the meaning of letref v O := e in t is defined in terms of the meaning of t in
the expanded world X', executed in the corresponding expanded run-time world Y'.
The environment '1 and the state u are converted to the expanded worlds using the
functorial structure of the types Frrlr and STATE. (This is the main reason for inter-
preting types as functors.) The meaning of A-abstraction similarly uses the functorial
structure to convert '1 to the expanded world Y.
The reader might wonder why the meanings of letref and assignment involve sub-
term meanings in the definition-time world X (or its derivatives) whereas the meaning
of get involves subterm meanings in the execution-time world Y. This is merely a mat-
ter of style. We could have equivalently defined all the meanings by interpreting the
subterms in the world Y. For instance, the meta-term [th+9 (ex'1[v -Ix]) [j+o] in
the semantic equation for letref is equivalent to
[t]y+9 (ey 0 j)'1[v - Iy]) [idy +9]
using the equations (8), (9) and (11). We prefer to state things in terms of world X to
the extent possible because it shows the lack of dependence on the world Y. But, in
the case of get, the environment has dependence on the world Y for the binding of x.
Theorem 12 (Type soundness and coherence) The meaning of a term (II Iff- e: w)
is a unique natural transformation of type Frrlr -:... Fro, independent of the type deriva-
tion of the term.
The type soundness proof is similar to that of Lemma 9. One must also verify the
naturality conditions. The coherence proof is similar to Theorem 10. We can also
show that the semantics is independent of the allocation function.
Theorem 13 (Independence of allocation function) If alloc and alloc' are two alloca-
tion (unctions and [ ] and [ ]' are the corresponding semantic (unctions then, for all
terms (II Iff- e: w ), [II Iff- e: w] = [II Iff- e: w]' .
This is proved using a strengthened inductive hypothesis: for all worlds X,X' such
that there is a bijection i: X - X',
[II Iff- e: w]x = (iR)ro 0 [II Iff- e: w ]~, 0 (i)ITIr
The hypothesis is proved by induction on the type derivation of e. The theorem follows
by taking X' = X and i = idx.
Finally, we show that the semantics is preserved by the reduction rules:
Lemma 14 (Semantics ofsubstitution) If(II I f,z:w' f- e:w) and (II Iff- t:w') are
terms
[IIlf f- eft / z] : w]x '1 = [IIlf, z: w' f- e: w]x ('1[z - [IIlf f- t: w']'1])
If (II, z: w' Iff- e: w) and (II Iff- t: w') are terms
[IIlf f- eft / z] : w]x '1 = [II, z: w' If f- e: w Jx ('1[z - a ro , ([IIlf f- t: w'J'1)])
268 Chapter 9. Assignments for Applicative Languages
The proof follows the structure of the derivation of e[t/z] as given in Theorem 3.
One must formulate the evident hypotheses for interpreting contraction and linear
substitution steps.
Theorem 15 (Soundness of reduction) If (f1 I r f- t: w) is a term and t - t' then
[IT Ir f- t': w] = [IT I r f- t: w].
The proof is by case analysis on the reduction rule applied.
Categorical remarks As for the denotational model of the previous section, there
are separate reflector and coreflector functors to the subcategory applicative func-
tors. The reflector L sends functors F to applicative functors LF given by (LF) (X) =
adX](F(X») and (LF)(i) = F(i) t (LF) (X) with identity projections. The action of
L on natural transformations is (LF)(</>:F ...:... G)[X] = ac[X] a (</>[X] t (LF)(X»).
The coreflector R sends functors F to RF given by (RF)(X) = F(zerox)(F(X») and
(RF)(i) = F(i) t (RF)(X) with identity projections. The action of R on natural trans-
formations is (RF) (</>)[X] = F(</>)[X] t (RF)(X).
References
[ABL86] R. Amadio, K. B. Bruce, and G. Longo. The finitary projection model for
second order lambda calculus and solutions to higher order domain equa-
tions. In Proceedings, Symposium on Logic in Computer Science, pages 122-
130. IEEE Computer Society, June 1986.
[BMMM95] S. Brookes, M. Main, A. Melton, and M. Mislove, editors. Mathematical Foun-
dations of Programming Semanatics: Eleventh Annual Conference, vol-
ume 1 of Electronic Notes in Theoretical Computer Science Elsevier, 1995.
[C094] K. Chen and M. Odersky. A type system for a lambda calculus with as-
signments. In M. Hagiya and I. e. Mitchell, editors, Theoretical Aspects
of Computer Software, volume 789 of Lecture Notes in Computer SCience,
pages 347-364. Springer-Verlag, 1994.
[CW86] L. Cardelli and P. Wegner. On understanding types, data abstraction, and
polymorphism. Computing Surveys, 17(4):471-522, 1986.
[FF89) M. Felleisen and D. P. Friedman. A syntactic theory of sequential state.
Theoretical Computer Science, 69(3):243-287, 1989.
[FH92) M. Felleisen and R. Hieb. The revised report on the syntactiC theories of
sequential control and state. Theoretical Computer Science, 103(2):235-
271,1992.
[FOP+95) P.I. Freyd, P. W. O'Hearn, A. J. Power, M. Takeyama, and R. D. Tennent.
Bireflectivity. In Brookes et al. [BMMM95).
[GH90) J.e. Guzman and P. Hudak. Single-threaded polymorphic lambda calculus.
In Proceedings, Fifth Annual IEEE Symposium on Logic in Computer Science,
pages 333-343. IEEE Computer Society Press, June 1990.
[GL86) D.K. Gifford and I.M. Lucassen. Integrating functional and imperative pro-
gramming. In ACM Symposium on LIsp and Functional Programming, pages
28-38,1986.
[GLT89) I.-Y. Girard, Y. Lafont, and P. Taylor. Proofs and Types. Cambridge Univer-
sity Press, 1989.
[GS90) e. A. Gunter and D. S. Scott. Semantic domains. In J. van Leeuwen, editor,
Handbook of Theoretical Computer Science, pages 633-674. North-Holland,
1990.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 269
[HB85j P. Hudak and A. Bloss. The aggregate update problem in functional pro-
gramming systems. In ACM Symposium on Principles of Programming Lan-
guages, pages 300-314, 1985.
[HHJ+87j C. A. R. Hoare, I. J. Hayes, He Jifeng, C. C. Morgan, A. W. Roscoe, J. W.
Sanders, I. H. Sorensen, J. M. Spivey, and B. A. Sufrin. Laws of programming.
Comm. ACM, 30(8):672-686, August 1987.
[HJW92j P. Hudak, S. Peyton Jones, and P. Wadler (eds). Report on the programming
language HASKELL: A non-strict purely functional language (Version 1.2).
SIGPLAN Notices, 27(5):Section R, May 1992.
[HMT83j J. Y. Halpern, A. R. Meyer, and B. A. Trahktenbrot. The semantics of local
storage, or what makes the free list free? In Tenth Annual ACM Symposium
on Principles of Programming Languages, pages 245-257. ACM, 1983.
[HR96j H. Huang and U. S. Reddy. Type reconstruction for SCI. In D. N. Turner,
editor, Functional Programming, Glasgow 1995, Electronic Workshops in
Computing. Springer-Verlag, 1996.
[HS88j P. Hudak and R. Sundaresh. On the expressiveness of purely functional I/O
systems. Technical Report YALEU/DCSjRR665, Yale University, December
1988.
[Hue80j G. Huet. Confluent reductions: Abstract properties and applications to
term rewriting systems. J. ACM, 27(4):797-821, October 1980. (Previous
version in Proc. Symp. on Foundations of Computer Science, Oct 1977.).
[Hug90j R. J. M. Hughes. Why functional programming matters. In Research Topics
in Functional Programming, UT Austin Year of Programming Series, pages
17-42. Addison-Wesley, Reading, Mass., 1990.
[Kar81) K. Karlsson. Nebula, A functional operating system. Tech. report, Chalmers
University, 1981.
[Lau91) J. Launchbury. Projection Factorisations in Partial Evaluation. Cambridge
University Press, Cambridge, 1991.
[LG88j J.M. Lucassen and D.K. Gifford. Polymorphic effect systems. In ACM Symp.
on Principles of Programming Languages, pages 47-57,1988.
[MH) L. M. McLoughlin and S. Hayes. Interlanguage working from a pure func-
tionallanguage. Functional Programming mailing list, November 1988.
[Mit90j J. C. Mitchell. Type systems for programming languages. In J. van Leeuwen,
editor, Handbook of Theoretical Computer Science, Volume B, pages 365-
458. North-Holland, Amsterdam, 1990.
[Mog91) E. Moggi. Notions of computations and monads. Information and Compu-
tation, 93:55-92, 1991.
[MT91j I. A. Mason and C. L. Talcott. Equivalence in functional languages with
effects. J. of Functional Programming, 1:287-327, 1991.
[MT92j I. A. Mason and C. L. Talcott. Inferring the equivalence of functional pro-
grams that mutate data. Theoretical Computer Science, 105(2):167-215,
1992.
[MTH90) R. Milner, M. Tofte, and R. Harper. The Definition of STANDARD ML. The MIT
Press, 1990.
[Ole82) F. J. Oles. A Category-Theoretic Approach to the Semantics of Programming
Languages. PhD thesis, Syracuse University, 1982. See Chapter 11.
270 Chapter 9. Assignments for Applicative Languages
Contents
1 The Problem 273
2 The Basic Approach 276
3 An IDustrative Language 277
4 Controlling Interference 280
5 Passive Phrases 281
6 Some Unresolved Questions 283
7 Directions for Further Work 284
Appendix: Classes as Syntactic Sugar 284
Acknowledgements 285
References 285
1 The Problem
It has long been known that a variety of anomalies can arise when a program-
ming language combines assignment with a sufficiently powerful procedure
mechanism. The simplest and best-understood case is aliasing or sharing
between variables, but there are also subtler phenomena of the kind known
vaguely as "interfering side effects."
In this paper we will show that these anomalies are instances of a general
phenomenon which we call interference. We will argue that it is vital to con-
strain a language so that interference is syntactically detectable, and we will
suggest principles for this constraint.
Between simple variables, the only form of interference is aliaSing or shar-
ing. ConSider, for example, the factorial-computing program:
procedure (act(n,f); integer n,f;
begin integer k;
k:= 0; f:= 1;
while k != n do begin k:= k + 1; f:= k x f end
end.
First appeared in Conference Record of the Fifth Annual ACM Symposium on Prindples of Pro-
gramming Languages, pages 39-46, Tucson, Arizona, January 1978. ACM, New York. © 1978
Association for Computing Machinery, reprinted by permission.
274 Chapter 10. 5yntactic Control of Interference
)
n1
I--------l~
n 1-----1
n2
nk 0
3 An IDustrative Language
To illustrate the above principles we will first introduce an ALGOL-based lan-
guage which, although it satisfies Principle I, permits uncontrolled interfer-
ence. We will then impose Principle II to make interference syntactically de-
tectable. Finally, we will explore the consequences of Principle ill.
Unlike ALGOL, the illustrative language is completely typed, so that reduc-
tion (i.e. application of the copy rule) cannot introduce syntax errors. It pro-
vides lambda expressions and fixed-point operators for all program types, and
a named Cartesian product, which is needed for the collections discussed un-
der Principle II. Procedure declarations, multiple-parameter procedures, and
classes are treated as syntactic sugar, i.e. as abbreviations which are defined
in terms of more basic linguistic constructs.
Arrays, call-by-value, jumps and labels, unions of types, references, input-
output, and critical regions are not considered.
We distinguish between data types, which are the types of values of simple
variables, and program types, which are the types which can be declared for
identifiers and specified for parameters. The only data types are integer, real,
and Boolean, as in ALGOL, but there are an infinite number of program types.
Specifically, the set of program types is the smallest set such that:
278 Chapter 10. Syntactic Control of Interference
(Tl) if 8 is a data type, then 8 var (meaning variable) and 8 exp (meaning
expression) are program types.
(T2) sta (meaning statement) is a program type.
(T3) If wand w' are program types, then w ~ w' is a program type.
(T4) If w is a function from a finite set of identifiers into program types, then
II( w) is a program type.
A formal parameter specified to have type 8 var can be used on either side
of assignment statements, while a formal parameter specified to have type
8 exp can only be used as an expression. The program type w ~ w' describes
procedures whose single parameter has type w and whose call has type w'.
For example, the ALGOL procedures
procedure pl(n); integer n; n := 3;
real procedure p2 (x); real x; p2 := x x x;
would have types integer var ~ sta and real exp ~ real exp, respectively.
The program type II(w) is a Cartesian product in which components are
indexed by identifiers rather than by consecutive integers. Specifically, II(w)
describes collections in which each i in the domain of w indexes a compo-
nent of type w(i). The function w will always be written as a list of pairs of
the form argument: value. Thus, for example, IIUnc: sta, val: integer exp) de-
scribes collections in which inc indexes a statement and val indexes an integer
expression. A typical phrase of this type might be (inc: n := n + 1; val: n x n).
To simplify the description of syntax we will ignore aspects of concrete
representation such as parenthesization, and we will adopt the fiction that
each identifier has a fixed program type (except when used as a component
index), when in fact the program type of an identifier will be specified in the
format I : w when the identifier is bound.
We write <w id> and <w> to denote the sets of identifiers and phrases
with program type w. Then the syntax of the illustrative language is given by
the following production schemata, in which D ranges over all data types, w,
w', WI. • .. , Wn range over program types, and it, ... , in range over identifiers:
<8 exp> .. - <8 var>
<integer exp> ::= 0 I <integer exp> + <integer exp>
<Boolean exp> ::= true I <integer exp> = <integer exp>
I <Boolean exp> & <Boolean exp>
(and similarly for other constants and operations on data types)
<sta> ::= <D var> := <D exp>
<sta> ::= noaction I <sta> ; <sta>
I while <Boolean exp> do <sta>
<sta> ::= new <D var id> in <sta>
<w> ::= <w id>
<w ~ w'> ::= A<w id>. <w'>
John C. Reynolds 279
new I : [~:ger
Boolean
1 in S
begin [::ger
Boolean
1 I; Send.
let I = Q in P == (,u.P)(Q)
let rec I = Q inP == (,u.P)(y(,u.Q»).
(However, unlike Landin, we are using call-by-name.) We will omit type speci-
fications from let and let rec expressions when the type of I is apparent from
Q.
As shown in the Appendix, classes (in a slightly more limited sense than in
SIMULA) can also be defined as abbreviations.
As an example, the declaration of the procedure fact shown at the begin-
ning of this paper, along with a statement S in the scope of this declaration,
would be written as:
let fact = '\(n:integer exp,f:integer var).
new k: integer in
(k:= 0; f:= 1; while k f. n do (k:= k + 1; f:= k x f»)
inS.
After eliminating abbreviations, this becomes
(.\fact: integer exp - (integer var - sta). S)
(.\n: integer exp.,\f: integer var.
new k: integer in
(k := 0; f := 1; while k f. n do (k := k + 1; f:= k x f»)) .
4 Controlling Interference
The illustrative language already satisfies Principle I. If we can constrain it
to satisfy Principle n as well, then P # Q will hold when P and Q have no
free identifiers in common. By assuming the most pessimistic definition of #
compatible with this result (and postponing the consequences of Principle ill
until the next section), we get
P # Q iff F(P) n F(Q) = {},
where F(P) denotes the set of identifiers which occur free in P.
To establish Principle n, we must consider each way of binding an identi-
fier. A new variable declaration causes no problems, since new variables are
guaranteed to be independent of all previously declared entities. But a lambda
expression can cause trouble, since its formal parameter will interfere with its
global identifiers if it is ever applied to an actual parameter which interferes
with the global identifiers, or equivalently, with the procedure itself. To avoid
this interference, we will restrict the call peA) of a procedure by imposing the
requirement P # A.
The following informal argument shows why this restriction works. Con-
sider a beta-reduction (,u.P)(Q) = PII-Q. Within P there may be a pair of
identifiers which are syntactically required to satisfy the #-relationship, and
therefore must be distinct. If so, it is essential that the substitution I - Q pre-
serve the #-relationship. No problem occurs if neither identifier is the formal
parameter I. On the other hand, if one identifier is I, then the other distinct
identifier must be global. Thus the #-relation will be preserved if K # Q holds
John C. Reynolds 281
for all global identifiers K, i.e. for all identifiers occurring free in M.P. This is
equivalent to (i\I.P) # Q.
More formally, one can show that, with the restriction on procedure calls:
<w'> ::= <w-w'>«w» when<w-w'>#<w>,
syntactic correctness is preserved by beta reduction (and also by reduction of
collection expressions), and continues to be preserved when other productions
restricted by # are added, e.g.
<sta> ::= <stal> II <sta2> when <stal> # <sta2> .
The restriction P # A on P(A) also affects the language constructs which
are defined as abbreviations. For let I = Q in P == (i\I.P)(Q), and for
let ree I = Q in P == (i\I. P) (Y(i\I. Q», we see that, except for I, no free iden-
tifier of Q can occur free in P. Thus, although one can declare a procedure or
a collection of procedures which use global identifiers (the free identifiers of
Q), these globals are masked from occurring in the scope P of the declaration,
where they would interfere with the identifier I.
For multi-parameter procedures, P (AI. ... ,An) == P (AI) ... (An) implies
the restrictions P # AI. P(Ad # A2, ... , P(Ad··· (An-I) # An, which are
equivalent to requiring P # At for each parameter and At # A j for each pair of
distinct parameters.
For example, consider the following procedure for a "repeat" statement:
let repeat = i\(s: sta, b: Boolean exp). (s; while -.b do s) in ...
In any useful call repeat(AI,A2), the statement Al will interfere with the
Boolean expression A2. Although this is permitted in the unconstrained il-
lustrative language, as in ALGOL, it is prohibited by the restriction Al # A2.
Instead, one must group the interfering parameters into a collection:
let repeat = i\x:II(s: sta, b:Boolean exp). (x.s; while -.x.b do x.s) in ...
and use calls of the form repeat ( (s: AI, b: A2) ).
This example is characteristic of Principle II. Although interfering param-
eters are permitted, they require a somewhat cumbersome notation. In com-
pensation, it is immediately clear to the reader of a procedure body when
interference between parameters is possible.
5 Passive Phrases
In making interference syntactically detectable, we have been unnecessarily
restrictive. For example, we have forbidden parallel constructs such as
x:= n II y:= n
or
let twice = i\s: sta. (s ; s) in (twice(x := x + 1) \I twice(y := y x 2») .
Moreover, the right side of the reduction rule Y(f) => f(Y(f» violates the re-
quirement f # Y(f), giving a clear sign that there is a problem with recursion.
282 Chapter 10. Syntactic Control of Interference
In the first two cases, we have failed to take into account that the expres-
sion n and the procedure twice are passive: they do no assignment (to global
variables in the case of procedures), and therefore do not interfere with them-
selves. Similarly, when f is passive, f # Y(f) holds, and the reduction rule for
Y(f) becomes valid. This legitimizes the recursive definition of procedures
which do not assign to global variables.
(Recursive procedures which assign to global variables are a more difficult
problem. Within the body of such a procedure, the global variables and the
procedure itself are interfering entities, and must therefore be represented
by components of a collection named by a single identifier. This situation
probably doesn't pose any fundamental difficulties, but we have not pursued
it.)
The following treatment of passivity is more tentative than the previous
development. Expressions in our language are always passive, since they never
cause assignment to free variables. Procedures may be active or passive, inde-
pendently of their argument and result types. Thus we must distinguish the
program type W - p Wi describing passive procedures from the program type
w - Wi describing (possibly) active procedures.
More formally, we augment the definition of program types with
(TS) If wand Wi are program types, then w -p Wi is a program type.
and we define passive program types to be the smallest set of program types
such that
(PI) 8 exp is passive.
(P2) w - p Wi is passive.
(P3) If w(i) is passive for all i in the domain of w, then I1(w) is passive.
Next, for any phrase r, we define A(r) to be the set of identifiers which
have at least one free occurrence in r which is outside of any subphrase of pas-
sive type. Note that, since identifier occurrences are themselves subphrases,
A(r) never contains identifiers of passive type, and since r is a subphrase of
itself, A(r) is empty when r has passive type.
Then we relax the definition of P # Q to permit P and Q to contain free
occurrences of the same identifier, providing every such occurrence is within
a passive subphrase. We define:
P#Q == A(P) nF(Q) = {} & F(P) nA(Q) = {}.
Finally, we modify the abstract syntax. We define a passive procedure to
be one in which no global identifier has an active occurrence:
<w -p Wi> ::= ,\<w id>. <Wi> when A( <Wi» - {<w id>} = {}.
Passive procedures can occur in any context which permits active procedures
<w - Wi> ::= <w -p w'> ,
but only passive procedures can be operands of the fixed-point operator:
<w> ::= Y«w-p w».
John C. Reynolds 283
where 0 and 0' are passive types and DC and DC' are nonpassive types.
The most serious problem with our treatment of passivity is our inability to
retain the basic property that beta-reduction preserves syntactic correctness.
Consider, for example, the reduction
(i\p: mixed. (x := p.a II y := p. a») «a: n + I, b: n := 0»
=> x := (a: n + I, b: n := 0). a II y := (a: n + I, b: n := 0). a
=> x:= n + 1 II y := n + 1
where "mixed" stands for the program type II(a: integer exp, b: sta). Although
the first and last lines are perfectly reasonable, the intermediate line is rather
dubious, since it contains assignments to the same variable n within two state-
ments to be executed in parallel. Nevertheless, our definition of # still permits
the intermediate line, on the grounds that assignments within passive phrases
cannot be executed.
However, if we accept
x:= (a:n + l,b:n:= O).a # y:= (a:n + l,b:n:= O).a,
then it is hard to deny
(i\s: sta.x := (a: n+l, b: (n := 0 II s». a) # y:= (a: n+l, b: n := O).a .
But this permits the reduction
(,\s: sta.x := (a: n + I, b: (n := 0 II s».a) (y := (a: n + I, b: n := 0). a)
=> x:= (a: n + I, b: (n := 0 II y := (a: n + I, b: n := 0). a) ). a
=> x:= n + 1
284 Chapter 10. Syntactic Control of lnterference
Here the intermediate step, in which the underlined statement is clearly illegal,
is prohibited by our syntax.
This kind of problem is compounded by the possibility of collection-
returning procedures. For instance, in the above examples, one might have
silly(n + 1, n := 0), where silly has type integer exp - (sta - mixed), in place
of the collection (a: n + 1, b: n := 0).
A possible though unaesthetic solution to these problems might be to per-
mit illegal phrases in contexts where passivity guarantees nonexecution. A
more hopeful possibility would be to alter the definition of substitution to
avoid the creation of illegal phrases in such contexts.
Within the scope S. one may declare X to be a new element of class C by writing
the statement
newelement X: C in S' . (2)
Then within the statement S' one may write X.h to denote the component Pk of the
class element X.
To express these notations in terms of procedures. suppose Pl •...• Pn have types
WI •..•• Wn. respectively. Then we define (1) to be an abbreviation for:
Aclrnowledgements
Most of this research was done during a delightful and stimulating sabbatical at the
University of Edinburgh. Special thanks are due to Rod Burstall and Robin Milner for
their encouragement and helpful suggestions. and to the members of IFIP working
group 2.3. especially Tony Hoare. for establishing the viewpoint about programming
which underlies this work.
References
[1] Wirth. N. The programming language PASCAL. Acta Informatica 1 (1971). pp. 35-
63.
[2] van Wijngaarden. A. (ed.). Mailloux. B. J.. Peck. J. E. L.. and Koster. C. H. A.
Report
on the Algorithmic Language ALGOL 68. MR 101. Mathematisch Centrum. Amster-
dam. February 1969.
[3] Hoare. C. A. R. Towards a theory of parallel programming. In Operating Systems
Techniques (eds. C. A. R. Hoare and R. N. Perrott). Academic Press. New York.
1972. pp. 61-71.
286 Chapter 10. Syntactic Control of Interference