You are on page 1of 284

Progress in Theoretical Computer Science

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

Library of Congress Cataloging-in-Publication Data

ALGOL-like languages I Peter W . O'Hearn, Robert D . Tennent, editors.


p. cm . -- (Progress in theoretical computer science)
Includes bibliographical references and index.
ISBN-13: 978-1-4612-8661-5

I . ALGOL (Computer program lang uage) I. O'Hearn, Peter W . (Peter


William), 1963- II. Tennent, R. D. , 1944- Ill. Series.
QA76.73.A24A413 1997
OOS.13'J--dc2 1 96-46972
CIP

Primed on acid-free paper

e 1997 Birkhll.user Boston Birkhiiuser ltIJV


mo
Softcover reprint of the hardcover 1st edition 1997

Copyright is not claimed for works of U.s. Govemmenl employees.


All rights reserved. No pan of this publication may be reproduced, stored in a retrieval system,
or transmitted, in any Conn or by any means, electronic, mechanical, photocopying, recording,
or otherwise, without prior pennission of the copyright owner.

Pennission to photocopy for internal or personal use of specifi c clicnts is granted by


Birkhll.user Boston for libraries and other users registered wi th the Copyright Clearance
Center (CCC), provided that the base feeof$6.00 percopy, plus $0.20 per page is paid direclly
to CCC, 222 Rosewood Drive, Danvers, MA 01923, U.S.A. Special requests should be
addressed directly to Birkhiiuscr Boslon, 675 Massachusetts Avenue, Cambridge, MA 02139,
U.S.A.
Cover design by R. D. Tennent and Typesmith, with apologies to Guy Steele, designer of
the cover for The Little Lisper, by Daniel Friedman, Science Research Associates (1974).

ISBN- I3: 978-1-4612-8661-5 e-ISBN-13: 978-1-4612-4118-8


DOl: lO.IOO7m8-1-4612-4118-8
Typeset by the Authors in lEX.

987 6 5 4 3 2 I
Table of Contents
Contributors vii

Introduction 1

Part I mSTORICAL BACKGROUND 17


1 Revised Report on the Algorithmic Language ALGOL 60 19
Peter Naur (editor), J. w. Backus, F. L. Bauer, ]. Green, C. Katz,
]. McCarthy, A. ]. Perlis, H. Rutishauser, K. Samelson, B. Vauquois,
]. H. Wegstein, A. van Wijngaarden and M. Woodger

2 The Varieties of Programming Language 51


Christopher Strachey

Part n BASIC PRINCIPLES 65


3 The Essence of ALGOL 67
John C. Reynolds

4 ALGOL and Functional Programming 89


Peter W. O'Hearn

5 Orthogonality of Assignments and Procedures in ALGOL 101


Matthias Felleisen and Stephen Weeks

6 IDEALIZED ALGOL and its Specification Logic 125


John C. Reynolds

7 Towards Fully Abstract Semantics for Local Variables 157


Albert R. Meyer and Kurt Sieber

Part m LANGUAGE DESIGN 171


8 Design of the Programming Language FORSYTHE 173
John C. Reynolds

9 Assignments for Applicative Languages 235


Vipin Swamp, Uday S. Reddy, and Evan Ireland

10 Syntactic Control of Interference 273


John C. Reynolds

Contents of Volume 2 287


Contributors
Samson Abramsky A. John Power
Department of Computer Science Laboratory for the Foundations of
University of Edinburgh Computer Science
Edinburgh, U.K. EH9 3JZ Department of Computer Science
sarnson@dcs.ed.ac.uk University of Edinburgh
Edinburgh, U.K. EH9 3JZ
Stephen Brookes
ajp@dcs.ed.ac.uk
School of Computer Science
Carnegie-Mellon University Uday S. Reddy
Pittsburgh, PA 15213-3890 Computer Science Department
Stephen.Brookes@cs.crnu.edu University of Illinois
Matthias Felleisen Urbana, IL 61801
Department of Computer Science reddy@cs.uiuc.edu
Rice University John C. Reynolds
Houston, TX 77251 School of Computer Science
rnatthias@cs.rice.edu Carnegie Mellon University
Evan Ireland Pittsburgh, PA 15213
School of Information Science John.Reynolds@cs.crnu.edu
Massey University
Palmerston North, New Zealand Kurt Sieber
E.lreland@rnassey.ac.nz Informatik
Universitiit des Saarlandes
Guy McCusker Saarbriicken, Germany 66041
Department of Computing sieber@cs.uni-sb.de
Imperial College
London, U.K. SW7 2BZ Vipin Swamp
garn@doc.ic.ac.uk The MITRE Corporation
Burlington Road
Albert R. Meyer Bedford, MA 01730
Laboratory for Computer Science swarup@linus.rnitre.org
MIT
Cambridge, MA 02139 Makoto Takeyama
rneyer@theory.lcs.rnit.edu Department of Computing Science
Peter W. O'Hearn Chalmers University of Technology and
Department of Computer Science Goteborg University
Queen Mary and Westfield College Goteborg, Sweden S 41296
London, U.K. El 4NS rnakoto@cs.chalrners.se
ohearn@dcs.qrnw.ac.uk Robert D. Tennent
Frank J. Oles Department of Computing and
Mathematical Sciences Department Information Science
IBM T. J. Watson Research Center Queen's University
Yorktown Heights, NY 10598 Kingston, Canada K7L 3N6
oles@watson.ibrn.com rdt@qucis.queensu.ca
Andrew M. Pitts Stephen Weeks
Computer Laboratory School of Computer Science
University of Cambridge Carnegie Mellon University
Cambridge, u.K. CB2 3QG Pittsburgh, PA 15213
ap@cl.carn.ac.uk Stephen.Weeks@cs.crnu.edu
Introduction
In recent years there has been a remarkable convergence of interest in
programming languages based on ALGOL 60. Researchers interested in
the theory of procedural and object-oriented languages discovered that
ALGOL 60 shows how to add procedures and object classes to simple
imperative languages in a general and clean way. And, on the other
hand, researchers interested in purely functional languages discovered
that ALGOL 60 shows how to add imperative mechanisms to functional
languages in a way that does not compromise their desirable properties.
Unfortunately, many of the key works in this field have been rather
hard to obtain. The primary purpose of this collection is to make the
most significant material on ALGoL-like languages conveniently available
to graduate students and researchers.

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.

PART I HISTORICAL BACKGROUND

In 1959, John Backus presented a paper [Bac59] on "the proposed international


algebraic language" (which soon after evolved into the language that became
known as ALGOL 60 [NB+60)). Backus begins by giving informal descriptions
of the syntax and semantics of the language, and outlines reasons why more
precise descriptions are needed. But he then makes the following admission.
The author had hoped to complete a formal description of the set of
legal programs and of their meanings in time to present it here. Only the
description of legal programs has been completed however. Therefore
the formal treatment of the semantics of legal programs will be included
in a subsequent paper.
2 Introduction

Backus goes on to introduce the meta-notation we now know as Backus-Naur


formalism and uses it to specify the syntax of the language; however, the
"subsequent paper" on the semantics never materialized, and the semantics is
again described informally in the Revised Report on ALGOL 60 [NB+63).
Many computer scientists today are unfamiliar with this important docu-
ment, and so most of it has been included as Chapter 1 of this volume; sub-
sequent corrections and discussion of various "ambiguities" may be found in
[Knu67, DHW76).
The importance of having precise descriptions of syntax and semantics is
today generally appreciated; however, more than 35 years after Backus's talk,
researchers are still trying to produce satisfactory descriptions of ALGOL-like
languages. This situation is not unprecedented: mathematicians needed hun-
dreds of years to sort out the semantic issues underlying the differential and
integral calculi, and some thirty years passed between Alonzo Church's for-
mulation of the (untyped) lambda calculus [Chu41) and the first mathematical
models by Dana Scott [Sc072b).
Scott's work on the lambda calculus was partly motivated by a collabora-
tion with Christopher Strachey [SS71) in which they outlined a "mathematical"
approach to the semantics of procedural programming languages, using the
powerful domain-theoretic tools Scott had developed. This approach, now
termed denotational semantics, has become well known; a short introductory
paper by Strachey has been included in this volume as Chapter 2.
In [SS71), Scott and Strachey had suggested that if "you put your domains
on the table first" then this would help to reveal language-design issues and
possibilities. This point of view is illustrated well by Strachey's chapter. The
design of ALGOL 60 is compared to that of a language PAL with quite differ-
ent domain structure. The comparison takes place exclusively on the level
of domains appropriate to each language, independently of surface syntactic
similarities or dissimilarities.
One of the most significant contributions to semantic analysis of program-
ming languages by Strachey and his collaborators, such as Scott [Sc072a), Rod
Burstall [Bur70), and Peter Landin [Lan65), is the distinction between the en-
vironment and the store (or state). The domain analysis of ALGOL 60 in Sec-
tion 3.1 of Chapter 2 reveals the disjointness of the "storable" and "denotable"
values in this language; this was later described by John Reynolds (see Chap-
ter 3) as the distinction between "data types" and "phrase types."
In Strachey's paper, labels and jumps are not treated in any detail, but the
important concept of continuations, which became the "standard" approach to
these features, had already been discovered; see [SW74, Rey93].
An unsatisfactory aspect of traditional denotational semantics for typed
languages is that full advantage was not taken of syntactic typing informa-
tion; for example, in the discussion of ALGOL 60 in Chapter 2, there is a single
domain of all possible procedures, with "run-time" selection of the appropri-
ate sub-domain.
Also, notice that the treatment of assignment is based on the low-level
idea of "locations" or L-values, using global states with fixed structure. The
approach to dynamic allocation adopted by Scott and Strachey is described in
Part II. Basic Principles 3

[Sc072a); it is based on recording in every state which locations are currently


"active" (i.e., in use). This has become known as the "marked-stores" approach.
A planned work by Strachey, "An abstract model of storage," referenced
in both of these papers as being "in preparation", never appeared. (Strachey
died in 1975.) More information on traditional denotational semantics may be
found in [MS76, Sto77).
PART II BASIC PRINCIPLES

A significant change in attitudes to ALGOL 60 was initiated by the paper "The


Essence of ALGOL" by John Reynolds, reprinted here as Chapter 3. At the
time (1981), this language was generally regarded as having been superseded
by new and improved "ALGOL-like" languages, such as ALGOL 68 [vW+69)
and PASCAL [Wir71). But Reynolds argues that such languages are not truly
ALGOL 60-like, and, in many important respects, are less satisfactory than the
original. This is reminiscent of Tony Hoare's opinion [Hoa74) that ALGOL 60 is
a language so far ahead of its time that it was not only an improvement
on its predecessors but also on nearly all its successors.
Furthermore, Reynolds points out that most formal models of programming
languages then in use were failing to do justice to typed languages and to
important concepts such as representation-independent storage variables and
stack-oriented storage management. As a result, languages designed with such
models in mind would inevitably be influenced by concepts that are not truly
ALGOL-like.
In more detail, Reynolds characterized "ALGOL-like" languages as follows.
(i) The procedure mechanism is based on the fully typed, call-by-name
lambda calculus, and equational principles such as the f3 and 11 laws are
valid even in the presence of imperative features such as assignment
commands and jumps.
(ii) The language has assignments, but procedures, variables, and other de-
notable meanings are not assignable.
(iii) Apart from overflow, roundoff error, and error stops, expressions are
purely "mathematical," without non-local jumps, side effects, or ambigu-
ous coercions.
(iv) Allocation and de-allocation are based on a stack discipline, but the
treatment of storage variables is otherwise representation independent.
(v) Generic features such as conditionals, recursion, and procedures are
uniformly applicable to all types of phrases.
According to these criteria, languages such as ALGOL 68 and PASCAL and their
descendants are not ALGOL-like. (In some minor respects, such as the incom-
plete typing for parameters, even ALGOL 60 is not ALGOL-like! but these are
regarded as design mistakes.)
Syntactic and semantic descriptions tailored to this view of ALGOL-like lan-
guages differ significantly from that given by Strachey in Chapter 2. Syntactic
descriptions must deal with an infinity of phrase types, and must take into
4 Introduction

account subtypes (coercions) which are not necessarily interpreted as subsets


and which can introduce ambiguity. Typing contexts must be used to treat
non-context-free constraints; a judgement of the form P E (e, rr) as used by
Reynolds would today be written rr I- P : e. Instead of using domains of all
possible (infinitary) environments and all possible procedures, there is a do-
main of finitary environments for each assignment of types to finite sets of
identifiers, and a domain of procedures for each type of argument and result.
A representation-independent view of storage variables is obtained by
treating them as (acceptor, expression) pairs, where an acceptor can be viewed
as a function mapping a storable value to a command meaning. This allows
for parameterizing semantic domains and semantic interpretation functions
by arbitrary sets of states. It is then possible to deal with local-variable dec-
larations by "expanding" the set of states in use. This puts the stack-oriented
nature of the language "on the table" at an early stage in the semantic defini-
tion. The general semantic framework can be presented quite elegantly using
the category-theoretical notions of functors and natural transformations; see
Part IV.
Chapters 4 to 7 formalize or develop in various ways some of the key
ideas in "The Essence of ALGOL." Reynolds emphasized that the full {3 and '1
laws of the typed lambda calculus are valid in an ALGOL-like language, even
for imperative phrases such as commands and variables. "ALGOL and Func-
tional Programming" by Peter O'Hearn shows that an ALGOL-like language ac-
tually preserves all equivalences of the purely functional fragment, provided
"snap-back" operations do not exist. An example of a "snap-back" operation
would be a block expression in which a command is executed as part of the
evaluation of an expression, but, because of possible side-effects to non-local
variables, the initial state for that execution is then restored. Although this
is certainly implementable, it violates the natural irreversibility of imperative
computation.
The paper "On the Orthogonality of Assignments and Procedures in
ALGOL" by Stephen Weeks and Matthias Felleisen formalizes the orthogonality
of the functional (lambda calculus-like) and imperative fragments of an ALGOL-
like language in terms of separate reduction calculi for the two fragments. The
essential properties of these are proved independently, and it is also proved
that evaluation of a program can be factored into a functional phase, followed
by an imperative phase.
The usual basis for specification and verification of programs in simple
imperative languages is the familiar "axiomatic" system of [Hoa69), based on
(precondition, command, postcondition) triples. Unfortunately, most attempts
to apply these techniques to languages with full procedures and jumps have
been fairly unsatisfactory. The paper "IDEALIZED ALGOL and its Specification
Logic" by John Reynolds, reprinted here as Chapter 6, introduced several in-
novations:
• specifications as predicates of the environment, with universal speCifi-
cations those true in all environments;
• logical connectives (conjunction, implication and universal quantifica-
tion) at the level of specification formulas (i.e., treating Hoare triples as
Part III. Language Design 5

the atomic formulas in a first-order theory);


• "non-interference" predicates to deal with aliasing and interference via
non-local variables; and
• call-by-name as the basic parameter mechanism.
Specification logic may be viewed as a combination of Hoare's logic and Scott's
"Logic of Computable Functions" [Sc069], in the same way that IDEALIZED
ALGOL combines a simple imperative language with a PCF-like typed lambda
calculus. Examples of specification logic in use may be found in Section 3.3 of
[Rey81], where a subset of ALGOL W is used as the programming language.
One way to highlight unsatisfactory aspects of a model of local variables
is to show that it fails to be fully abstract; i.e., fails to validate operationally
justified equivalences. The paper "Towards Fully Abstract Semantics for Lo-
cal Variables" by Albert Meyer and Kurt Sieber, included here as Chapter 7,
demonstrates that traditional marked-store models fail to validate some ex-
tremely simple expected equivalences. This work also showed the weaknesses
of existing program logics, because most logics for imperative languages with
procedures were sound in marked-store models, with specification logic being
a notable exception.
"The Essence of ALGOL" was originally presented as an invited address
to a symposium in tribute to Adriaan van Wijngaarden, one of the design-
ers of both ALGOL 60 and ALGOL 68, on the occasion of his retirement. It is
reported that another of the authors of the ALGOL report commented that
Reynolds's paper should have been titled "An Essence of ALGOL." For compar-
ison, the reader may want to consult some of the other opinions that have
been expressed by various authors on what is "essential" about ALGOL 60
[vW63 , WH66, Knu67, Str67, Wic73, Hoa74, Cla79, Wex81, THM83], and how
it should be formally described [vW64, Lan64, Mos74, HJ82, THM83, AW85].

PART m LANGUAGE DESIGN

Reynolds concludes "The Essence of ALGOL" with the following remarks.


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.
Chapters 8 to 10 explore three language-design possibilities.
"Design of the Programming Language FORSYTHE" by John Reynolds de-
scribes a language based on the ideas presented in "The Essence of ALGOL." It
contains one further innovation: The use of "intersection" (conjunctive) types
[CD78] to further Simplify, unify and generalize the type structure of the lan-
guage.
"Assignments for Applicative Languages" by Uday Reddy, Vipin Swamp
and Evan Ireland, presents an approach to combining imperative and func-
tional aspects by using types to separate imperative and applicative parts of
a program. Types are used to keep the procedure mechanism independent of
side effects, which allows {3, 11, and other properties of functional languages
to be retained. This ALGoL-like use of types to constrain the scope of effects
6 Introduction

is at the root of a body of more recent work on introducing assignment into


"pure" functional languages [PW93, LP95).
We have mentioned that, despite being imperative, ALGOL-like languages
preserve all of the reasoning principles used in "pure" functional program-
ming. However, this does not mean that ALGOL-like languages are neces-
sarily simple to reason about, since this only involves the easy part: rea-
soning about parts of programs that don't involve change. In particular, it
is evident from the examples of specification logic in use in [Rey8l) that
manipulating non-interference predicates within a logic for program verifi-
cation is extremely inconvenient. The underlying problem is that interac-
tions between assignments and procedures in conventional higher-order pro-
cedurallanguages can produce undesirable phenomena such as aliasing and
covert interference via non-local variables. For this reason, many researchers
[Bac78, WA85, BW88, Hug89, Hud89) abandoned procedural languages entirely
and promoted "purely" functional languages in which there is no interference
at all. Other language designers have attempted to avoid these problems by
Significantly circumscribing the procedures in their languages; see, for exam-
ple, the discussion of EUCLID in [Mor82).
The paper "Syntactic Control of Interference" by John Reynolds, reprinted
here as Chapter 10, demonstrates that it is possible to avoid aliaSing and
covert interference while retaining ALGOL-like higher-order procedures in a
slightly restricted form. The resulting programming language has not only
the desirable functional attributes of ALGOL 60, but also many of the desirable
attributes of simple imperative languages that are typically lost when proce-
dures are added.

Introduction to Volume 2

In this volume, ALGOL-like languages are studied from various modern


denotational-semantic points of view. This material will be suitable for ad-
vanced seminars and researchers with adequate background in logic, domain
theory, type theory and category theory.
Two main strands emerge. The more developed is the explicit-state ap-
proach, where programs are modelled using functions that pass states around
as values. This approach identifies uniformity properties (based on naturality
and relational parametricity) that constrain the way that state is used, analo-
gous to how monotonicity and continuity constrain the way that higher-order
functions work in domain theory. Applications include soundness of a pro-
gram logic and a type system, structuring and design of a translation to inter-
mediate code, and discovery of new reasoning principles.
The other strand is the implicit-state approach, where a program is viewed
in terms of a history of observable actions instead of in terms of state trans-
formations. This approach is more recent; it promises to give a very different
and complementary way of specifying meanings of imperative programs, and
excellent results have been obtained, indicating its potential.
Part N. Functor-Category Semantics 7

PART IV FuNcrOR-CATEGORY SEMANTICS

The functor-category approach to the semantics of local variables outlined


in "The essence of ALGOL" is described in detail in Frank Oles's Ph.D. the-
sis. Excerpts from this work have been included here as Chapter 11; see also
[OleS5, OT92). This chapter describes how the extension of the store via local-
variable declarations can be represented using a category of store shapes and
expansions, and how a treatment of procedures consistent with this view of
local declarations can be obtained using a category of functors from store
shapes into domains.
The term "stack discipline" refers to a way of managing the allocation and
de-allocation of storage variables at runtime. Reynolds regarded the stack
discipline as so integral to ALGOL'S essence that (a mathematical abstraction
of) it deserves a prominent position in a satisfactory semantic definition. He
shows this viewpoint at work in "Using Functor Categories to Generate Inter-
mediate Code," included as Chapter 12. Phrases in an ALGoL-like language
are translated to intermediate code using the compositionally-defined valua-
tion function for interpretation in a functor category. The semantics is tuned
to compiling (instead of input-output behaviour), with store shapes replaced
by a category of concrete "stack descriptors" and primitive types interpreted
using programs in the intermediate language instead of functions on states.
While ALGOL'S stack discipline was the original focus of functor-category
semantics, the ideas have been used also to model dynamic allocation [PS93,
Sta96a) and local names in the pi-calculus [Sta96b, FMS96). In both of these
cases a name or location can survive past execution of the block in which it
was created: the shape of the store or active name-space is not preserved.
Thus, the stack discipline is not built into functor categories themselves, but
rather has to do with the specific way that Reynolds and Oles used functors in
the semantics of ALGOL. The point is that a functor-category model can make
it clear, in the semantics of types, whether and where it is possible for store
shapes to be altered.

PART V SPECIFICATION loGIC

In Chapters 6 and 10, Reynolds focused on non-interference as a means of


controlling and reasoning about interactions between procedures and imper-
ative features. But the semantics of non-interference has proved to be rather
subtle. An operational semantics of specification logic is sketched in [ReySl),
but the final section of Chapter 6 points out that this interpretation is not
completely satisfactory. In particular, it fails to validate two powerful, and
seemingly reasonable, axioms that make use of non-interference assumptions.
The paper "Semantical Analysis of Specification Logic" by Bob Tennent,
reprinted here as Chapter 13, addresses this problem by slightly adapting the
functor-category approach of Chapters 3 and 11 to allow the treatment of cer-
tain "intensional" properties of commands within a formally extensional intu-
itionistic first-order theory. It turns out that specification logic is incompatible
with the law of the excluded Iniddle, and so the use of an underlying intuition-
istic logic is not only natural, because of the underlying functor model, but in
a sense necessary.
B Introduction

While Chapter 13 solves the previously known problems in the interpre-


tation of specification logic, it also points out an additional problem with
the treatment of non-interference predicates in Chapter 6 and [ReyS1): non-
interference for procedu.ral phrases is not correctly defined by induction on
types as a "logical" predicate. The paper "Semantical Analysis of Specification
Logic, 2" by Peter O'Hearn and Bob Tennent, reprinted here as Chapter 14,
addresses this problem. It gives a type-independent interpretation of non-
interference which, for phrases of basic types, reduces to the definition given
in Chapter 13. This interpretation validates all of the axioms for reasoning
about non-interference.
These two papers also contain a semantic analysis of passivity, i.e., the
ability to read from, but not write to, the store. In Chapter 13 it is shown that
expression meanings cannot have side effects, not even "temporary" ones as
in the snap-back form of block expression; this is crucial to allow validation
of a Hoare-like axiom for assignments in an ALGoL-like language. This notion
of passivity is extended to all types in Chapter 14, again by using a uniform,
type-independent, definition.
Specification logic has evolved somewhat with the development of its se-
mantics. Today we regard its essence as consisting of four parts. First, spec-
ification logic is a theory, in the negative fragment (&, =>, Y) of many-sorted
first-order intuitionistic logic with equality. The atomic formulas of the the-
ory are Hoare triples {P}C{Q} and non-interference formulas C # E (for arbi-
trary C and E). Second, the treatment of functional features is LCF-like, with
PI1-equality, formal function extensionality, and laws for fixed-point unwind-
ing (and induction). Third, for non-interference we have the decomposition
schema and axioms for passivity and chastity, as in Chapter 14, and the ax-
ioms of strong constancy and non-interference composition. And finally there
are analogues of Hoare's axioms for imperative constructs such as sequential
composition and iteration, axioms for precedent strengthening, consequent
weakening, and so on. Most distinctive is the axiom for variable declarations,
which extends the usual Hoare-logic rule by using noninterference assump-
tions between local variables and non-local identifiers.

PART VI PROCEDURES AND LocAL VARIABLES

In Chapter 7 of Volume 1, Meyer and Sieber emphasized that traditional deno-


tational models of imperative languages fail to capture the sense in which non-
local procedures are independent of locally-declared variables. An "invariant-
preserving" model described in that paper accounts for some of the difficul-
ties, and this line of work is continued by Kurt Sieber in Chapter 15. Sieber
uses the concept of "logical relations" [Pl073, StaBS) as a means to exclude un-
desirable elements. A full-abstraction result is given showing the coincidence
of semantic equality and a contextually-defined "observational" equivalence
relation, up to second-order types.
Peter O'Hearn and Bob Tennent also use logical relations to construct a
model in Chapter 16. They base their construction on the functor-category
model of Oles, adding to it relational conditions that govern interactions with
local variables. Although their semantic technology is similar to Sieber's, the
Part VII. Interference, Irreversibility and Concurrency 9

emphasis is rather different. Where Sieber views relations as a means to "keep


a model small," O'Hearn and Tennent view relations as a rigorous approxi-
mation to an informal notion of parametricity, as support for a conceptual
connection with representation independence and data abstraction [Rey83].
The basic idea is that the information-hiding provided by local variables and
procedural abstraction is analogous to the independence of a program using
an abstract data type from the details of the implementation of the type; see
also [Ten94].
Chapter 17. by Andy Pitts. differs from other papers in this volume in that
it concentrates on operational rather than denotational methods. Pitts shows
how relational-parametricity principles. as in the semantics of Chapter 16 and
[OR96]. can be formulated without explicit reference to a semantic model. He
defines logical relations for IDEALIZED ALGOL based on an operational seman-
tics. and uses these to derive a completeness result characterizing observa-
tional equivalence. A main point of his work is that the separation of the
reasoning principles implicit in models based on logical relations from their
use in constructing the models. This results in a clear and mathematically
Simple presentation of the resulting principles. and pleasant technical results.

PART vn INTERFERENCE, IRREVERSIBILITY AND CONCURRENCY

In "Syntactic Control of Interference" (Chapter 10 of Volume 1) Reynolds ar-


gues that a language should be constrained to make non-interference easily
detectable. The language design given there is very successful in most re-
spects; but it is pointed out that the treatment of passivity is such that syn-
tactic correctness is not always preserved by p-reductions. This problem has
since been addressed by Reynolds in [Rey89]. A somewhat simpler solution
to the problem is presented in "Syntactic Control of Interference Revisited"
by Peter O'Hearn, John Power, Makoto Takeyama and Bob Tennent, reprinted
here as Chapter 18.
The development of the material in Chapter 18 was done partly in reaction
to a paper by Uday Reddy [Red94] which, it turns out, provided the first model
for the type rules for paSSivity. Only later it was realized that the treatment
of passivity in Chapters 13 and 14 also provides such a model. Reddy sets
out his approach in detail in "Global State Considered Unnecessary," reprinted
here as Chapter 19.
Reddy's paper is remarkable in a number of respects. First, it is an implicit-
state semantics, in that imperative programs are treated in terms of sequences
of observable actions, instead of as transformations on a set of states. (This it
has in common with ideas from the process-calculus literature [Mil89].) At
the same time the semantics is domain-theoretic, in much the same spirit
as domain models of functional languages. This allows suitable categorical
structure to be identified, resulting in a novel semantics of syntactic control
of interference.
The second remarkable aspect of Reddy's paper is that it contains the first
semantic account of the irreversibility of state change in imperative programs.
The idea at issue is expressed by Strachey in Chapter 2 of Volume 1 as follows:
10 Introduction

The machine state ... behaves in a typically "operational" way. The


state transformation produced by obeying a command is essentially ir-
reversible and it is, by the nature of the computers we use, impossible to
have more than one version of [the state] available at anyone time.

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

[BW88) R. Bird and P. Wadler. Introduction to Functional Programming. Prentice-


Hall International, London, 1988.
[CD78) M. Coppo and M. Dezani. A new type assignment for .\-terms. Archiv. Math.
Logik, 19:139-156,1978.

[Chu41) A. Church. The Calculi of Lambda Conversion. PriJiceton University Press,


Princeton, 1941.
[Cla79) E. M. Clarke. Programming language constructs for which it is impossible
to obtain good Hoare-like axiom systems. J. ACM, 26(1):129-147, 1979.
[DHW76) R. M. De Morgan, I. D. Hill, and B. A. Wichmann. A supplement to the
ALGOL 60 Revised Report. The Computer Journal, 19(3):276-288, 1976.

[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.

[Hug89) j. Hughes. Why functional programming matters. The Computer Journal,


32:98-107,1989.
[Knu67) D. E. Knuth. The remaining troublespots in ALGOL 60. Comm. ACM,
10(10):611-617, 1967.
[Lan64) P. j. Landin. A formal description of ALGOL 60. In Steel [Ste64), pages 266-
294.
[Lan65] P. j. Landin. A correspondence between ALGOL 60 and Church's lambda
notation. Comm ACM, 8(2,3):89-101 and 158-165, 1965.
[LIC96) Proceedings, 11th Annual1EEE Symposium on Logic in Computer Science,
New jersey, USA, 1996. IEEE Computer Society Press, Los Alamitos, Califor-
nia.
[LP95) j. Launchbury and S. Peyton jones. State in HASKELL. LIsp and Symbolic
Computation, 8(4):293-341, December 1995.
[Mil89) R. Milner. Communication and Concurrency. Prentice-Hall International,
1989.
Bibliography 13

[Mor82) 1. H. Morris. Real programming in functional languages. In J. Darlington,


P. Henderson, and D. A. Turner, editors, Functional Programming and its
Applications, pages 129-176. Cambridge University Press, Cambridge, Eng-
land,1982.
[Mos74) P. Mosses. The mathematical semantics of ALGOL 60. Technical monograph
PRG-12, Oxford University Computing Laboratory, Programming Research
Group, Oxford, January 1974.
[MS76) R. E. Milne and C. Strachey. A Theory of Programming Language Semantics.
Chapman and Hall, London, and Wiley, New York, 1976.
[NB+60) P. Naur (ed.), J. W. Backus, et al. Report on the algorithmic language
ALGOL 60. Comm. ACM, 3(5):299-314, 1960. Also Numerische Mathematik
2:106-136.
[NB+63) P. Naur, J. W. Backus, et al. Revised report on the algorithmic language
ALGOL 60. Comm. ACM, 6(1):1-17,1963. Also The Computer ]ournaI5:349-
67, and Numerische Mathematik 4:420-53.
[Ole85] F. J. Oles. Type algebras, functor categories and block structure. In M. Nivat
and J. C. Reynolds, editors, Algebraic Methods in Semantics, pages 543-573.
Cambridge University Press, Cambridge, England, 1985.
[OR95) P. W. O'Hearn and U. S. Reddy. Objects, interference, and the Yoneda em-
bedding. In S. Brookes, M. Main, A. Melton, and M. Mislove, editors, Math-
ematical Foundations of Programming Semantics, Eleventh Annual Confer-
ence, volume 1 of Electronic Notes in Theoretical Computer Science, Tulane
University, New Orleans, Louisiana, March 29-AprilI1995. Elsevier Science
(http://www.elsevier.nl).
[OR96) P. W. O'Hearn and 1. C. Reynolds. From ALGOL to polymorphic linear
lambda-calculus. Unpublished draft, 1996.
[OT92) P. W. O'Hearn and R. D. Tennent. Semantics of local variables. In M. P. Four-
man, P. T. Johnstone, and A. M. Pitts, editors, Applications of Categories
in Computer SCience, volume 177 of London Mathematical Society Lecture
Note Series, pages 217-238. Cambridge University Press, Cambridge, Eng-
land,1992.
[Plo73) G. D. Plotkin. Lambda-definability and logical relations. Memorandum SAl-
RM-4, School of Artificial Intelligence, University of Edinburgh, October
1973.
[PS93) A. Pitts and I. Stark. Observable properties of higher order functions that
dynamically create local names, or: What's new? In A. M. Borzyszkowski
and S. Sokolowski, editors, Mathematical Foundations of Computer SCience,
volume 711 of Lecture Notes in Computer Science, pages 122-140, Gdansk,
Poland, 1993. Springer-Verlag, Berlin.
[PW93) S. Peyton-Jones and P. Wadler. Imperative functional programming. In Con-
ference Record of the Twentieth Annual ACM SIGPLAN-SIGACT Symposium
on Principles of Programming Languages, pages 71-84, Charleston, South
Carolina, 1993. ACM, New York.
[Red94) U. S. Reddy. Passivity and independence. In Proceedings, Ninth AnnuallEEE
Symposium on Logic in Computer Science, pages 342-352, Paris, France,
1994. IEEE Computer SOCiety Press, Los Alamitos, California.
14 Introduction

[ReyBl] J. C. Reynolds. The Craft of Programming. Prentice-Hall International, Lon-


don, 19B1.
[ReyB3] J. C. Reynolds. Types, abstraction and parametric polymorphism. In R. E. A.
Mason, editor, Information Processing 83, pages 513-523, Paris, France,
1983. North-Holland, Amsterdam.
[Rey89] J. C. Reynolds. Syntactic control of interference, part 2. In G. Ausiello,
M. Dezani-Ciancaglini, and S. Ronchi Della Rocca, editors, Automata, Lan-
guages and Programming, 16th International Colloquium, volume 372 of
Lecture Notes in Computer Science, pages 704-722, Stresa, Italy, July 1989.
Springer-Verlag, Berlin.
[Rey93] J. C. Reynolds. The discoveries of continuations. LIsp and Symbolic Compu-
tation, 6(3/4):233-247, 1993.
[Ros94] A. W. Roscoe, editor. A Classical Mind, Essays in Honour of c. A. R. Hoare.
Prentice-Hall International, 1994.
[Sc069] D. S. Scott. A type-theoretical alternative to CUCH, ISWIM, OWHY. Privately
circulated memo, Oxford University, October 1969. Published in Theoretical
Computer Science, 121(1/2):411-440, 1993.
[Sco72a] D. S. Scott. Mathematical concepts in programming language semantics. In
Proc. 1972 Spring Joint Computer Conference, pages 225-34. AFIPS Press,
Montvale, N.J., 1972.
[Sc072b] D. S. Scott. Models for various type-free calculi. In P. Suppes et al., edi-
tors, Logic, Methodology, and the Philosophy of Science, Iv, pages 157-187,
Bucharest, 1972. North-Holland, Amsterdam.
[SS71] D. S. Scott and C. Strachey. Toward a mathematical semantics for computer
languages. In J. Fox, editor, Proceedings of the Symposium on Computers
and Automata, volume 21 of Microwave Research Institute Symposia Series,
pages 19-46. Polytechnic Institute of Brooklyn Press, New York, 1971. Also
Technical Monograph PRG-6, Oxford University Computing Laboratory, Pro-
gramming Research Group, Oxford.
[Sta85] R. Statman. Logical relations and the typed A-calculus. Information and
Computation, 65:85-97,1985.
[Sta96a] I. Stark. Categorical models for local names. LIsp and Symbolic Computation,
9(1):77-107, feb 1996.
[Sta96b] I.A. Stark. A fully abstract domain model for the 7T-calculus. In [UC96!,
pages 36-42.
[Ste64] T. B. Steel, Jr., editor. Formal Language Description Languages for Com-
puter Programming, Proceedings of the IFIP Working Conference, Baden bei
Wien, Austria, September 1964. North-Holland, Amsterdam (1966).
[Sto77! J. E. Stoy. Denotational Semantics: The Scott-Strachey Approach to Pro-
gramming Language Theory. The MIT Press, Cambridge, Massachusetts,
and London, England, 1977.
[Str67! C. Strachey. Fundamental Concepts in Programming Languages. Unpub-
lished lecture notes, International Summer School in Computer Program-
ming, Copenhagen, August 1967.
Bibliography 15

[SW74] c. Stracheyand C. P. Wadsworth. Continuations: a mathematical semantics


for handling full jumps. Technical Monograph PRG-6, Oxford University
Computing Laboratory, Programming Research Group, Oxford, 1974.
[Ten94j R. D. Tennent. Correctness of data representations in ALGoL-like languages.
In Roscoe [Ros94j, chapter 23, pages 405-417.
[THM83j B. A. Trakhtenbrot, J. 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, volume
164 of Lecture Notes in Computer SCience, pages 474-500, Pittsburgh, PA,
1983. Springer-Verlag, Berlin, 1984.
[vW63j A. van Wijngaarden. Generalized ALGOL. In R. Goodman, editor, Annual Re-
view in Automatic Programming, volume 3, pages 17-26. Pergamon Press,
Oxford, 1963.
[vW64j A. van Wijngaarden. Recursive definition of syntax and semantics. In Steel
[Ste64j, pages 13-24.
[vW+69j A. van Wijngaarden (ed.) et al. Report on the algorithmic language
ALGOL 68. Numerische Mathematik, 14:79-218, 1969.
[WA85j W. W. Wadge and E. A. Ashcroft. LUCID, the Dataflow Programming Lan-
guage, volume 22 of APIC Studies in Data Processing. Academic Press, Lon-
don, 1985.
[Wex81j R. L. Wexelblat, editor. History ofProgramming Languages. Academic Press,
New York, 1981.
[WH66j N. Wirth and C. A. R. Hoare. A contribution to the development of ALGOL.
Comm. ACM, 9(6):413-432, June 1966.
[Wic73j B. A. Wichmann. ALGOL 60 Compilation and Assessment. Academic Press,
London, 1973.
[Wir71j N. Wirth. The programming language PASCAL. Acta Informatica, 1:35-63,
1971.
Part I

HISTORICAL BACKGROUND
Chapter 1
Revised Report on the Algorithmic Language
ALGOL 60

Peter Naur (editor), J. w. Backus, F. L. Bauer, ]. Green, C. Katz,


J. McCarthy, A. ]. Perlis, H. Rutishauser, K. Samelson, B. Vauquois,
]. H. Wegstein, A. van Wijngaarden and M. Woodger

Dedicated to the Memory of WILLIAM TURANSKI 1

The report gives a complete defining description of the international al-


gorithmic language ALGOL 60. This is a language suitable for expressing
a large class of numerical processes in a form suitably concise for di-
rect automatic translation into the language of programmed automatic
computers.
In the first chapter, a survey of the basic constituents and features
of the language is given, and the formal notation, by which the syntactic
structure is defined, is explained.
The second chapter lists all the basic symbols, and the syntactic units
known as identifiers, numbers and strings are defined. Further, some
important notions such as quantity and value are defined.
The third chapter explains the rules for fOrming expressions and the
meaning of these expressions. Three different types of expressions exist:
arithmetic, Boolean (logical) and designational.
The fourth chapter describes the operational units of the language,
known as statements. The basic statements are: assignment statements
(evaluation of a formula), go to statements (explicit break of the sequence
of execution of statements), dummy statements, and procedure state-
ments (call for execution of a closed process, defined by a procedure
declaration). The formation of more complex structures, having state-
ment character, is explained. These include: conditional statements, for
statements, compound statements, and blocks.
In the fifth chapter, the units known as declarations, serving for defin-
ing permanent properties of the units entering into a process described
in the language, are defined.
The report ends with an alphabetic index of definitions.

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

DESCRIPTION OF THE REFERENCE LANGUAGE


Was sich iiberhaupt sagen liisst, lasst sich klar sagen;
und wovon man nicht reden kann, dariiber muss man schweigen.
LUDWIG WITTGENSTEIN

1. Structure of the Language


The purpose of the algorithmic language is to describe computational pro-
cesses. The basic concept used for the description of calculating rules is the
well-known arithmetic expression containing as constituents numbers, vari-
ables, and functions. From such expressions are compounded, by applying
rules of arithmetic composition, self-contained units of the language-explicit
formulae-called assignment statements.
To show the flow of computational processes, certain non-arithmetic state-
ments and statement clauses are added which may describe, e.g., alternatives,
or iterative repetitions of computing statements. Since it is necessary for the
function of these statements that one statement may refer to another, state-
ments may be provided with labels. A sequence of statements may be en-
closed between the statement brackets begin and end to form a compound
statement.
Statements are supported by declarations which are not themselves com-
puting instructions but inform the translator of the existence and certain prop-
erties of objects appearing in statements, such as the class of numbers taken
on as values by a variable, the dimension of an array of numbers, or even
P. Naur (ed.),]. W. Backus, F. L. Bauer,]. Green, C. Katz,]' McCarthy, et al. 21

the set of rules defining a function. A sequence of declarations followed by a


sequence of statements enclosed between begin and end constitutes a block.
Every declaration appears in a block in this way and is valid only for that block.
A program is a block or compound statement which is not contained within
another statement and which makes no use of other statements not contained
within it.
In the sequel the syntax and semantics of the language will be given. 2

1.1. FORMALISM FOR SYNTACTIC DESCRIPTION


The syntax will be described with the aid of metalinguistic formulae. 3 Their
interpretation is best explained by an example:
(ab) ::= (I [ I (ab)( I (ab) (d)

Sequences of characters enclosed in the brackets ( ) represent metalinguistic


variables whose values are sequences of symbols. The marks ::= and I (the
latter with the meaning of or) are metalinguistic connectives. Any mark in a
formula, which is not a variable or a connective, denotes itself (or the class of
marks which are similar to it). Juxtaposition of marks and/or variables in a
formula Signifies juxtaposition of the sequences denoted. Thus the formula
above gives a recursive rule for the formation of values of the variable (ab).
It indicates that (ab) may have the value ( or [ or that given some legitimate
value of (ab), another may be formed by follOwing it with the character ( or
by following it with some value of the variable (d). If the values of (d) are the
decimal digits, some values of (ab) are:
[«(1(37(
(l2345(
«(
[86

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. Basic Symbols, Identifiers, Numbers, and Strings. Basic Concepts.


The reference language is built up from the following basic symbols:
{basic symbol} .. - {letter} I {digit} I {logical value} I {delimiter}

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. DIGITS. LOGICAL VALUES

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.2.2. Logical Values


{logical value} ::= true I false

The logical values have a fixed obvious meaning.

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

(unsigned number) ::= (decimal number) I (exponent part) I


(decimal number) (exponent part)
(number) ::= (unsigned number) I + (unsigned number) I - (unsigned number)

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).

2.7. QUANTITIES, KINDS AND SCOPES


The following kinds of quantities are distinguished: simple variables, ar-
rays, labels, Switches, and procedures.
The scope of a quantity is the set of statements and expressions in which
the declaration of the identifier associated with that quantity is valid. For
labels see section 4.1.3.
P. Naur (ed.),]. W. Backus, F. L. Bauer,]. Green, C. Katz,]. McCarthy, et al. 25

2.8. VALUES AND TYPES

A value is an ordered set of numbers (special case: a single number), an


ordered set of logical values (special case: a single logical value), or a label.
Certain of the syntactic units are said to possess values. These values
will in general change during the execution of the program. The values of
expressions and their constituents are defined in section 3. The value of an
array identifier is the ordered set of values of the corresponding array of sub-
scripted variables (cf. section 3.1.4.1).
The various "types" (integer, real, Boolean) basically denote properties of
values. The types associated with syntactic units refer to the values of these
units.

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.

(expression) ::= (arithmetic expression) I (Boolean expression) I


(designational expression)

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

A variable is a designation given to a single value. This value may be used


in expressions for forming other values and may be changed at will by means
of assignment statements (section 4.2). The type of the value of a particular
variable is defined in the declaration for the variable itself (cf. section 5.1. Type
Declarations) or for the corresponding array identifier (cf. section 5.2. Array
Declarations).

3.1.4. Subscripts

3.1.4.1. Subscripted variables designate values which are components of


multidimensional arrays (cf. section 5.2. Array Declarations). Each arithmetic
expression of the subscript list occupies one subscript position of the sub-
scripted variable, and is called a subscript. The complete list of subscripts is
enclosed in the subscript brackets [ J. The array component referred to by a
subscripted variable is specified by the actual numerical value of its subscripts
(cf. section 3.3. Arithmetic Expressions).

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. FUNCTION DESIGNATORS

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.2.4. Standard functions


Certain identifiers should be reserved for the standard functions of anal-
ysis, which will be expressed as procedures. It is recommended that this re-
served list should contain:
abs(E) for the modulus (absolute value) of the value of the expression E
sign(E) for the sign of the value of E (+ 1 for E>O, 0 for E =0, -1 for E <0)
sqrt(E) for the square root of the value of E
sin(E) for the sine of the value of E
cos(E) for the cosine of the value of E
arctan(E) for the principal value of the arctangent of the value of E
In(E) for the natural logarithm of the value of E
exp(E) for the exponential function of the value of E (e E )
These functions are all understood to operate indifferently on arguments both
of type real and integer. They will all yield values of type real, except for
sign(E) which will have values of type integer. In a particular representation
these functions may be available without explicit declarations (cf. section 5.
Declarations).
3.2.5. Transfer functions
It is understood that transfer functions between any pair of quantities and
expressions may be defined. Among the standard functions it is recommended
that there be one, namely
entier(E),
which "transfers" an expression of real type to one of integer type, and assigns
to it the value which is the largest integer not greater than the value of E.

3.3. ARITHMETIC EXPRESSIONS


3.3.1. Syntax
(adding operator) ::= + I -
(multiplying operator) ::= x I / I .;-
(primary) ::= (unsigned number) I (variable) I (function designator) I
«arithmetic expression»
(factor) ::= (primary) I (factor) I (primary)
(term) ::= (factor) I (term) (multiplying operator) (factor)
(simple arithmetic expression) ::= (term) I (adding operator) (term) I
(simple arithmetic expression) (adding operator) (term)
(if clause) ::= if (Boolean expression) then
(arithmetic expression) ::= (simple arithmetic expression) I
(if clause) (simple arithmetic expression) else (arithmetic expression)
28 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60

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)

Simple arithmetic expression:


U - Yu + 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. Precedence of operators

The sequence of operations within one expression is generally from left to


right, with the following additional rules.

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.

3.3.6. Arithmetics of real quantities

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. BOOLEAN ExPRESSIONS

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.5. The Operators


Relations take on the value true whenever the corresponding relation is
satisfied for the expressions involved, otherwise false.
The meaning of the logical operators -. (not), /\ (and), v (or), ::::> (implies),
and == (equivalent), is given by the following function table:
bl false false true true
b2 false true false true
-.bl true true false false
bl A b2 false false false true
bl v b2 false true true true
bl :::> b2 true true false true
bi == b2 true false false true

3.4.6. Precedence of operators


The sequence of operations within one expression is generally from left to
right, with the following additional rules.

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. DESIGNATIONAL EXPRESSIONS

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.

3.5.4. The subscript expression


The evaluation of the subscript expreSSion is analogous to that of sub-
scripted variables (cf. section 3.1.4.2). The value of a switch designator is
defined only if the subscript expression assumes one of the positive values:
1,2,3, ... , n, where n is the number of items in the switch list.

3.5.5. Unsigned integers as labels


Unsigned integers used as labels have the property that leading zeroes do
not affect their meaning; e.g., 00217 denotes the same label as 217.

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

In order to make it possible to define a specific dynamic succession, state-


ments may be provided with labels.
Since sequences of statements may be grouped together into compound
statements and blocks, the definition of statement must necessarily be recur-
sive. Also since declarations, described in section 5, enter fundamentally into
the syntactic structure, the syntactic definition of statements must suppose
declarations to be already defined.

4.1. COMPOUND STATEMENTS AND BLOCKS


4.1.1. Syntax
(unlabelled basic statement) ::= (assignment statement) I (go to statement) I
(dummy statement) I (procedure statement)
(basic statement) ::= (unlabelled basic statement) I (label):(basic statement)
(unconditional statement) ::= (basic statement) I (compound statement) I (block)
(statement) ::= (unconditional statement) I (conditional statement) I (for statement)
(compound tail) ::= (statement) end I (statement) ; (compound tail)
(block head) ::= begin (declaration) I (block head) ; (declaration)
(unlabelled compound) ::= begin (compound tail)
(unlabelled block) ::= (block head) ; (compound tail)
(compound statement) ::= (unlabelled compound) I (label): (compound statement)
(block) ::= (unlabelled block) I (label):(block)
(program) ::= (block) I (compound statement)

This syntax may be illustrated as follows. Denoting arbitrary statements, dec-


larations, and labels by the letters S, D, and L, respectively, the basic syntactic
units take the forms:
Compound statement:
L: L: ... begin S ; S ; ... S ; Send
Block:
L: L: ... begin D ; D ; ... D ; S ; S ; ... S ; Send
It should be kept in mind that each of the statements S may again be a com-
plete compound statement or block.

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:

Q: begin integer i, k; real w;


for i := 1 step 1 until m do
for k:= i + 1 step 1 until m do
begin w:= A[i,k];
A[i, k] := A[k, i];
A[k, i] := wend for i and k
end block Q

4.1.3. Semantics

Every block automatically introduces a new level of nomenclature. This is


realized as follows. Any identifier occurring within the block may through a
suitable declaration (cf. section 5. Declarations) be specified to be local to the
block in question. This means (a) that the entity represented by this identifier
inside the block has no existence outside it, and (b) that any entity represented
by this identifier outside the block is completely inaccessible inside the block.
Identifiers (except those representing labels) occurring within a block and
not being declared to this block will be nonlocal to it, i.e., will represent the
same entity inside the block and in the level immediately outside it. A label
separated by a colon from a statement, i.e., labelling that statement, behaves as
though declared in the head of the smallest embracing block, i.e., the smallest
block whose brackets begin and end enclose that statement. In this context a
procedure body must be considered as if it were enclosed by begin and end
and treated as a block.
Since a statement of a block may again itself be a block the concepts local
and nonlocal to a block must be understood recursively. Thus an identifier,
which is nonlocal to a block A, mayor may not be nonlocal to the block B in
which A is one statement.

4.2. ASSIGNMENT STATEMENTS

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.3.5. Go to an undefined switch designator


A go to statement is equivalent to a dummy statement if the designational
expression is a switch designator whose value is undefined.

4.4. DUMMY STATEMENTS

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. CONDITIONAL STATEMENTS

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.

4.5.3.1. If statement. The unconditional statement of an if statement will be


executed if the Boolean expression of the if clause is true. Otherwise, it will be
skipped and the operation will be continued with the next statement.

4.5.3.2. Conditional statement. According to the syntax, two different forms


of conditional statements are possible. These may be illustrated as follows:
if Bl then 51 else if B2 then 52 else 53 ; 54

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

Here Bl to B3 are Boolean expressions, while SI to S3 are unconditional state-


ments. S4 is the statement following the complete conditional statement.
The execution of a conditional statement may be described as follows. The
Boolean expression of the if clauses are evaluated one after the other in se-
quence from left to right until one yielding the value true is found. Then the
unconditional statement following this Boolean is executed. Unless this state-
ment defines its successor explicitly the next statement to be executed will be
S4; i.e., the statement following the complete conditional statement. Thus the
effect of the delimiter else may be described by saying that it defines the suc-
cessor of the statement it follows to be the statement following the complete
conditional statement.
The construction
else (unconditional statement)
is equivalent to
else if true then (unconditional statement)
If none of the Boolean expressions of the if clauses is true, the effect of the
whole conditional expression will be equivalent to that of a dummy statement.
For further explanation, the following picture may be useful:

if Bl then 51 else if B2 then 52 else 53 ; 54


L __ --.1L --.1
Bl false B2 false

4.5.4. Go to into a conditional statement


The effect of a go to statement leading into a conditional statement follows
directly from the above explanation of the effect of else.

4.6. FOR STATEMENTS


4.6.1. Syntax
(for list element) ::= (arithmetic expression) I (arithmetic expression) step
(arithmetic expression) until (arithmetic expression) I (arithmetic expression)
while (Boolean expression)
(for list) ::= (for list element) I (for list),(for list element)
(for clause) ::= for (variable) := (for list) do
(for statement) ::= (for clause) (statement) I (label):(for statement)

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.6.5. The value of the controlled variable upon exit


Upon exit out of the statement S (supposed to be compound) through a
go to statement, the value of the controlled variable will be the same as it was
immediately preceding the execution of the go to statement.
If the exit is due to exhaustion of the for list, on the other hand, the value
of the controlled variable is undefined after the exit.

4.6.6. Go to leading into a for statement


The effect of a go to statement, outside a for statement, which refers to a
label within the for statement, is undefined.

4.7. PROCEDURE STATEMENTS

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)

These examples correspond to examples given in section 5.4.2.

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.4. Actual-formal correspondence


The correspondence between the actual parameters of the procedure state-
ment and the formal parameters of the procedure heading is established as
follows. The actual parameter list of the procedure statement must have the
same number of entries as the formal parameter list of the procedure decla-
ration heading. The correspondence is obtained by taking the entries of these
two lists in the same order.

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.1. If a string is supplied as an actual parameter in a procedure state-


ment or function designator, whose defining procedure is an ALGOL 60 state-
ment (as opposed to non-ALGOL code, cf. section 4.7.8), then this string can
only be used within the procedure body as actual parameter in further proce-
dure calls. Ultimately, it can only be used by a procedure body expressed in
non-ALGOL code.

4.7.5.2. A formal parameter which occurs as a left part variable in an assign-


ment statement within the procedure body and which is not called by value
can only correspond to an actual parameter which is a variable (special case of
expression).
P. Naur (ed.),]. W. Backus, F. L. Bauer, J. Green, C. Katz, J. McCarthy, et al. 41

4.7.5.3. A formal parameter which is used within the procedure body as an


array identifier can only correspond to an actual parameter which is an array
identifier of an array of the same dimensions. In addition if the formal pa-
rameter is called by value, the local array created during the call will have the
same subscript bounds as the actual array.

4.7.5.4. A formal parameter which is called by value cannot in general cor-


respond to a switch identifier or a procedure identifier or a string, because
these latter do not possess values. (The exception is the procedure identi-
fier of a procedure declaration which has an empty formal parameter part
(cf. section 5.4.1) and which defines the value of a function designator (cf. sec-
tion 5.4.4). This procedure identifier is in itself a complete expression.)

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.

4.7.8. Procedure body expressed in code


The restrictions imposed on a procedure statement calling a procedure
having its body expressed in non-ALGOL code evidently can only be derived
from the characteristics of the code used and the intent of the user, and thus
falls outside the scope of the reference language.

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

A declaration may be marked with the additional declarator own. This


has the following effect: upon a re-entry into the block, the values of own
quantities will be unchanged from their values at the last exit, while the values
of declared variables which are not marked as own are undefined. Apart from
labels and formal parameters of procedure declarations and with the possible
exception of those for standard functions (cf. sections 3.2.4 and 3.2.5), all
identifiers of a program must be declared. No identifier may be declared more
than once in anyone block head.

Syntax
(declaration) ::= (type declaration) I (array declaration) I (switch declaration) I
(procedure declaration)

5.1. TYPE DECLARATIONS


5.1.1. Syntax
(type list) ::= (simple variable) I (simple variable),(type list)
(type) ::= real I integer I Boolean
(local or own type) ::= (type) I own (type)
(type declaration) .. - (local or own type)(type list)

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. ARRAY DECLARATIONS


5.2.1. Syntax
(lower bound) ::= (arithmetic expression)
(upper bound) ::= (arithmetic expression)
(bound pair) ::= (lower bound): (upper bound)
(bound pair list) ::= (bound pair) I (bound pair list),(bound pair)
(array segment) ::= (array identifier)! (bound pair list) 1 I
(array identifier), (array segment)
(array list) ::= (array segment) I (array list),(array segment)
(array declaration) ::= array (array list) I (local or own type) array (array list)
P. Naur (ed.), J. W. Backus, F. L. Bauer,]. Green, C. Katz, ]. McCarthy, et al. 43

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.3.4. Evaluation of expressions in the switch list


An expression in the switch list will be evaluated every time the item of
the list in which the expression occurs is referred to, using the current values
of all variables involved.

5.3.5. Influence of scopes


If a switch designator occurs outside the scope of a quantity entering into
a designational expression in the switch list, and an evaluation of this switch
designator selects this designational expression, then the conflicts between
the identifiers for the quantities in this expression and the identifiers whose
declarations are valid at the place of the switch designator will be avoided
through suitable systematic changes of the latter identifiers.

5.4. PROCEDURE DECLARATIONS


5.4.1. Syntax
(formal parameter) ::= (identifier)
(formal parameter list) ::= (formal parameter)
(formal parameter list) (parameter delimiter) (formal parameter)
(formal parameter part) ::= (empty) I «formal parameter list»
(identifier list) ::= (identifier) I (identifier list),(identifier)
(value part) ::= value (identifier list); I (empty)
(specifier) ::= string I (type) I array I (type) array I label I switch I procedure I
(type) procedure
(specification part) ::= (empty) I (specifier) (identifier list); I
(specification part) (specifier) (identifier list);
(procedure beading) ::= (procedure identifier) (formal parameter part); (value part)
(specification part)
(procedure body) ::= (statement) I (code)
(procedure declaration) ::= procedure (procedure beading) (procedure body)
(type) procedure (procedure beading) (procedure body)
P. Naur (ed.),]. W. Backus, F. L. Bauer, J. Green, C. Katz, J. McCarthy, et al. 45

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

procedure Transpose(a)Order:(n); value n;


array a; integer n;
begin real w; integer i, k;
for i := 1 step 1 until n do
for k:= 1 + i step 1 until n do
begin w:= a[i,k];
a[i,k]:= a[k,i];
a[k,i]:= w
end
end Transpose

integer procedure Step(u); real u;


Step:= if 0 ~ U /\ U ~ 1 then 1 else 0

procedure Absmax(a)size:(n, m)Result:(y)Subscripts:(i, k);


conunent The absolute greatest element of the matrix a, of size n by m,
is transferred to y, and the subscripts of this element to i and k;
array a; integer n, m, i, k; real y;
begin integer p, q;
y:= 0;
for p:= 1 step 1 until n do for q := 1 step 1 until m do
if abs(a[p,q]) > y then beginy:= abs(a[p,q]);i:= p;k:= q end
end Absmax

procedure Innerproduct(a, b)Order:(k, p)Result:(y); value k;


integer k, p; real y, a, b;
begin real s;
s:= 0;
for p := 1 step 1 until k do s := s + a x b;
y:= s
end Innerproduct

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

parameters in the procedure body will, whenever the procedure is activated


(cf. section 3.2. Function Designators and section 4.7 Procedure Statements)
be assigned the values of or be replaced by actual parameters. Identifiers in
the procedure body which are not formal will be either local or nonlocal to the
body depending on whether they are declared within the body or not. Those of
them which are nonlocal to the body may well be local to the block in the head
of which the procedure declaration appears. The procedure body always acts
like a block, whether it has the form of one or not. Consequently the scope
of any label labelling a statement within the body or the body itself can never
extend beyond the procedure body. In addition, if the identifier of a formal
parameter is declared anew within the procedure body (including the case of
its use as a label as in section 4.1.3), it is thereby given a local Significance
and actual parameters which correspond to it are inaccessible throughout the
scope of this inner local quantity.

5.4.4. Values of function designators

For a procedure declaration to define the value of a function designator


there must, within the procedure body, occur one or more expliCit assignment
statements with the procedure identifier in a left part; at least one of these
must be executed, and the type associated with the procedure identifier must
be declared through the appearance of a type declarator as the very first sym·
bol of the procedure declaration. The last value so assigned is used to con·
tinue the evaluation of the expression in which the function designator occurs.
Any occurrence of the procedure identifier within the body of the procedure
other than in a left part in an assignment statement denotes activation of the
procedure.

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.

5.4.6. Code as procedure body

It is understood that the procedure body may be expressed in non-ALGOL


language. Since it is intended that the use of this feature should be entirely
a question of hardware representation, no further rules concerning this code
language can be given within the reference language.
P. Naur (ed.), J. W. Backus, F. L. Bauer,]. Green, C. Katz, J. McCarthy, et al. 47

ALPHABETIC INDEX OF DEFINITIONS OF


CONCEPTS AND SYNTACTIC UNITS
All references are given through section numbers. The references are given in three
groups:
def Following the abbreviation "def', reference to the syntactic definition (if any) is
given.
synt Following the abbreviation "synt", references to the occurrences in metalinguis-
tic formulae are given. References already quoted in the def-group are not re-
peated.
text Following the word "text", the references to definitions given in the text are given.
The basic symbols represented by signs other than boldface words have been collected
at the beginning. The examples have been ignored in compiling the index.

+, see: plus (array identifier), def 3.1.1 synt 3.2.1,


-, see: minus 4.7.1, 5.2.1 text 2.8
x, see: multiply (array list), def 5.2.1
I, +, see: divide (array segment), def 5.2.1
T,see:exponentiation (assignment statement), def 4.2.1
<,~, =, ~f >, *-, see: synt 4.1.1 text I, 4.2.3
(relational operator) (basic statement), def 4.1.1 synt 4.5.1
=, :J, V, A, .." see: (logical operator) (basiC symbol), def 2
., see: comma begin, synt 2.3, 4.1.1
. , see: decimal point (block), def 4.1.1 synt 4.5.1 text I, 4.1.3,
10, see: ten 5
:, see: colon (block head), def 4.1.1
;, see: semicolon Boolean, synt 2.3, 5.1.1 text 5.1.3
:=, see: colon equal (Boolean expression), def 3.4.1 synt 3,
,see: space 3.3.1.4.2.1.4.5.1,4.6.1 text 3.4.3
(),see: parentheses (Boolean factor), def 3.4.1
[ ], see: subscript brackets (Boolean primary), def 3.4.1
, " see: string quotes (Boolean secondary), def 3.4.1
(Boolean term), def 3.4.1
(actual parameter), def 3.2.1, 4.7.1 (bound pair), def 5.2.1
(actual parameter list), def 3.2.1, 4.7.1 (bound pair list), def 5.2.1
(actual parameter part), def 3.2.1, 4.7.1 (bracket), def 2.3
(adding operator), def 3.3.1 (code), synt 5.4.1 text 4.7.8, 5.4.6
alphabet, text 2.1 colon:, synt 2.3, 3.2.1,4.1.1,4.5.1, 4.6.1,
arithmetic, text 3.3.6 4.7.1, 5.2.1
(arithmetic expression), def 3.3.1, synt 3, colon equal :"'. synt 2.3, 4.2.1, 4.6.1, 5.3.1
3.1.1, 3.4.1,4.2.1, 4.6.1, 5.2.1 comma" synt 2.3, 3.1.1, 3.2.1, 4.6.1,
text 3.3.3 4.7.1, 5.1.1, 5.2.1, 5.3.1, 5.4.1
(arithmetic operator), def 2.3 text 3.3.4 comment, synt 2.3
array, synt 2.3, 5.2.1, 5.4.1 comment convention, text 2.3
array, text 3.1.4.1 (compound statement), def 4.1.1
(array declaration), def 5.2.1 synt 5 synt 4.5.1 text 1
text 5.2.3 (compound tail), def 4.1.1
48 Chapter 1. Revised Report on the Algorithmic Language ALGOL 60

(conditional statement), def 4.5.1 (identifier list), def 5.4.1


synt 4.1.1 text 4.5.3 if, synt 2.3, 3.3.1,4.5.1
(if dause), def 3.3.1, 4.5.1 synt 3.4.1,
(decimal fraction), def 2.5.1
3.5.1 text 3.3.3, 4.5.3.2
(decimal number), def 2.5.1 text 2.5.3 (if statement), def 4.5.1 text 4.5.3.1
decimal point., synt 2.3, 2.5.3 (implication), def 3.4.1
(dedaration), def 5 synt 4.1.1 text 1,
integer, synt 2.3, 5.1.1 text 5.1.3
5 (complete section)
(integer), def 2.5.1 text 2.5.4
(dedarator), def 2.3
(delimiter), def 2.3 synt 2 label, synt 2.3, 5.4.1
(designational expression), def 3.5.1 (label), def 3.5.1 synt 4.1.1, 4.5.1, 4.6.1
synt 3, 4.3.1, 5.3.1 text 3.5.3 text 1, 4.1.3
(digit), def 2.2.1 synt 2, 2.4.1, 2.5.1 (left part), def 4.2.1
dimension, text 5.2.3.2 (left part list), def 4.2.1
divide /,7, synt 2.3, 3.3.1 text 3.3.4.2 (letter), def 2.1 synt 2, 2.4.1, 3.2.1, 4.7.1
(dummy statement), def 4.4.1 synt 4.1.1 (letter string), def 3.2.1, 4.7.1
text 4.4.3 local, text 4.1.3
(local or own type), def 5.1.1 synt 5.2.1
else, synt 2.3, 3.3.1, 3.4.1, 3.5.1, 4.5.1 (logical operator), def 2.3 synt 3.4.1
text 4.4.3 text 3.4.5
(empty), def 1.1 synt 2.6.1, 3.2.1, 4.4.1, (logical value), def 2.2.2 synt 2, 3.4.1
4.7.1, 5.4.1 (lower bound), def 5.2.1 text 5.2.4
end, synt 2.3, 4.1.1
entier, text 3.2.5 minus -, synt 2.3, 2.5.1, 3.3.1 text 3.3.4.1
exponentiation', synt 2.3, 3.3.1 multiply x, synt 2.3, 3.3.1 text 3.3.4.1
text 3.3.4.3 (multiplying operator), def 3.3.1
(expression), def 3 synt 3.2.1, 4.7.1 non-local, text 4.1.3
text 3 (complete section) (number), def 2.5.1 text 2.5.3, 2.5.4
(exponential part), def 2.5.1 text 2.5.3 (open string), def 2.6.1
(factor), def 3.3.1 (operator), def 2.3
false, synt 2.2.2 own, synt 2.3, 5.1.1 text 5, 5.2.5
(for dause), def 4.6.1 text 4.6.3 (parameter delimiter), def 3.2.1, 4.7.1
(for list), def 4.6.1 text 4.6.4 synt 5.4.1 text 4.7.7
(for list element), def 4.6.1 text 4.6.4.1, parentheses ( ), synt 2.3, 3.2.1, 3.3.1,
4.6.4.2, 4.6.4.3 3.4.1,3.5.1,4.7.1, 5.4.1 text 3.3.5.2
(formal parameter), def 5.4.1 text 5.4.3 plus +, synt 2.3, 2.5.1, 3.3.1 text 3.3.4.1
(formal parameter list), def 5.4.1 (primary), def 3.3.1
(formal parameter part), def 5.4.1 procedure, synt 2.3, 5.4.1
(for statement), def 4.6.1 synt 4.1.1, (procedure body), def 5.4.1
4.5.1 text 4.6 (complete section) (procedure dedaration), def 5.4.1 synt 5
(function designator), def 3.2.1 text 5.3
synt 3.3.1, 3.4.1 text 3.2.3, 5.4.4 (procedure heading), def 5.4.1 text 5.4.3
goto, synt 2.3, 4.3.1 (procedure identifier), def 3.2.1
(go to statement), def 3.4.1 synt 4.1.1 synt 3.2.1, 4.7.1, 5.4.1 text 4.7.5.4
(procedure statement), def 4.7.1
text 4.3.3
synt 4.1.1 text 4.7.3
(identifier), def 2.4.1 synt 3.1.1, 3.2.1, (program), def 4.1.1 text 1
3.5.1, 5.4.1 text 2.4.3 (proper string), def 2.6.1
P. Naur (ed.),]. W. Backus, F. L. Bauer,]. Green, C. Katz, ]. McCarthy, et aI. 49

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

than it is to prove the approximate equivalence of two programs directly. As


there are usually several possible ways of implementing any particular func-
tion, it is obviously more satisfactory to specify the ideal mathematical func-
tion as the first step and then, as a second, to consider the implementation and
the approximation it introduces. All this is widely appreciated by numerical
analysts and programmers and it accounts, at least in part, for the popularity
of high level languages.
When it comes to the description of programming languages themselves,
however, the situation is quite different. Most of the work has been at the
level of symbol manipulation-that is to say it has been concerned with the
representations (generally on paper) rather than with the mathematical objects
represented. The unsatisfactory nature of our understanding of programming
languages is shown up by the fact that although the subject is clearly a branch
of mathematics, we still have virtually no theorems of general application and
remarkably few specialised results.
A second purpose of this paper is to advocate a more conventionally math-
ematical approach to the problem of describing a programming language and
defining its semantics, and, indeed to the problems of computation generally.

1. Mathematical Basis

In our search for a mathematical approach to semantics we shall make great


use of functions of various sorts. Many of these will be of "higher type"; i.e.,
will have other functions as their arguments or results. The word "functional"
is sometimes used for functions which operate on functions, but we prefer to
use the single word "function" for all types.

1.1 Functions

In order to specify a function mathematically we need to give it a domain (the


set of values in which its arguments lie) and range (the set of its function
values) as well as its graph (the set of ordered pairs of arguments and func-
tion values). The domain and range specify the functionality of the function;
i.e., the set of which it is a member. The graph, which is often given by an
expression or algorithm, identifies the particular member of this set.
The functionality of a function is often taken for granted or glossed over
when it is defined. This may be unobjectionable as the functionality can some-
times be deduced unambiguously from the expression for its graph. There
are, however, cases in which a more rigorous investigation shows up difficul-
ties and confusions of considerable importance. We shall therefore look rather
carefully at the domain and range of the more important functions which oc-
cur in the interpretation of a programming language. Before considering spe-
cific examples we need to discuss the general features of domains and ranges.
(As these are similar we shall use the word domain for both domains and
ranges.)
54 Chapter 2. The Varieties of Programming Language

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

The third method of combining the two domains Do and Dl is to form


those functions whose argument is in Do and whose result is in Dl; we shall
write Do - Dl to represent this domain. It will appear in the next section that
we are not interested in all the set-theoretic functions from Do to DI-0nly in
those which are in some sense "reasonable." But nor are we only interested
in total functions-it may well be that for some values of its arguments our
functions "fail to converge"; i.e., are undefined. We shall have more to say
about function domains in the next section. The general topic of domains
for programming languages is discussed further in several papers cited in the
references [I, 3, 51.
1.3 Reflexive Domains
When we come to examine the domains required by programming languages
later in the paper we shall often want to define a domain self-referentially. For
example in a list-processing language we might want to discuss the domain of
single level lists of atoms. Following LISP we could define a list as an ordered
pair, the first component of which was always an atom, while the second was
another list. Thus if A is the domain of atoms and D is the domain of single
level lists, we should have an equation
D = AxD
If we wanted to allow lists as well as atoms as the first component (so that
our domain was of list-structures, not merely single level lists) the defining
equation would become only very little more complicated:
D' = [A + D'I x D'
These equations are reminiscent of the recursive structure definitions in some
programming languages.
Another example is the type-free A-calculus of Church and Curry. If we
allow atoms as well as A-expressions every object must be either an atom or a
function (as every A-expression is considered to be a function). This leads to
the defining equation
D = A+ [D- DI
This looks much like our previous definitions but it conceals a serious
difficulty. If we take D - D to be the set of all set-theoretic (unctions from
D to itself, there is no solution to the equation. In fact there are always more
elements of D - D than there are of D so that the equation (which should be
interpreted as "up to an isomorphism" only) cannot be satisfied.
If D has k members, the full set D - D has kk members and, as Cantor's
theorem shows, this is always greater than k provided k is greater than one.
At first sight this looks like a fatal flaw and it certainly demonstrates
vividly the mathematical danger in failing to prove the existence of the ob-
jects we wish to discuss. We cannot now feel happy with any of our domains
defined by a self-referential equation (we shall refer to these as refleXive do-
mains) until we have proved their existence. It was the impossibility of doing
this for definitions involving D - D that prevented the construction of set-
theoretic models of the A-calculus and forced it to remain a purely formal
theory.
56 Chapter 2. The Varieties of Programming Language

Fortunately, however, in 1969 Dana Scott discovered a solution to this


problem. As we indicated in the last section the difficulty of cardinality is
avoided by restricting the domain Do - Dl to include only some of the set-
theoretic functions, though the restriction appears quite naturally as a conse-
quence of the theory. In outline Scott argues as follows.
It is reasonable to adjoin to every domain a partial ordering based intu-
itively on a degree of approximation or information content. For many elemen-
tary domains this partial ordering is of a rather trivial kind, but it is sufficient
to tum the domains into complete lattices. The partial ordering for a com-
pound domain can be derived from those of its components.
If a function is to be well behaved, it should be monotonic; i.e., preserve
the partial ordering-it should not be possible to get a better defined (more
accurate) result by giving it a worse defined argument. If a function is to be
computable, it should be possible to obtain any degree of approximation to
its result by giving a suffiCient, but still finite, amount of information about
its arguments. This means that the function should preserve limits in some
way. These two conditions lead to the idea that we should be concerned only
with continuous functions (the term is, of course, precisely defined) and this is
the restriction imposed on the construction Do - Dl. The exact mathematical
nature of continuous functions is discussed elsewhere; it is sufficient to say
here that they include all the ordinary and reasonable sorts of functions-and,
indeed, all those which are computable-and exclude only those which have
mathematically pathological properties.
Making use of these ideas Scott was then able to construct, by a method
which is reminiscent of that of Dedekind's cuts for constructing real numbers,
a reflexive model which satisfied the equation D = D - D, thus producing
the first set-theoretic model for the A-calculus.
In a further extremely elegant piece of work he proved the existence of a
universal domain U which satisfies the equation
U = A + [U + U] + [U x U] + [U - U]
where A is any domain. The domain U proves to be extremely rich in sub-
domains and Scott was able to show that these include all the reflexive do-
mains which can be defined by a self-referential equation using A (or its com-
ponents) and the domain constructing operators +, x, and -.
This is not the place to go into the details of this work and the interested
reader is referred to the papers by Scott [1, 2, 3]. We can proceed with our
analysis of the characteristic functions of programming languages secure in
the knowledge that all the reflexive domains we require, no matter how com-
plicated, do have a mathematical existence.

2. Characteristic Domains in Programming Languages


2.1 Denotations
Programming languages follow the example of mathematics generally in al-
lOwing names chosen by the user to stand for or denote certain objects. The
Christopher Strachey 57

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.

2.2 Stored Values


The state of the machine, and in particular the state of its store, determines
the contents of the locations. We shall use the Greek letter 0" to stand for a
machine state and S to stand for the set of all possible machine states. We
shall not discuss the nature of the domain S exhaustively-it seems probable
that it may vary from language to language-but it must always contain at
least enough information to give the contents of all the locations in use; it
must therefore include a component with functionality L - V where V is the
domain of all stored values-i.e., those quantities which can be the value of
the right hand side of an assignment command.
The two functions p and 0" together with their associated domains D and V
go a long way to characterising a programming language. There is a fundamen-
tal difference between these two functions which is the source of many of the
confusions and difficulties both about programming languages and also about
operating systems. This is that while the environment p behaves in a typically
"mathematical" way-several environments can exist at the same point in a
program, and on leaving one environment it is often possible to go back to a
previous one-the machine state 0" which includes the contents function for
the store, behaves in a typically "operational" way. The state transformation
produced by obeying a command is essentially irreversible and it is, by the
nature of the computers we use, impossible to have more than one version of
0" available at anyone time. It is this contrast between the static, nesting, per-
manent environment, p, and the dynamic irreversibly changing machine state,
0", which makes programming languages so much more complicated than con-
ventional mathematics in which the assignment statement, and hence the need
for 0", is absent.

2.3 The Assignment Command


We can now give a model for an abstract store and explain the meaning of
the assignment command in terms of it. The model is deliberately simplified
and the explanation informal. Most existing programming languages need a
more complicated model and a more formalised description of the assignment
command is necessary before much more detailed work on semantics can be
carried out. Many of these have been, or are in the process of being, worked
out [4, 6] but for the present paper it seems better to avoid as much detail as
pOSSible, and to give only the main outlines.
The simplest model of the store contains the following domains:
Christopher Strachey 59

L Locations OC, OC' E L


V Storable Values PEV
5 = [L ~ V] Stores (j,(j'E5

We postulate the following basic functions on these domains. In each case


we first give the functionality (i.e., range and domain of the basic function) and
then an expression which defines it.
(i) Contents: L ~ [5 ~ V]
Contents(oc)«(j) = (j(oc)
(ii) Update: [L x V] ~ [5 ~ 5]
If (j'= Update(oc, P)«(j)
Contents(oc)«(j') = p, and
*' oc.
Contents( oc')( (j') = Contents ( oc')( (j) if oc'
Thus the effect of updating a location oc in a store (j with a value P is to
produce a new store (j' which yields the contents P for the location oc but is
everywhere else identical with (j. This, of course, is exactly what we expect
a simple update operation to do. A point to notice is that the partially ap-
plied update function Update(oc, P) is of type [5 ~ 5]-Le., a function that
transforms (alters) the store.
Before we can deal with the assignment command we need to introduce
functions which yield the values of expressions. Since expressions (which in-
clude names as a special case) in general have both L-values and R-values we
need two such functions, which we shall write as J: and:R. These functions
operate on expressions in the programming language and their results clearly
depend on the environment, p, to provide a denotation for the names in the
expression; it is also fairly obvious that their results may depend on the state
of the store, (j, as well. When we consider what results they should yield, we
must remember that there is a possibility that the evaluation of an expression
may have a side effect-i.e., it may alter the store as well as producing a re-
sult. This implies that the results of our evaluation functions should be pairs
consisting of a value and a possibly altered store.
To express these ideas in symbols, we need two new domains
Exp Expressions in the Programming Language EO, El E Exp
Env = [Id ~ D] Environments P E Env
Then we have the basic functions
J:: Exp ~ [Env ~ [5 ~ [L x 5]]]
and :R: Exp ~ [Env ~ [5 ~ [R x 5]]]
The detailed definitions of J: and :R form part of the semantic description of
the programming language, and we shall not consider them further here.
We can now consider the effect of a general assignment command of the
form
EO := El
(Note that the left side of this is an expression, EO, although most program-
ming languages limit rather severely the sorts of expression that may be used
here.)
The operation takes place in three steps.
60 Chapter 2. The Varieties of Programming Language

1. Find the L-value of £0.

2. Find the R-value of £1.


3. Do the updating.
(Note: we have assumed a left-to-right order for the evaluations.)
If we are obeying this command in an environment p with an initial store
0"0, these three steps can be written symbolically as
1. ,c,(£o)(p)(O"o) = (ex, 0"1)
ex is the L-value of £0, 0"1 is the store which may have been altered while finding
ex; if there are no side effects, 0"1 = 0"0.
2. !R(£I>(P)(0"1) = ({3,O"z)
{3 is the R-value of £1. Note the use of 0"1 in place of 0"0; it is this that expresses
the left-to-right order of evaluation.
3. Update(ex,{3)(O"z) = 0"3
Then the effect of the whole command is to change 0"0 into 0"3.
We can now introduce another semantic function e which gives the mean-
ing of commands. The functionality of e will be
e: Cmd - [Env - [5 - 5]]
where Cmd is the domain of commands in the programming language. In
terms of e we can write
e(£o:= £1)(P) = 0
where 0 E 5 - 5 and, for the example above,
0(0"0) = 0"3 .

3. Two Specimen Languages


In order to make the ideas discussed above more concrete we give below a dis-
cussion of two programming languages in terms of their domains. The first,
ALGOL 60, is probably familiar to most readers; the second, PAL, is unlikely to
be known to many. For both languages we start by listing (and if necessary dis-
cussing) the elementary domains; we then define and discuss various derived
compound domains which occur naturally in the description of the language
and finally give the composition of the characteristic domains D and V.
3.1 ALGOL 60
a. Elementary Domains
T Booleans (truth values)
N Integers
R Reals
Q Strings (quotations)
J Labels (jump points)
L Locations
5 Stores (machine states)
T, N, and R have their ordinary mathematical properties. ALGOL 60 has no
basic operations on Q, but strings may be passed as parameters. We treat
J and 5 as elementary domains because we do not want to investigate their
structure. We note that 5 at least includes L - V.
Christopher Strachey 61

b. Derived Domains
Expression Values

E=D+V
E must contain D because a name by itself is a simple form of expression.

Procedures

P = [D* - [5 - 5]] + [D* - x 5]]]


[5 - [V
The parameters must lie in D as they are denoted by formal parameters inside
the body of the procedure; their number is unspecified, so that the parameter
list is a member of D* = DO + DI + D2 + . . .. The body of an ordinary (non-
type) procedure is a command-i.e., it transforms the state of the machine
and so is in [5 - 5]. A type procedure produces a result which, perhaps by
chance, in ALGOL 60 lies in V (not in E) but the procedure itself may also have
a side effect and alter the store. Thus its functionality must be [5 - [V x 5]].
Arrays
The elements of an array can be assigned to and must therefore denote loca-
tions.
Al L + L2 + L3 + ... (vectors)
L*
A2 Al + A[2 + ... (matrices)
AI* L**
A3 A2* = L*** (3-arrays)

A L*+L**+L***+··· (all arrays)


L·*

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

where P = [D* - [S - S]] + [D* - [S - [V x S]]] and W = S - [E x S].


Stored Values

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

P = L- [S- [Lx S])

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

[3] D. S. Scott. Continuous lattices. In F. W. Lawvere, editor, Toposes, Algebraic


Geometry and Logic, pages 97-136. Springer-Verlag, Berlin, 1972. Also Techni-
cal Monograph PRG-7, Oxford University Computing Laboratory, Programming
Research Group, Oxford.
[4] D. S. Scott and C. Strachey. Toward a mathematical semantics for computer
languages. In J. Fox, editor, Proceedings of the Symposium on Computers and Au-
tomata, volume 21 of Microwave Research Institute Symposia Series, pages 19-46.
Polytechnic Institute of Brooklyn Press, New York, 1971. Also Technical Mono-
graph PRG-6, Oxford University Computing Laboratory, Programming Research
Group, Oxford.
[5] D. S. Scott and C. Strachey. Data types as lattices. In preparation. 2
[6] c. Strachey. An abstract model for storage. In preparation.
[7] A. Evans, Jr. PAL-a language designed for teaching programming linguistics.
In Proceedings ACM 23rd National Conference, pages 395-403. Brandin Systems
Press, Princeton, N.j., 1968.
[8] J. C. Reynolds. GEDANKEN-a simple typeless language based on the principle of
completeness and the reference concept. Comm. ACM, 13(5):308-19, May 1970.
[9] N. Wirth and H. Weber. EULER: a generalization of ALGOL, and its formal defini-
tion. Comm. ACM, 9(1/2):13-25 and 89-99, Jan. and Feb. 1966.
[10] c. Strachey. The varieties of programming language. In Proceedings of the Inter-
national Computing Symposium, pages 222-233, Venice, April 1972. Cini Foun-
dation, Venice.

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

Although ALGOL 60 has been uniquely influential in programming lan-


guage design, its descendants have been significantly different than their
prototype. In this paper, we enumerate the principles that we believe
embody the essence of ALGOL, describe a model that satisfies these prin-
ciples, and illustrate this model with a language that, while more uniform
and general, retains the character of ALGOL.

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

1 The Influence of Models of ALGOL


Among programming languages, ALGOL 60 [1] has been uniquely influential in
the theory and practice of language design. It has inspired a variety of models
which have in turn inspired a multitude of languages. Yet, almost without
exception, the character of these languages has been quite different than that
of ALGOL itself. To some extent, the models failed to capture the essence of
ALGOL and gave rise to languages that reflected that failure.
One main line of development centered around the work of P. ]. Landin,
who devised an abstract language of applicative expressions [2] and showed
that ALGOL could be translated into this language [3]. This work was influ-
enced by McCarthy's LISP [4] and probably by unpublished ideas of C. Strachey;
in turn it led to more elaborate models such as those of the Vienna group [5].
Later many of its basic ideas, often considerably transformed, reappeared in
the denotational semantics of Scott and Strachey [6].
In [2], after giving a functional description of applicative expressions,
Landin presented a state-transition machine, called the SECD machine, for
First appeared in J. W. de Bakker and J. c. van Vliet, editors, Algorithmic Languages, pages 345-
372, Proceedings of the International Symposium on Algorithmic Languages, Amsterdam, October
1981. North-Holland, Amsterdam. Reprinted with the permission of the editors.
68 Chapter 3. The Essence of ALGOL

their evaluation. Then in [3] he extended applicative expressions to "impera-


tive applicative expressions" by introducing assignment and a label-like mech-
anism called the J-operator. The imperative applicative expressions were not
described functionally, but by an extension of the SECD machine called the
"sharing machine." In later models, such as that of the Vienna group, sharing
was elucidated by introducing a state component usually called the "store" or
"memory."
For our present concerns, three aspects of Landin's model are especially
significant. First, the variety of values that can be assigned to variables is the
same as the variety that can be denoted by identifiers or passed as parame-
ters. Landin does not emphasize this fact; it is simply a direct consequence
of the typelessness of imperative applicative expressions. Second, no distinc-
tion is made between assignments to variables and assignments to locations
embedded within data structures. Again, this is inherent in the nature of the
model, in which variables themselves are locations embedded within the data
structures of the sharing machine.
Finally, since operands are evaluated before operators, the basic method
of parameter passing is call by value, and call by name is described in terms
of call by value using parameterless functions (in contrast to the ALGOL 60
report [1], where call by value is described in terms of call by name using
appropriately initialized local variables). This approach apparently stems from
the view that undefined values do not "exist," so that a function cannot map an
undefined value into a defined value (as in LISP, where the conditional must be
regarded as a special form rather than a function). This is in contrast with the
more recent view of Scott that an undefined value is as legitimate as any other;
its only peculiarity is being least in a partial ordering that must be respected
by functions.
Directly or indirectly, Landin's model was the basis for a number of
programming languages, including his own ISWIM [7], Evans and Wozen-
craft's PAL [8], and my GEDANKEN [9]. Less obviously, the model influenced
ALGOL 68 [10], despite the significant distinction that this language is highly
typed. All of these languages inherited from the model the characteristics de-
scribed above: anything that can be passed as a parameter can be assigned to
a variable, there is no fundamental distinction between assignments to vari-
ables and to components of data structures, and call by value is either the only
or the basic mode of parameter transmission.
As a consequence, all of these languages are significantly different from
ALGOL; in certain respects they are closer to the spirit of LISP. They are all
subject to the criticism of references made by Hoare [11]. (Strictly speaking,
only ALGOL 68 and GEDANKEN use the reference concept, but Hoare's criticism
is equally applicable to the sharing or L-value approach used in ISWIM and
PAL.)
Moreover, except for ALGOL 68, none of these languages obey a stack dis-
cipline. It would require a clever compiler to make any use of a stack during
program execution, and even then it would be difficult for a programmer to
foresee when such use would occur.
In ALGOL 68, a stack diScipline is obtained by imposing the restriction that
a procedure value becomes undefined upon exit from any block in which a
John C. Reynolds 69

global variable of the procedure is declared. However, this restriction is im-


posed for the specific purpose of rescuing the stack; a stack discipline is not
a natural consequence of the basic character of the language.
Another line of development stemming from ALGOL 60 has led to lan-
guages such as PASCAL [12] and its descendants, e.g. EUCLID [13], MESA [14],
and ADA [15], which are significantly lower-level than ALGOL. Each of these
languages seriously restricts the block or procedure mechanism of ALGOL by
eliminating features such as call by name, dynamic arrays, or procedure pa-
rameters.
I am not familiar enough with the history of these languages to do more
than speculate about the influence of models. However, a desire to be "closer
to the machine" than ALGOL 60 seems evident from the abandonment of fea-
tures requiring inefficient or "clever" implementations. In this respect, im-
plementations themselves can be thought of as models influencing language
design.
1n addition, the influence of program-proving formalisms, particularly the
work of Hoare [16], is clear. An axiomatic definition of PASCAL [17) seems to
have influenced that language, and the axiomatization of EUCLID [13] was a
major goal of its design.
Since Hoare's treatment of procedures [18] does not encompass call by
name, procedure parameters, or aliasing, it may account for the weakening of
the procedure mechanism in some of these languages. Certainly the view of
procedures given by this kind ofaxiomatization is profoundly different than
the copy rule.

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

The qualification "fully typed" indicates agreement with van Wijngaarden


that all type errors should be syntactic errors, and that this goal requires a syn-
tax with an infinite number of phrase classes, themselves possessing grammat-
ical or (more abstractly) algebraic structure. (I believe that this characteristic
will be the most influential and long lasting aspect of ALGOL 68.) The failure
of this property for ALGOL 60 is a design mistake, not part of its essence.
When carried to the extreme, this principle suggests that the lambda calcu-
lus is the source of all identifier binding. More precisely, except for syntactic
sugar (language constructs that can be defined as abbreviations in terms of
more basic constructs, as the for statement is defined in the ALGOL 60 Re-
port), the only binding mechanism should be the lambda expression.
2. There are two fundamentally different kinds of type: data types, each
of which denotes a set of values appropriate for certain variables and
expressions, and phrase types, each of which denotes a set of meanings
appropriate for certain identifiers and phrases.
This syntactic distinction reflects that fact that in ALGOL values (which can be
assigned to variables) are inherently different from meanings (which can be
denoted by identifiers and phrases, and passed as parameters). Thus ALGOL-
like languages contradict the principle of completeness [9].
Moreover, in ALGOL itself data types are limited to unstructured types such
as integer or Boolean, while structuring mechanisms such as procedures and
arrays are only applicable to phrase types.
3. The order of evaluation for parts of expressions, and of implicit conver-
sions between data or phrase types, should be indeterminate, but the
meaning of the language, at an appropriate level of abstraction, should
be independent of this indeterminacy.
By "appropriate" we mean a level of abstraction where overflow and roundoff
are ignored and termination with an error message is regarded as equivalent
to nontermination. This principle prohibits expressions with side effects such
as assignments to nonlocal variables or jumps to nonlocallabels, but not ex-
pressions that cause error stops.
If types are described grammatically, the indeterminacy of implicit con-
versions will cause ambiguity. For example, in a context calling for a real
expression, 3 + 4 might be parsed as either
<real exp> <real exp>

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

Denotational Semantics Domain equations Semantic equations


Algebraic Semantics Definition of the target Definition of the target
algebra carrier algebra operations
Operational Semantics Definition of the set of Definition of the state-
states of the interpreter transition function

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.

3 Data Types and Expressions


To stay close to ALGOL 60, we take {integer, real, Boolean} as the set of data
types. To introduce an implicit conversion from integer to real, we define the
partial ordering
• real

• 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

A type assignment is a function from some finite set of identifiers to phrase


types. To describe the syntax of our language we will use phrase-class names
of the form <0, IT>, where 0 is a phrase type and IT is a type assignment, to
denote the set of phrases P such that
1. The identifiers occurring free in P belong to the domain of IT.
2. When its free identifiers are given the phrase types indicated by IT,
P has phrase type o.
We will describe syntax by production schemas (in the spirit though not the
notation of van Wijngaarden) in which the metavariables T, 0, IT, and l range
over data types, phrase types, type assignments, and identifiers, respectively.
A fragment of an appropriate syntax for expressions is
<0, IT> .. - <0', IT> when 0' s; 0
when l E dom(IT) and IT(l) =0
<integer exp, IT> ::= 0 11 I <integer exp, IT> + <integer exp, IT>
<real exp, IT> ::= 0.5 I <real exp, IT> + <real exp, IT>
<Boolean exp, IT> ::= true I false I <T exp, IT> = <T exp, IT>
I <Boolean exp, IT> and <Boolean exp, IT>
(Here dom(IT) denotes the domain of the type assignment IT.) This is an ab-
stract syntax to the extent that precedence considerations are ignored. One
could "concretize" it by adding parentheses around the right side of each pro-
duction, but a realistic concrete syntax would require far fewer parentheses.
In fact, we will use fewer parentheses in our examples of programs, trusting
the reader's intuition to supply the missing ones sensibly.
In the first two production schemas 0 ranges over all phrase types, not just
the types of expressions introduced so far. The first schema shows the pur-
pose of the subtype relationship. The second shows that an identifier assigned
some phrase type can always be used as a phrase of that type.
In accordance with Principle 3, the syntax is ambiguous (aside from paren-
thesization considerations), but this ambiguity must not result in ambiguous
meanings. An appropriate method for insuring this requirement is described
in (19); it requires that the syntax possess a property that might be called
"minimal typing":
For any phrase P and type assignment IT, if there is any 0 such
that P E <0, IT>, then there is a minimal 00 such that P E <0, IT>
if and only if 00 s; o.
(When a phrase-class name is used as a set it stands for the set of all phrases
that can be derived from that phrase-class name.)
To prohibit expressions with side effects, we will forbid any occurrence of
commands within expressions (except in vacuous contexts such as parameters
of constant procedures) and insist that the bodies of function procedures be
expressions. Actually, this is unnecessarily Draconian; one would like to per-
mit block expressions, as in ALGOL W (22), but restricted to avoid side effects.
However, this topic is beyond the scope of this paper.
John C. Reynolds 73

4 The Simple Imperative Language


The next step is to introduce variables for each data type. But here we en-
counter a surprising complication. As a consequence of Principle 4, we want
to have conditional variables. For example, when n is an integer variable and
x is a real variable, we should be able to write if p then n else x on either side
of an assignment statement. When used on the right side, this phrase must be
considered as a real expression, since when p is false it can produce a nonin-
teger value. But when used on the left side, it must be considered an integer
variable, since when p is true it cannot accept a noninteger value. Thus there
are variables that accept a different data type than they produce.
The first step in dealing with this situation is to realize that, in addi-
tion to variables, which accept and produce values, and expressions, which
only produce values, it is natural to introduce phrases called acceptors, which
only accept values. Thus for each data type T, we will have the phrase type
T acc(eptor). The subtype relation for acceptors is the dual of the subtype rela-
tion for data types. For example, since integer is a subtype of real, integer val-
ues can be implicitly converted into real values, so that a real acceptor can be
used in any context requiring an integer acceptor, i.e. real acc :s; integer acc.
The second step is to categorize variables separately by the data types that
they accept and produce. Thus for each pair of data types Tl and T2, we have
the phrase type Tl (accepting) T2 (producing) var(iable), which is a subtype of
both Tl acc and T2 expo Among variables themselves, Tl T2 var is a subtype of
Ti T2 var when Tl acc is a subtype of Ti acc and T2 exp is a subtype of T2 exp;
i.e. when the data types satisfy Ti :s; Tl and T2 :s; T2.
The case construction for variables raises the same difficulty as the condi-
tional. But a further problem arises if the empty construction case n of 0 is
permitted. Of course, it would not be unreasonable to prohibit this construc-
tion, but it is consistent to view it as a phrase whose phrase type univ(ersal) is
a subtype of all phrase types. All phrases of this type have the meaning "unde-
fined," which implicitly converts into the undefined element .L of the domain
of meanings of any other phrase type.
The only other phrase type needed to describe the simple imperative lan-
guage is comm(and). (Throughout this paper, we will speak of commands
rather than statements.) In summary, the phrase types of the simple impera-
tive language, which we will call primitive phrase types, are
Texp T acc comm univ
and the subtype relation is the least partial ordering such that
T :s; T' implies T exp :s; T' exp

T' :s; T implies T acc :s; T' acc


Ti :s; Tl and T2 :s; T2 implies Tl T2 var :s; Ti T2 var

Tl T2 var :s; T2 exp

univ:s; ()
74 Chapter 3. The Essence of ALeoL

An appropriate syntax is:

<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

implicit conversion functions from variables to acceptors and expressions are


the projections from DTI T2 var to DTI ace and DT2 expo
These two views of variables provide a nice example of the way in which
formal definition can influence language design. As long as we do not impose
any structure involving L-values or references upon states, there is no danger
of defining anything, such as call by reference, that involves these concepts.
On the other hand, the more abstract approach opens the door to features,
such as doublets in Pop-2 [21] or implicit "references" in GEDANKEN [9], that
define a variable by giving arbitrary procedures for accepting and producing
values.
In fact, the more abstract treatment of variables makes no commitment
at all to the structure of states; S is a parameter of the semantics that can
sensibly stand for any set at all. To emphasize this generality, we make S an
explicit argument of De and Envrr, and regard the semantics of a phrase as a
family of functions, indexed by S, from environments to meanings:

If P E <O,7T> then Ile,rr (P)(S) E Envrr (S) - De (S),


where
Envrr(S) = n
LEdom(rr)
Drr(L) (S),

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'.

3. When fl,f2 E S - SJ., G(fl * f2) = G(fd * G(f2).


4. Whenf E S - SJ., g. f = G(f)' (gJ.)'
5. When hE S - S - SJ., G(Ds(h» = Ds,(g· h· G).
Intuitively, 9 maps each state in S' into the member of S that is "embedded"
within it, while G maps each state-transition function in S - SJ. into the state-
transition function in S' - S~ that "simulates" it.
More precisely, an expansion of S to S' induces, for each phrase type
B, a function in Do(S) - Do(S') that maps meanings appropriate to S
into meanings appropriate to S'. If we write Do«g,G» for the function in
Do(S) - Do(S') induced by (g, G), then
Dcomm«g, G» = G,
DTexp«g,G»(e E S - (VT)J.) = g. e,
DTacc«g, G»(a E VT - Dcomm(S» = a· G,
DTl T2 var( (g, G» «a, e» = (DTl ace< (g, G» (a), DT2 exp( (g, G» (e».
By pointwise extension, an expansion of S to S' induces, for each type
assignment TT, a function in Env1T (S) - Env1T (S') that maps environments
appropriate to S into environments appropriate to S'. If we write Env1T «g, G»
for the function in Env1T (S) - Env1T (S') induced by (g, G), then
Env1T «g, G»(11 E Env1T (S»(L) = D 1T (,)«9,G»(I1(L».
We can now state the fundamental relationship between the semantics of
a phrase for different state sets: If P is a phrase in <B,TT> and (g, G) is an
expansion of S to S', then
PO.1T(P)(S) . Do«g, G» = Env1T «g, G» ·PO.1T(P)(S').
(In fact, properties (1) to (5) of expansions are sufficient to make this relation-
ship hold for all phrases of the simple imperative language.)
As shown in [20], this development can be described succinctly in the lan-
guage of category theory. State sets and expansions form a category ~, with
(Is,Is-s.L) as the identity on Sand (g,G)· (g',G') = (g'. g,G· G') as com-
position. Then each Do and EnV1T is a functor from ~ to the category Dom
of domains and continuous functions, and the fundamental relationship given
above is that PO.1T (P) is a natural transformation from Env1T to Do.
John C. Reynolds 77

5 Procedures and Their Declarations


To provide procedures, we introduce a binary operation - upon phrase types.
A phrase of type 01 - O2 denotes a procedure whose calls are phrases of
type 02 containing an actual parameter of type 01. Multiple parameters will be
treated by Currying, i.e.
P(A1, ... ,A n) means P(Ad··· (An)
and
i\(F1:0}, ... ,Fn:On).B means i\F1:01.··· i\Fn:On.B.
This way of de sugaring multiple parameters is sufficiently well known that we
will not formalize it.
Thus what would conventionally be called a proper procedure (or T func-
tion procedure) accepting parameters of types 0 1 , ••• , On is regarded as a
phrase of type 01 - ... - On - comm (or 01 - ... - On - T exp). Note that
parameterless proper procedures are simply commands (as was recognized
in ALGOL W (22), where an actual parameter of this type could be any com-
mand), and parameterless function procedures are Simply expressions (which
is a natural and pleasant consequence of call by name).
It is easy to see that if 02 :s; O~, then 01 - 02 :s; 01 - O~. Less obviously, if
0;' :s; ell then 01 - 02 :s; 0;' - 02. For example, since an integer expression can
appear in any context requiring a real expression, a proper procedure accept-
ing a real expression can also accept any integer expression and is therefore
meaningful in any context requiring a proper procedure accepting an integer
expression. Thus real exp - comm :s; integer exp - comm.
In summary, the set of phrase types is the smallest set containing the
primitive phrase types and closed under the binary operation -. Its subtype
relation is the least partial ordering satisfying the properties given earlier plus
0;' :s; 01 and 02 :s; O~ implies 01 - 02 :s; 0;' - O~.
In brief, - is antimonotone in its first operand and monotone in its second
operand.
A suitable syntax for application (procedure statements and function des-
ignators), abstraction (lambda expressions), and least fixed-points (recursion)
is
<02,rr> ::= <0 1 - 02,rr>«01,rr»
<01 - 02, rr> ::= i\L: 01. <02, [rr I L: OIl>
<0, rr> ::= rec<O - 0, rr>
Here [rr I L : OIl denotes the type assignment similar to rr except that it maps
Linto ell i.e.
dom([rr I L: OIl) = dom(rr) U {L},
[rr I L: OIl(L) = ell
[rr I L: OIl(L') = rr(L') when L' ,fo L.
For later developments, it will be convenient to extend this notation by using
the following abbreviations:
78 Chapter 3. The Essence of ALeoL

[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

G(c) «0", v» = if c(O") =.L then .L else (c(O"), v).

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) =

(.\0", (O",inih)) . Ilcomm,[1T!L:TT varj (C')(5 x VT)(I1') . (g.1.)'

(Our unALGOL-like use of a standard initialization is the simplest way to avoid


the abyss of nondeterminate semantics.)
However, this approach to variable declaration has a radical effect on the
notion of what procedures mean that forces us to abandon the conventional
idea that DiiI -02 (5) = DOl (5) - D02 (5). The problem is that variable decla-
rations may intervene between the point of definition of a procedure and its
point of call, so that the state set 5' relevant to the call is different than the
John C. Reynolds 81

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'),

such that, for all (g,G) E expand(S,S'), (g',G') E expand(S',S"), and


a E DOl (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).

A full description of this kind of semantics is presented in [20]; in partic-


ular abstraction and application are defined and the validity of beta and eta
reduction is proved. This is done by showing that the above definition of -
makes Dorri- (the category of functors and natural transformations from ~ to
Dom) into a Cartesian closed category, which is an extremely general model of
the typed lambda calculus. I
Despite its apparent complexity, much of which is due to our avoidance
of category theory in this exposition, this kind of semantics shows that our
language is obtained by adding the typed lambda calculus to the simple im-
perative language in a way that imposes a stack diSCipline. The essential idea
is that the procedure mechanism involves a "hidden abstraction" over a family
of semantics indexed by state sets.
We suspect that this kind of hidden abstraction may arise in other situa-
tions where a formal language is extended by adding a procedural or defini-
tional mechanism based on the lambda calculus. The generality of the idea
is indicated by the fact that the definition of - and the proof that Dorri- is
Cartesian closed do not depend upon the nature of the category ~.

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

In the ALGOL 60 report, call by value is explained in terms of call by name


by saying that a value specification is equivalent to a certain modification of
the procedure body. In fact, however, this modification involves only the body
and not the formal parameter list, so that it is equally applicable to commands
that are not procedure bodies. In essence, call by value is really an operation
on commands rather than parameters.
To capture this idea, we introduce the syntax

<comm, [rr I ( : T exp] > ::= T value (in <comm, [rr I (: TT var] >

which is de sugared by the equivalence

T value ( in C =comm.[rrICT exp]

new T var (' in (1' := (; (.\t: TT var. C)((')),

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

Co; Ll: Cl ; ... ; Ln: K =compi,7T

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.

10 Products and Sums


Although procedures and arrays are the only ways of building compound
phrase types in ALGOL, most newer languages provide some kind of product of
types, such as records in ALGOL W or class members in SIMULA 67 [26], and of-
ten some kind of sum of types, such as unions in ALGOL 68 or variant records
in PASCAL. In this section we will explore the addition of such mechanisms to
our illustrative language.
Since we distinguish two kinds of type, we must decide whether to have
products of data types or phrase types (or both). Products of data types would
be record-like entities, except that one would always assign to entire records
rather than their components. (Complex numbers are a good example of a
simple product of data types.) On the other hand, products of phrase types
are more like members of SIMULA classes than like records; one can never
assign to the entire object, but only to components that are variables; other
types of components, such as procedures, are also possible. In this paper, we
will only consider products (and sums) of phrase types, thereby retaining the
ALGOL characteristic that data types are never compound.
We must also decide between numbered and named products, Le. between
selecting components by an ordinal or by an identifier (Le. field name). In this
paper we will explore named products, since they are more commonly used
than numbered products, and also since they are amenable to a richer subtype
relationship.
To introduce named products of phrase types, we expand the set of phrase
types to include
prod IT,
where IT is a type assignment. Usually we will write products in the form
prod[Ll: 01 I ... I Ln: On], where Ll, ... , LIl are distinct identifiers. However,
it should be understood that the phrase type denoted by this expreSSion is
independent of the ordering of the pairs Lk: Ok.
For a subtype ordering, one at least wants a component-wise ordering. But
a more interesting and useful structure arises if we permit implicit conversions
that drop components, e.g.
John C. Reynolds 85

prod[age:integer exp I sex: Boolean exp I salary: integer var]


:;; prod [ age: integer exp I salary: integer var]
In general, we have
prod rr :;; prod rr' if and only if
dom(rr') s;;: dom(rr) and ('v'L E dom(rr'»)7T(L) :;; 7T'(L).
Next we introduce the syntax of phrases for constructing products and
selecting their components:
<prod[LI: lh I ... I Ln: 6 n ], 7T> ::= (LI: <61. 7T>, ... , Ln: <6n , 7T»
where Lb ..• , Ln are distinct identifiers, and
<6,7T> ::= <prod[L: 6], 7T>. L
In the second production, notice that our subtype ordering permits us to write
[L: 6] instead of [ ... I L: 6 I ... ].
The semantics of products is explicated by the following equivalences:
When PI E <6 1, 7T>, ... ,Pn E <6n , 7T>, LI, ... , Ln are distinct, and 1:;; k :;; n,
(LI:PI, ... , Ln:Pn). Lk =6k,rr Pk·
WhenP E <prod 7T',7T> anddom(7T') = {LI, •.. ,Ln},
{Ll: (P. Ld, .. ·, Ln: (P. Ln» =prod rr',rr P
We have mentioned that this kind of product is closely related to the
class concept of SIMULA 67. In [25] it is shown that class declarations (in the
reference-free sense of Hoare [27] rather than of SIMULA itself) can be desug-
ared into constructions using such products.
Finally, we introduce named sums of phrase types. (Roughly speaking,
type sums are disjoint unions.) We expand the set of phrase types to include
sum 7T,
where 7T is a type assignment. The subtype relation is
sum 7T :;; sum 7T' if and only if
dom(7T) s;;: dom(7T') and ('v'L E dom(7T») 7T(L) :;; 7T'(L).
In contrast to the situation with products, a subtype of a sum can contain
fewer (rather than more) alternatives. To construct sums and to do case anal-
YSis, we introduce the syntax
<Sum[L : 6], rr> ::= tag L: <6, 7T>
<6, rr> ::= sumcase Lis <sum[LI: 61 I ... I Ln: 6 n ], rr>
in (LI: <6, [7T I L: 611>, ... , Ln: <6, [rr I L: 6 n ]»
where LI, ... , Ln are distinct identifiers.
Again, the semantics can be explicated by equivalences. When LI, ... , Ln
are distinct, 1:;; k:;; n, PI E <6,[7T I L:611>, ... ,Pn E <6,[7T I L:6n ]>, and
A E <6k,7T>,
sumcase L is tag Lk:A in (LI:PI, ... , Ln:Pn) =6,rr let L be A in Pk.
86 Chapter 3. The Essence of ALGOL

When S E <sum 1T', 1T> and dom(1T') = {L1. .•• , Ln},


sumcase Lis S in (LI:tag LI:L, ••. , Ln:tag Ln: L) =Sum1T'.1T S.
Since sumcase is a binding operation, Principle 1 requires us to ex-
press it in terms of a construction in which the binding is done by lambda
expressions. For this purpose, we introduce the idea of "source-tupling."
Suppose PI, ... ,Pn are procedures of phrase types lh - (}, ... , (}n - (),
respectively. Then sourcetuple(LI:P1. ... , Ln:Pn) is a procedure of type
Sum[LI: (}l I ... I Ln: (}n] - () that will behave like Pk when applied to a param-
eter tagged with Lk.
To make this precise we use the syntax
<Sum[LI: (}l I ... I Ln: (}n] - (},1T> ::=
sourcetuple(LI: <(}I - (), 1T>, ••• , Ln: <(}n - (), 1T»
where L1. ••• , Ln are distinct identifiers.
Then sumcase is de sugared by the following equivalence: If LI, ... , Ln are dis-
tinct, S E <Sum[LI: (}I I ... I Ln: (}n], 1T>, Pk E <(), [1T I L: (}k]> for 1 :s; k :s; n,
then
sumcase Lis S in (LI:PI. ... , Ln:Pn) =0.1T

sourcetuple(LI: M: (}l.PI. ... , Ln: AL: (}n.Pn)(S),


It should be noted that sums of phrase types do not introduce any failure
of typing such as the "mutant variable record problem" of PASCAL, since one
cannot change the tag of a sum by assignment. On the other hand, sums of
data types would also avoid these problems since a branch on the tag of a
value would not imply any assumption that a variable with that value would
continue to possess the same tag. This suggests that the type-safety problem
with sum-like constructions is due to a failure to distinguish data and phrase
types.

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).

[6) D. Scott and C. Strachey, Toward a mathematical semantics for computer


languages, Proc. Symposium on Computers and Automata, Polytechnic Inst.
of Brooklyn, Vol. 21, pp. 19-46. (Also: Technical Monograph PRG-6, Oxford
Univ. Computing Lab., Programming Research Group (1971).)
[7) P. J. Landin, The next 700 programming languages, Comm. ACM 9 (3) (March
1966) 157-166.
[8) A. Evans, PAL-A language designed for teaching programming linguistics, Proc.
ACM 23rd Natl. Con{., 1968 (Brandin Systems Press, Princeton, NJ) pp. 395-403.

[9) J. C. Reynolds, GEDANKEN-A simple typeless language based on the principle of


completeness and the reference concept, Comm. ACM 13 (5) (May 1970) 308-319.
[10) A. van Wijngaarden (ed.) et aI., Revised report on the algorithmic language
ALGOL 68, Acta Informatica 5 (1975) 1-236.
[11) c. A. R. Hoare, Recursive data structures, Int. ]. Comput. Information Sci. 4 (2)
(June 1975) 105-132.
[12) N. Wirth, The programming language PASCAL, Acta Informatica 1 (1) (1971) 35-
63.
[l3) R. L. London, J. V. Guttag, ].]. Horning, B. W. Lampson, J. G. Mitchell and
G. J. Popek, Proof rules for the programming language EUCLID, Acta Informat-
ica 10 (1) (1978) 1-26.

[14) c. M. Geschke, J. H. Morris, and E. H. Satterthwaite, Early experience with MESA,


Comm. ACM 20 (8) (August 1977) 540-553.
88 Chapter 3. The Essence of ALGOL

(15) ]. D. Ichbiah, J. C. Heliard, O. Roubine, ]. G. P. Barnes, B. Krieg-Brueckner and


B. A. Wichmann, Preliminary ADA reference manual and Rationale for the design
of the ADA programming language, SIGPLAN Notices 14 (6) (June 1979).
(16) c. A. R. Hoare, An axiomatic basis for computer programming, Comm. ACM 12
(10) (October 1969) 576-580 and 583.
(17) c. A. R. Hoare and N. Wirth, An axiomatic definition of the programming language
PASCAL, Acta Informatica 2 (4) (1973) 335-355.
(18) c. A. R. Hoare, Procedures and parameters: An axiomatic approach, in E. En-
geler (Ed.), Symposium on Semantics of Algorithmic Languages, Lecture Notes in
Mathematics, Vol. 188 (Springer-Verlag, Berlin, 1971) pp. 102-116.
(19) J. C. Reynolds, Using category theory to design implicit conversions and generic
operators, in: N. D. Jones (Ed.), Semantics-Directed Compiler Generation, Pro-
ceedings of a Workshop, Aarhus, Denmark, January 14-18,1980, Lecture Notes
in Computer SCience, Vol. 94 (Springer-Verlag, Berlin, 1980) pp. 211-258.
(20) F. J. Oles, A Category-Theoretic Approach to the Semantics of Programming Lan-
guages, Ph.D. Dissertation (completed August 1982), Syracuse University. See
Chapter 11.
(21) R. M. Burstall, ]. S. Collins and R. ]. Popplestone, Programming in Pop-2 (Edin-
burgh University Press, 1971).
(22) N. Wirth and C. A. R. Hoare, A contribution to the development of ALGOL,
Comm. ACM 9 (6) (June 1966) 413-432.
(23) J. C. Reynolds, The Craft of Programming (Prentice-Hail, Englewood Cliffs, NJ,
1981).
(24) ]. c. Reynolds, IDEAUZED ALGOL and its Specification Logic, Computer and Infor-
mation Science, Syracuse University, Report 1-81 (July 1981). See Chapter 6.
(25) ]. c. Reynolds, Syntactic control of interference, Conference Record of the Fifth
Annual Symposium on Principles of Programming Languages, Tucson, January
1978 (Association for Computing Machinery, New York, 1978) pp. 39-46. See
Chapter 10.
(26) 0.-]. Dahl, B. Myhrhaug and K. Nygaard, SIMULA 67 Common Base Language, Nor-
wegian Computing Centre, Oslo (1968).
(27) c. A. R. Hoare, Proof of correctness of data representations, Acta Informatica 1
(4) (1972) 271-281.
(28) E. W. Dijkstra, A Discipline of Programming (Prentice-Hail, Englewood Cliffs, NJ,
1976).
(29) c. T. Zahn, A control statement for natural top-down structured programming,
Proceedings, Programming Symposium, Paris, April 9-11, 1974, Lecture Notes in
Computer Science, Vol. 19 (Springer-Verlag, Berlin, 1974) pp. 170-180.
Chapter 4
ALGOL and Functional Programming
Peter W. O'Hearn
A simple idealized ALGOL is considered, based on Reynolds's "essence
of ALGOL." It is shown that observational equivalence in this language
conservatively extends observational equivalence in its assignment-free
functional sub language.

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

The proof of conservativity utilizes denotational models. The interesting


twist in the proof is the use of a non-standard model for the ALGoL-like lan-
guage. We want to work with a model of the full imperative language in
which semantic equality conservatively extends equality in a standard domain-
theoretic model of functional languages. It turns out that standard models
of ALGoL-like languages are not suitable because they contain what Reynolds
calls "snapback" operations, which cause backtracking of state changes that
require copying of the entire state (cf. [7, 8] for discussion). These operations
violate the intuitive property of irreversibility of state changes, and Section 3
shows an example of where snapback invalidates an equivalence true in the
assignment-free sublanguage. Thus, conservativity fails for the standard mod-
els. The main step in the proof is the formulation of a non-standard model for
which a semantic conservativity result does hold.
The result we seek concerns not only semantic equality, but observational
equivalence; that is, equivalence in all program contexts. It can be (and is of-
ten) the case that semantic equality and observational equivalence for a model
and language do not match. In order to extend our result to observational
equivalence we need to work with a fully abstract model of the assignment-
free sublanguage, a model in which semantic and observational equivalence do
coincide. For this we use Plotkin's [9) fully abstract model of PPCF, a language
with recursion and basic arithmetic constructs, and extended with a (deter-
minate) parallel conditional. The proof does adapt easily to other functional
sublanguages, including sequential PCF, simply by working with term models.
But since this adaptation should be clear from the form of the proof it seems
reasonable, for the sake of Simplicity, to show the result utilizing the stan-
dard continuous-function model of parallel peF. A fully abstract model is not
required for the full ALGoL-like language.
I consider the result given here to be part of folklore. Amongst those with
a detailed knowledge of "The essence of ALGOL" the result is, I suspect, either
already known or would become known soon after the question was consid-
ered. But it is a piece of folklore that deserves to be explicitly noted, especially
in light of the growing interest in integrating functional and imperative pro-
gramming, e.g., [10, 11, 12, 13, 14, 15). Conservative extension results of the
kind considered here have been a specific concern in [16,17,18,19).

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 .

For Simplicity, we only consider storage variables that hold natural-number


values; variables for the booleans could easily be added. Though we will not
do so here, in the presence of product types we could take comm as the only
Peter W. O'Hearn 91

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

Table 1: Semantic Equations

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

9 E S[bool - bool] defined by:


g(e)s = e(s I .e ..... 0)
where .e E L is a fixed location. Intuitively, 9 executes e after changing the
state, by assigning 0 to .e, and so there are two states, s and (s I .e ..... 0), that
playa role in the evaluation of the semantic expression g(e)s. To see this
issue on the level of equivalences, consider the terms
M = ifx (((x») 0 N = ifx (((tt») 0
where (:bool- boo} and x:bool are identifiers, and 0 = Ybool('\X.X) is the
divergent boolean. This is a valid equivalence in PPCF, P[M] = P[N], because
in the model ( is applied directly to the value of x, which is a truth value.
However, in the S[·] model (is applied to an argument of semantic type S~ TJ.,
and so there is an opportunity to apply ( in states where x is false. Specifically,
define e E S[bool] by

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['\xt.M]usa = e[M](ulx t ..... ('\s' ES.a))s ('\xt.M ofPPCF 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[·].

3.2 Observational Conservativity


Observational equivalence will be generated by observing convergence at
ground type. In the case of lA, this means a closed term of type comm or
var, as well as terms of type nat or bool.
Definition 2 (Observational Equivalence)
1. For PPCF terms M, N, M =PPCF N iff for all ground PPCF contexts C[· ],
:P[C[M]] =..L <= :P[C[N]] = ..L

2. For 14 terms M, N, M =IA N iff for all ground 14 contexts C[· ],


S[C[M]] =..L <= S[C[N]] = ..L
96 Chapter 4. ALGOL and Functional Programming

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:

(p,p') ERP-P' = 'I1(a,a') ERP.('\s.(ps)(as»,p'(a'») ERP'


(p,p') E Rt-t' = 'I1(a,a') Rt. (p(a),p'(a'») Rt'
E E

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

This, together with Lemma 1, yields the result.

Proposition 4 (Observational Conservativity) For all PPCF terms M and N,


M =PPCF N = M =IA N

Proof: If a PPCF context C[ . ] distinguishes M and N in:J>[ . ], say:J>[ C[M]] *- 1.


and :J>[ C[N]] = 1., then by the Semantic Conservativity lemma we have
e[ C[M]] *- 1. and e[ C[N]] = l.. The <= direction then follows from the Ad-
equacy lemma.
Conversely, if M =PPCF N then :J>[M] = :J>[N] by the Full Abstraction theo-
rem for :J>[ .]. By the Semantic Conservativity lemma we get e[M] = e[N], and
then M =IA N follows from the Adequacy lemma and the compositionality of
e[·]. 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

new 0 (Ax. ([·](x:= x + 1»; if x = 1 then skip else n)

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

[19] J. G. Riecke and R. Viswanathan. Isolating side effects in sequential languages. In


Conference Record of POPL'95: 22nd ACM SIGPLAN-SIGACT Symposium on Prin-
ciples of Programming Languages, pages 1-12, San Francisco, California, 1995.
ACM, New York.
[20] c. Strachey. The varieties of programming language. In Proceedings of the Inter-
national Computing Symposium, pages 222-233, Venice, April 1972. Cini Foun-
dation, Venice. See Chapter 2.
[21] c. A. Gunter. Semantics of Programming Languages: Structures and Techniques.
The MIT Press, Cambridge, Mass., and London, England, 1992.
[22] A. R. Meyer and K. Sieber. Towards fully abstract semantics for local variables:
preliminary report. In Conference Record of the Fifteenth Annual ACM Sympo-
sium on Principles of Programming Languages, pages 191-203, San Diego, Cali-
fornia, 1988. ACM, New York. See Chapter 7.
[23] U. S. Reddy. Global state considered unnecessary: Introduction to object-based
semantics. LIsp and Symbolic Computation, 9(1):7-76, 1996. See Chapter 19.
[24] F. J. ales. A Category-Theoretic Approach to the Semantics of Programming Lan-
guages. Ph.D. thesis, Syracuse University, Syracuse, N.Y., 1982. See Chapter 11.
[25] K. Sieber. Full Abstraction for the Second-order Subset of an ALGOL-like Language
(preliminary report). Technischer Bericht A 01/94, Informatik, Universitiit des
Saarlandes, Saarbriicken, Germany, 1994. See Chapter 15.
[26] R. D. Tennent. Semantical analysis of specification logic. Information and Com-
putation, 85(2):135-162, 1990. See Chapter 13.
[27] J. C. Reynolds. Syntactic control of interference. In Conference Record of the Fifth
Annual ACM Symposium on Principles of Programming Languages, pages 39-46,
Tucson, Arizona, January 1978. ACM, New York. See Chapter 10.
[28] U. S. Reddy. Passivity and independence. In Proceedings, Ninth Annual IEEE Sym-
posium on Logic in Computer Science, pages 342-352, Paris, France, 1994. IEEE
Computer Society Press, Los Alamitos, California.
[29] P. W. O'Hearn, A. J. Power, M. Takeyama, and R. D. Tennent. Syntactic control of
interference revisited. In Brookes et al. [32]. See Chapter 18.
[30] R. Milner. Fully abstract models of typed '\-calculi. Theoretical Computer Science,
4:1-22,1977.
[31] Conference Record of the Twentieth Annual ACM SIGPLAN-SIGACT Symposium on
Principles of Programming Languages, Charleston, South Carolina, 1993. ACM,
New York.
[32] S. Brookes, M. Main, A. Melton, and M. Mislove, editors. Mathematical Foundations
of Programming Semantics, Eleventh Annual Conference, volume 1 of Electronic
Notes in Theoretical Computer Science, Tulane University, New Orleans, Louisiana,
March 29-April1 1995. Elsevier Science.
Chapter 5
On the Orthogonality of Assignments and
Procedures in ALGOL
Matthias Felleisen and Stephen Weeks

According to folklore, ALGOL is an "orthogonal" extension of a simple im-


perative programming language with a call-by-name functional language.
The former contains assignments, branching constructs, and compound
statements; the latter is based on the typed '\-calculus. To formalize
ALGOL'S notion of "orthogonality", we define an extended '\-calculus that
models ALGOL. The calculus includes the full p-reduction rule and re-
duction rules for assignment statements and commands. It has the usual
Church-Rosser property and its recursion-free subset is strongly normal-
izing.
In support of the "orthogonality" claim, we show that the proofs of
the Church-Rosser and Strong-Normalization theorems are combinations
of separate theorems for each sub-language. In addition, the calculus sat-
isfies a Postponement Theorem which operationally formalizes the idea
that the evaluation of an ALGOL program has two distinct phases which
correspond to the two sub-languages. The first phase corresponds to an
unrolling of the program according to the usual p and fixpoint reduc-
tions. The result of this phase is essentially an imperative program. The
second phase corresponds to the execution of an imperative program on
an ordinary stack machine.

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

sub-language of lA contains only arithmetic primitives; and the language lacks


facilities for handling exceptions or dealing with complex data. Thus, it is
simple enough to permit an easily comprehensible semantics, yet complete
enough to permit a generalization of our results to other members of the
ALGOL-family.
Based on the syntactic definition in the first subsection, we specify the se-
mantics of the language in the second subsection via a combination of two
reduction systems. One system describes the command component, the other
one is a Simply-typed A-calculus. The union of the two basic reduction re-
lations generates the calculus for the entire language. We then show in the
final subsection that this calculus provides an adequate reasoning system for
the language; i.e., it is consistent and strong enough to define a well-behaved
evaluation function.

2.1 Syntax and Informal Semantics


The following definition specifies the abstract (context-free) syntax of lA's
commands and expressions:
M r n' I 0P I x I (M M) arithmetic expressions
I skip I if M M M I begin M M nop, branching, sequencing
I new (x, M). M block
I!MIM:=M dereferencing, assignment

I Ax.M I (rec M) (recursive) procedures


where n E 71., op E {+, -, *}, and x E Vars, a denumerable set of variables.
The first part of the set of terms (above the line) constitutes the syntax of
the imperative sub-language; the second part (below the line) is the A-calculus
extension, including a rec-construct for declaring recursive objects (proce-
dures and the "diverging integer"). Both Ax.M and new(x,N).M bind x in
M; no other construct binds a variable in a term. A variable occurs free in a
term if it is not bound by a surrounding binding construct. A context is a term
with a single "hole", [ ], in the place of a sub-term; C[ ] denotes a context. The
notation C[M] refers to the result of "filling" the hole of the context C[ ] with
term M, possibly capturing some free variables of M.
Convention 1. We adopt a number of Barendregt's [1] A-calculus conventions
for lA. Specifically, we identify terms that are equivalent modulo change of
bound variables and use M == N to denote this equivalence. The sets of free
and bound variables in distinct terms do not interfere with each other in defi-
nitions and theorems. Contexts are not subject to this convention. I
The type system filters legal from illegal terms and separates the set of
terms into a sub-language of commands (of type comm) and expressions. It is
a Simple version of FORSYTHE'S type system [17].
An expression in the imperative sub-language may either be of ground
type (0) or of a first-order arithmetic function type (0 - 0) and 0 - (0 - 0).
For example, r n' is of type 0 and (+ r 1 ') is of type 0 - o. A command in the
imperative sub-language is a term of type comm. Like ALGOL and FORSYTHE,
104 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL

• 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

Figure 1: Type Inference System for IA

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

the requirement that if-expressions and begin-expressions be of type 0 is only


to simplify the theory, and may be removed in practice with the following ab-
breviations:
if L M N ~ l\x. if L (M x) (N x)
begin M N ~ l\x. begin M (N x);
in each case, x is a vector of variables of the length required to assign an
appropriate ground type to the expressions (M x) and (N x).
The behaviour of lA's imperative sub-language is that of an assembly lan-
guage for a stack-machine. Numerals are basic values; arithmetic expressions
are evaluated as usual. Blocks allocate a new location (reference) with some
initial value, execute their body, and finally deallocate the location. The com-
pound and branching terms are also executed in the usual manner: the former
sequences the execution of two terms, the latter compares the result of its first
sub-expression to '0' and branches to the second if the answer is affirmative
and to the third term otherwise.
The informal semantics of the procedural language is easily explained via
ALGOL'S copy rule [13]. A procedure replaces its parameters by its arguments
(after renaming internal bound variables, which we implicitly assume accord-
ing to Convention 1). A recursive definition is unrolled as far as necessary.
The sub-language of IA does not provide a looping construct but again, this
omission only simplifies the analYSis of the theory and is no real obstacle to
programming in IA. For example, a while loop is a simple abbreviation, based
on recursive functions:
while M do N ~ «(ree l\w.l\x.l\y. if x skip begin y «w x) y» M) N)
The procedure ~!
l\j.new(S, 'O').new(i,j).
(begin while (!i) do (begin S:=«+ !S)!i)
i :=«- ! i) '1'»
IS)
employs a mixture of functional and imperative facilities. It passes the type
inference system as an expression of type int-int, that is, as a procedure from
integers to integers. (~! j) computes the sum from '0' to j in an imperative
fashion. Here is an equivalent functional version:

~" ~ (ree l\sum.M.if i'D' «+ i) (sum «- i) 'I '»»


2.2 Semantics and Calculus
To formalize lA's semantics, we define an equational calculus. 2 The definition
of the basic notion of reduction for IA relies heavily on our experience with
extended l\-calculi for untyped languages with assignment statements, specif-
ically, the definition of the l\n-R-calculus for a call-by-name language and that
of the l\v-B-calculus for a language with first-class reference cells [2, 8].
2Jn contrast, Lent [101 defines an operational semantics of an lA-like language based on so-
called structural operational semantics. His major concern is an "adequacy theorem" for his
language with respect to a categorical model.
106 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL

(8) «op 'n') 'rn') 'n op rn'


(pop) new(x, 'n'). v v, v is a numeral or skip
(D) new(x, 'n').E[!x] new (x, 'n').E['n'],x rt. TR(E)
(a) new(x, 'n').E[x := 'rn'] new(x, 'rn').E[skip],x rt. TR(E)
(T) if '0' M N M
(F) if 'n + l' M N N
(B) begin skip M M

(f3) «Ax.M) N) [N/x]M


(fix) (ree M) (M (ree M»

Figure 2: Basic Notions of Reduction

Convention 2. We adopt Barendregt's [1] conventions about reduction rela-


tions and calculi. A notion of reduction, r, denotes a binary relation on the set
of phrases. The relation -r, the compatible closure of r, is defined by:
(M,N) E r => C[M] -r C[N] for all contexts C.
The relation - ; is the reflexive and transitive closure of - r ' The relation =r
is the equivalence relation generated by - r ' Given two notions of reduction
rl and rz, rlrZ denotes rl u rz· I
In order to describe the rules for aSSignment, we must first define a set
of special contexts, evaluation contexts, and the corresponding set of trapped
references, TR(E):
E "= TR(E) E .. - TR(E)
I[] 0 I !E TR(E)
I «op E) M) TR(E) IE:= M TR(E)
I «op 'n') E) TR(E) I x:= E TR(E)
I new(x,E).M TR(E) I ifEMM TR(E)
I new(x,'n').E {x} u TR(E) I begin E M TR(E)
The hole in an evaluation context indicates the sub-expression whose assign-
ment commands and dereferencing expressions may be executed next. It is an
easy induction to show that a term has a unique partitioning into an evalua-
tion context and an assignment command or dereferencing expression, unless
it is a numeral or a skip. This fact guarantees the well-behavedness of the
imperative part of the calculus; we call this the unique-partition property.
Figure 2 defines the basic notions of reduction for IA. The first part (above
the line) characterizes the behaviour of the imperative sub-language and the
second part (below the line) is the usual set of reductions for the simply-typed,
call-by-name A-calculus with a recursion construct.
The rules capture the informal semantics in an intuitive manner. Sequenc-
ing of the begin-expression is enforced by the structure of evaluation contexts
Matthias Felleisen and Stephen Weeks 107

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 ,.

2.3 Characteristics of eval


To show that the language IA is a deterministic, well-behaved language like
ALGOL, we need to prove that the evaluator is indeed a function, and that
this function is only undefined if all reductions starting from the program are
infinite. The easiest way to prove these results is via a Church-Rosser theorem
and a subject reduction theorem for types. Both results hold for the two sub-
languages and carry over to IA in a uniform manner.
We begin by proving that eval is a well-defined function.
3Not all expressions whose effects do not interfere can be reduced independently in the current
calculus.
108 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL

Theorem 2 Let M be a program, let n, m E 7L.

1. If eval(M) = 'n' and eval(M) = 'm' then m = n.


2. eval(M) = 'n' if and only if M -ia 'n'.
Proof: Both parts are immediate consequences of Theorem 3. I
Theorem 3 (Church-Rosser Theorem) If L -ia M and L -ia N, then there
exists K such that M -ia K and N -ia K.
Proof: The proof consists of three lemmas. The first two establish the Church-
Rosser property for each subsystem, i.e., p and {3fix, separately. The third
lemma shows that the subsystems merge smoothly.
1. The notion of reduction {3fix is Church-Rosser. This fact immediately
follows from the Church-Rosser Theorem for Simply-typed A-calculus
(with reel. The new syntax does not interfere with the proof.
2. The reflexive-compatible closure of p satisfies the diamond property di-
rectly. This is shown by careful case analysis: see Appendix A, Lemma 19
for details. Hence, by a diagram chase, the reflexive and transitive clo-
sure of - p satisfies the diamond property, and p is Church-Rosser.
3. The two reductions generated by {3fix and p commute. This is shown by
defining a parallel reduction relation Ip' and showing that -pOx and
Ip commute: see Lemma 22 for details.
The Church-Rosser Theorem for ia follows directly from the three lemmas by
the Hindley-Rosen Lemma [1, p. 64]. I
The second important property of the evaluator for IA is that it never gets
"stuck". That is, we can show that every program either goes into an infinite
loop or terminates giving a numeral. This result is the subject of the following
theorem. 4
Theorem 4 (Uniform Evaluation) For all programs M, either M -ia 'n', for
some n E 7L or, for all N such that M - ia N, there exists N' such that N - ia N'.
Proof: The result follows from two lemmas. The first (Lemma 5) shows that
reductions preserve types, and hence that a program always reduces to a pro-
gram. The proof of the second (Lemma 6) provides a characterization of cer-
tain normal forms, and in particular shows that a program in normal form
must be a numeral. I
Lemma 5 If M - ia N and IT t> M : T, then IT t> N : T.
Proof: By induction on the structure of M: The base cases are vacuously true.
The inductive cases require an easy application of the inductive hypothesis or
a careful inspection of the type of the contractum of a redex. See Appendix B,
Lemma 25 for details. I
4If IA contained integer division (or other partial primitive functions), then the calculus ia
would have to include error values and reductions for error values. The Uniform· Evaluation The·
orem would have to state that a program either reduces to a numeral or an error value.
Matthias Felleisen and Stephen Weeks 109

Lemma 6 If P is a program in normal form (that is to say, there is no p' such


that P - ia P'), then P = 'n' for some n E 71..
Proof: We prove the following more general lemma by induction on the struc-
ture of an arbitrary expression N:
if {(Xl. int ref), ... , (x n , int ref)} I> N: T,
and N is a normal form, then N E tl. the language described by the following
grammar:
'n' I op I (op 'n') I (op t2) I Xi I i\y.M I skip I t2
.. -
"- E[!xill E[Xi:= 'n'], where Xi i:TR(E)
The specific result follow by noting that a program M must satisfy 0 I> M : into
See Lemma 27 for details. I
Theorems 2 and 4, together with Theorem 3, show that the calculus defines
a well-behaved interpreter and that it is sound with respect to program equiV-
alences [2]. Also, the proofs of the preceding theorems and lemmas show that
the functional and imperative sub-languages are independent and that meta-
properties follow from modularized proofs. In the next section we will prove
that the evaluation itself can proceed in a highly modular fashion.

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,

eval(M) = 'n' iff for some program N, M -;Uix N -~ 'n'


Proof: The direction from right to left is trivial, the other direction is difficult.
We proceed as follows. The first step is to define a parallel reduction relation,
l p ' that contains - p and is contained in -~ (see Definition 21). This fol-
lows the method of Tait/L",f for the Church-Rosser Theorem. The second step
is to replace all - p steps with I.:P. steps in the reduction M -~ 'n', which
must exist since eval(M) = 'n'. The final step is to prove that we can push
all -Pfix to the left and all 1p to the right (see Appendix C, Lemma 29 for
details). The proof method is mspired by Plotkin's proof of standardization
for the untyped i\-calculus [14, p. 140]. I
According to this theorem, we may view compilation as reduction in the
functional fragment and execution as reduction in the imperative fragment.
However, Reynolds points out that recursion presents a major obstacle be-
cause
110 Chapter 5. Orthogonality of Assignments and Procedures in ALeoL

the reduction phase may go on forever, producing an infinite "head-


normal" term. Nevertheless, such an infinite term can still be viewed
as a simple imperative program; operationally, one simply implements
the two phases as coroutines. [18, p. 3]
Put differently, a compiler cannot know in advance how far to unroll a recur-
sive program. Hence, the compiler must unroll it all the way, which means
that the result is an infinite term. The important point is that although this
infinite term is in (an extended version of) the imperative language, it can still
be executed in the usual imperative reduction system.
In order to formalize these notions, we use the following strategy. First,
we extend the language of IA with an additional constant, n, denoting observ-
able nontermination and define the function [.]; from expressions in IA to
the extended language. The translation unrolls all recursive functions i times.
Second, we define W, a purely imperative sublanguage of extended IA that pre-
cisely characterizes the /3-normal-forms of unrolled programs. This imperative
sublanguage may be viewed as the target language of the compiler. Finally, we
prove a new Postponement theorem for the extended language.
We start with the definition of the extended expression language and the
"unrolling" translation.
Definition 8 The i'th unrolling of an IA expression M, notation: [M];, is de-
fined by induction on the structure of M:
[(recM)]; = (,[MM··,· ([M];,n)))
i times

[b]; = b [skip]; = skip


[op]; = op [if Ml M2 M3]/ = if [Md/ [Mz]/ [M3];
[x]; =x [begin Ml Mz]/ = begin [Md; [Mz];
[i\x.M]/ = i\x.[M]/ [new(x,M1).Mz]/ = new(x,[Md/).[Mz]/
[(MI M2)]/ = ([Mdl [M2]/) [!M]/ = ![M]/
[MI := Mz]/ = [Md/ := [Mz]/
We refer to i as the unrolling index. The constant n is of ground type; for
higher types, it is an abbreviation for i\x. n for an appropriate vector x of
variables.
Unrolling a program i times roughly corresponds to using the axiom (fix)
i times on each rec-expression. More precisely,
(rec M) -furl (M(· .. (M (rec M» ... )
\ T '

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

Definition 9 The relation!;;;; is defined as follows:


1. O!;;;;M
2. M!;;;;M
3. if M!;;;; M' and C!;;;; C then C[M) !;;;; C[M/)
For contexts, C !;;;; C' if C[n] !;;;; C[O).
Thus, unrolling a term L produces a term that is below some term L' that
results from L via fix.
Lemma 10 For all i E N, LElA, there exists a term L' such that
[L]i !;;;; L' and L -fix * L';
Proof: By induction on the structure of L. I
The target language of the "compiler" is the purely imperative sub-
language, W, which is essentially extended IA without recursion, A-abstraction,
or arbitrary function application.
Definition 11 The imperative sub-language W of extended IA is defined by the
following grammar:
t "= o I rn' I x I «op t) t)
I new (x, t). t I ! tit := t
I skip I if t ttl begin t t
Legal phrases of W are those that satisfy the type inference rules of Figure 1
with the additional constraint that 0 is of one of the groundtypes (0).
In order to show that W is the proper target language, we prove that the
result of p-reducing an unrolled program to p-normal-form is always in W.
Lemma 12 For all i E N, LElA, the p-normal-form o([L]/ is in W.

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

To prove that evalp is equivalent to the original evaluator, we must show


that both the functional and imperative reduction system respect the prefix
ordering, 1;;. If this holds, the ~-normal-forms of all finitely unrolled versions
of a program clearly form a totally ordered, infinite set of W programs, which
may be perceived as the infinite ~-normal-form of an IA program. If the im-
perative executions of all elements of the infinite term preserve the ordering,
and if one of the elements reduces to the correct final answer, then the two
evaluators indeed agree. We begin by proving that the functional reductions
preserve the approximation ordering.
Lemma 14 For all programs M and i E N,
the ~-normal-form of[M]/ I;; the ~-normal-form of [M]i+l
Proof: The result follows from Lemmas 32 and 33. The first one shows that
[M]/ I;; [M]I+l. The latter shows that ~ normalization respects 1;;. See Ap-
pendix C for details. I
Second, we prove that imperative reductions preserve approximations. In
particular, if a W program terminates, then all dominating programs do.
Lemma 15 ForallM,N E W, ifM I;; N andM~; r n" thenN~; r n ,.

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,

if and only if there exists some i and ~-normal-form M such that


[L]I -p M - ; r n,
If this claim is true, then, by Lemma 15, W contains at most one result, which
must be the result of the original program.
The right to left direction of the auxiliary claim is obvious. For the left to
right direction, by the Postponement Theorem 7, eval(L) = r n , implies that
there is a reduction:
L -Plix N - ; r n,.
Clearly, no rec-expression or A-expression in N is relevant to the reduction
from N to r n,. Hence we may replace all rec-expressions and A-expressions in
Matthias Felleisen and Stephen Weeks 113

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

new (x, t1). t2 E K t1 E new (x, .). t2 :: K


en' E new (x, .). t2 :: K t2 (x, en') ::E new (x, 'n') .• :: K
val p::E new (x, 'n') .• :: K val 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

begin t1 t2 E K t1 E begin. t2:: K


skip E begin • t2 :: K t2 E K

«(x, en') ::E)l(x, em') (x, 'm'):: E


«y, 'n') :: E)!(x, 'm') (y, 'n') :: (El (x, 'm')), where x f y

«x, 'n') :: E).x


«y, 'n') :: E).x E.x, where x f y

Figure 3: CEK Machine


Matthias Felleisen and Stephen Weeks us

strong-normalization results for two different systems.


Theorem 17 {3p is strongly-normalizing.
Proof: Since the combination of two strongly-normalizing systems that satisfy
the postponement property is not necessarily strongly-normalizing, we need
to prove a technical lemma that strengthens these properties. For our case,
the relevant properties are:
finite branching A one-step reduction relation is finitely branching if, for ev-
ery term, the set of terms reachable in one step is finite.
strong postponement If rl and rz satisfy postponement, they satisfy strong
postponement if M ~~lr2 M" implies that there exists M' such that
M ~~ M' ~:; M", and m + n ~ 1.6
Our main technical theorem (Theorem 38) is the following:
If rl and rz are strongly-normalizing and satisfy the finite-
branching property, and if rlrZ satisfies the strong-postponement
property with respect to rz, then rlrZ is strongly normalizing.
Given this theorem, all that remains to prove that {3p is strongly-normalizing is
to show that {3p satisfies the strong postponement property with respect to p
and that p is strongly-normalizing. It is obvious that both {3 and p satisfy finite
branching. The proof of the Postponement Theorem (7) is easily modified to
show strong postponement. The following lemma shows that p is strongly
normalizing. I
Lemma 18 p is strongly-normalizing.
Proof: We note that every p reduction removes at least one keyword, with
the exception of (Y, which replaces an assignment with a skip. Hence, any
reduction starting with a term t could not possibly have more that 2s + k
steps, where s is the number of assignments in t and k is the number of other
keywords in t. I

5 Extensions and Alternatives


The preceding analysis of a small, but prototypical version of ALGOL formal-
izes a number of folklore claims. First, it proves that the language's calculus
is indeed the extension of a term rewriting systems for a simple imperative
language with a typed A-calculus. Second, the combination is orthogonal in
the sense that major properties for the two sub-calculi are compatible and
hold for the entire system. Finally, the analysis confirms the idea that the
evaluation of ALGOL programs can be neatly separated into two phases.
An extension of our results to more expressive languages than IA is pos-
sible. The analysis obviously carries over to extensions of IA that include
different primitive data types (boolean, float, characters), complex data types
of ground types (strings, arrays, records), and intersection types of ground
types with coercions. A little extra care is needed to extend the results to a
6Postponement here refers to Theorem 7, not to Theorem 16.
116 Chapter 5. Orthogonality of Assignments and Procedures in ALGOL

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.

[4] Matthias Felleisen. The Calculi of Lambda-v-CS·Conversion: A Syntactic Theory


of Control and State in Imperative Higher-Order Programming Languages. PhD
thesis, Indiana University, 1987.
[5] Matthias Felleisen. On the expressive power of programming languages. Science
of Computer Programming, 17:35-75, 1991. Preliminary version in Neil Jones,
editor, Proceedings of the European Symposium on Programming, pages 134-
151. Lecture Notes in Computer SCience, 432. Springer Verlag, Berlin, 1990.
[6] Matthias Felleisen and Daniel P. Friedman. Control operators, the SECD-machine,
and the lambda-calculus. In M. Wirsing, editor, Formal Description of Pro-
gramming Concepts III, pages 193-217. Elsevier Science Publishers B.V. (North-
Holland), Amsterdam, 1986.
[7] Matthias Felleisen and Daniel P. Friedman. A syntactic theory of sequential state.
Theoretical Computer Science, 69(3):243-287, 1989. Preliminary version in Pro-
ceedings of the ACM Symposium on Principles of Programming Languages, pages
314-325,1987.
Matthias Felleisen and Stephen Weeks 117

[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

Appendix A Church-Rosser Theorem for ia


Let ~ p be the reflexive closure of -po We show that ~p satisfies the diamond
property, hence so does - p.
Lemma 19 IfL~ pM andL~ pN, then there existsK such thatM ~ pK andN ~ pK.

Proof: If L == M, take K == N. Otherwise, we must have L == C[r] -p C[c] == M,


where (r, c) E p. By symmetry, we may assume that L '" N. We thus have that L ==
C'[r'] -p C'[c'] == N, where (r', c') E p. If rand r' are in different sub-expressions
of L we may simply reduce r' in M, and r in N to produce K. If M == N we are done,
take K == M. Because the partition of an expression into an evaluation context and
a redex is unique, the only remaining case is that r is a sub-expression of r' or vice
versa. Without loss of generality, we may assume that r' is a sub-expression of r. We
proceed by cases on r:
1. r == (op 'n') 'm'), c == 'n op m'. Impossible, no sub-expression of r is a p
redex.
2. r == new(x, 'n'). v, c == v. Impossible, no subexpression of r is a p redex.
3. r == new(x, 'n').E[!x], c == new(x, 'n').E['n']. We have that N _
C[new(x, 'n').Nll, where E[!x] -p Nl. By Lemma 20, there is an evaluation
context E' such that Nl == E'[!x]. Take K == C[new(x, 'n').E'['n'lJ.
4. r == new(x, 'n' ).E[x := 'm'], c == new(x, 'm' ).E[skip]. Similar to case 3.
5. r == if '0' Ll L2, C == Ll. Either N == C[if '0' Nl L2] because Ll -p Nl, or
N == C[if '0' Ll N2]. In the first case take K == C[Nll, in the second K == C[Lll.
6. r == if 'n + l' Ll L2, C == L2. Similar to case 5.
7. r == begin skip Ll, c == Ll. Similar to case 5. I
Lemma 20 If E[z] -p N, where E is an evaluation context and z is a fresh variable,
then there is an evaluation context E' such that N == E' [z].
Proof: By induction on the structure of E:
1. E == [ ]. Vacuously true.
2. E == (op E") M) -p N. The outermost 8-redex cannot be reduced, since E"[z]
cannot be a numeral. Hence either N == (op NJl M) where E"[z] -p Nl, or
N == (op E"[z]) N2) where M -p N2. In the first case we may apply the
induction hypothesis to E" to conclude that there exists an evaluation context
E'" such that Nl == E'" [z]. We may then take E' == (op E"') M). In the second
case we may take E' == (op E") N2).
All other cases follow similarly, noting in each case that the outermost redex cannot be
reduced. Thus, after applying the induction hypothesis, the desired evaluation context
is easily constructed. I
Definition 21 Parallel imperative reduction is defined as follows:
1. MTpM
2. If M - p N, then M T p N
3. If C Tp C' and M Tp N, then C[M]Tp C'[N].
For contexts, C T pC' iff C[z]T pC' [z], where z is a fresh variable.
Lemma 22 If L -Pflx M and L -p N, then there exists K such that M -p K and
N -PflxK.
Matthias Felleisen and Stephen Weeks 119

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

(d) r==new(y,'n').E[y:= 'm'].Similartocase2c.


(e) r == if '0' MI M2 - p MI == e. By rule 3 in the definition of Ip' and
induction on the number of free x's in MI:
[N/x]r if '0' [N /X]MI [N /X]M2
Ip [N' /X]MI
[N' /x]c

<0 r == if 'n + l' MI M2. Similar to case 2e.


(g) r == begin skip MI. Similar to case 2e.
120 Chapter 5. Orthogonality of Assignments and Procedures in ALeoL

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

Appendix B Uniform Evaluation


Lemma 25 IfM -ia N and IT I> M: T, then IT I> N: T.

Proof: By induction on the structure of M and case analysis on M - ia N:


1. M '" b or op or x. Vacuously true, since M is not a redex.
2. M '" ?l.X.MI -ia ?l.X.NI '" N because MI -ia NI. We know that IT I> M: T-T'
because IT[X/TJ I> MI : T'. We may apply the induction hypothesis to MI to
conclude that IT[X/TJI> NI : T'. Hence IT I> N: T-T'.
3. M", (ree MI). We distinguish two subcases:
(a) N = (ree NI),MI -ia NI. Similar to case 2.
(b) N = (MI (ree MI». We know that IT I> M : T because IT I> MI : T-T.
Hence IT I> N: T.

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'.

Proof: By induction on the structure of M. I


Lemma 27 If {(Xl, i nt ref), ... , (xn, i nt ref)} I> N : T, and N is a normal form, then
N E t1. the language described by the following grammar:

t1 'n' I op I (op 'n') I (op t2) I Xi I ?l.y.M I skip I t2


t2 E[!XiJI E[Xi := 'n'J, where Xi if; IR(E)

Proof: By induction on the possible structure of the expression N:


1. N", 'n' or op. N E ti.
2. N '" x. Since {(XI. int ref), ... , (xn, int ref)} I> N: T, we must have that X '" Xi,
for some i. Hence N E tr.
3. N '" ?l.y.NI. N E ti.
4. N '" (ree NI). Impossible, since N would not be in normal form.
5. N", (NI N2). Apply the induction hypothesis to NI. Using Lemma 28 we may
conclude that there are only the following possibilities, since NI is of - type.
(a) NI '" ap. Apply the induction hypothesis to N2. Since N2 is of type int,
either N2 '" 'n' or N2 E t2. In either case, N E ni.
Matthias Felleisen and Stephen Weeks 121

(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

Lemma 30 For all programs M and integers i, the (3-normal-form of[M]; is in W.

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 structure of M: Write M == (Ml M2). Since Ml is of - type,


it must be one of: op, x, "x.Mi, (rec Mi), or (Mi Mi'). In the first case we are done.
The next three cases contradict that M is closed, in normal form, and contains no
rec-expressions, respectively. In the last case, we may apply the induction hypothesis
to Ml to conclude that Mi == op, in which case we are done, or Mi == (op Mi"), which
is impossible. I
Lemma 32 For all expressions M, [M]; £ [M]l+l.

Proof: By induction on the structure of M. I


Lemma 33 For all expressions M, N, not containing any rec-expressions, if M !;;; N then

the (3-normal-form of M !;;; the (3-normal-form of N

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'.

Proof: By induction on the structure of M. I


Matthias Felleisen and Stephen Weeks 123

Appendix D Strong Normalization


Initially, we meant to prove that the orthogonal combination of two strongly-
normalizing systems is strongly-normalizing by applying van Daalen's corresponding
theorem [20, p. 80]:
Theorem 3S Ifrl and r2 are strongly-normalizing and ifrlr2 satisfies the postpone-
ment property with respect to r2 then rlr2 is strongly-normalizing.

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

Theorem 38 If r} and r2 are strongly-normalizing and satisfy the finite branching


property, and ifr}r2 satisfies the strong-postponement property with respect to r2, then
r}r2 is strongly normalizing.
First, we need to state two obvious properties of our notion of reachability:
D.I Rr(M) = M u (UM'ER;(M) Rr(M')}.
D.2 Bounded reduction length implies strong normalization.
Based on these, we now can prove the necessary lemmas for Theorem 38.
Proposition 39 Strong normalization plus finite branching implies finite reachability.
Proof: We use infinite reachability and finite branching to generate an infinite reduc-
tion sequence. Let M be a term that violates finite reachability. By Observation D.I
and finite branching we may conclude that there exists an M' E R; (M) that violates
finite reachability. Apply a similar argument to M'. Continue. I
Proposition 40 Strong normalization plus finite reachability implies bounded reduc-
tion length.
Proof: In fact, Ilr(M) :s; IRr(M)I. Any longer reduction would necessarily repeat a
term, contradicting strong-normalization. I
And finally we are ready to prove our main theorem.
Proof: (Theorem 38) We show that r}r2 satisfies bounded reduction length, thus by
Observation D.2 is strongly-normalizing. By Propositions 39 and 40 we know that r}
and r2 satisfy bounded reduction length. We show that Ilr lr2 (M) :s; m' + n', where
m' = Ilrl (M), n' = max{llr2 (M') 1M' E Rrl (M)}. We note that n' is well-defined
because r} satisfies finite reachability (by Proposition 39) and r2 satisfies bounded
reduction length. Consider a reduction M -~lr2 M". Because r}r2 satisfies the strong
postponement property with respect to r2, there exists a reduction M -~ M' -~2 M"
with m + n ~ I. We know that m :s; m'. Since M' E Rrl (M) we also know that
n :s; Ilr2 (M') :s; n'. Hence 1 :s; m + n :s; m' + n'. I
Chapter 6
IDEALIZED ALGOL and its Specification Logic
John C. Reynolds

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

More surprisingly, since procedures introduce the phenomenon of inter-


ference (of which the simplest case is aliasing between variables), even spec-
ifications that do not refer to procedures overtly can still depend upon the
environment. For example,
{x = 3}y:= 4{x = 3}
(which is provable from Hoare's axiom of assignment and is universally true
in the absence of a procedure mechanism) depends upon whether x and y
are aliases, Le. whether they are mapped by the environment into the same
storage location, so that assignment to one will change the value of the other.
In fact, to obtain universal specifications about commands in ALGoL-like
languages, it is necessary to extend the language of specifications well beyond
Hoare triples. A simple example of a universal specification is
y # x => {x = 3}y := 4{x = 3} ,

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

binding can be encapsulated in the process of beta-reduction, which is appli-


cable to specifications as well as programs.
In this report we will avoid formulating the semantics of specifications.
Although a semiformal semantics is given in [Reynolds, 1981aj, there is some
doubt that it is the "right" semantics. As we will see in Section 11, there are
useful and apparently noncontradictory rules of inference that are not valid in
this semantics.
It should be emphasized that this is a preliminary report of ongoing re-
search. Both the programming language and logic described in it can be ex-
pected to change considerably in the future.

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

(bindable phrase type) ::= (ordinary phrase type)


I commlike I explike
(phrase type) ::= (bindable phrase type) I spec

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

T~ ::s; T1 and T2 ::s; T~ implies T1 T2 var ::s; T~ T~ var


T1 Tz var ::s; T1 ace

T1 T2 var ::s; T2 exp


o~ ::s; 0 1 and 02 ::s; O2 implies 0 1 ~ 02 ::s; O~ ~ O2

T ace ::s; eommlike


comm ::s; eommlike

02 ::s; eommlike implies 0 1 ~ O2 ::s; commlike


T exp ::s; explike
02 ::s; explike implies 0 1 ~ O2 ::s; explike.
John C. Reynolds 129

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'

(w, rr) ::= L when L E dom(rr) and rr(L) = W

(B2, rr) ::= WI - B2, rr) «BI , rr»


(BI - B2, rr) ::= lu: BI. (B2, [rr I L: Bll)

(p, rr) ::= ree (p - p, rr)

(p, rr) ::=.lp

(B, rr) ::= if (Boolean exp, rr) then (B, rr) else (B, rr)

(B2, rr) ::= let L be (B I , rr) in (B2, [rr I L: BI ])


W, rr) ::= letree L: p be (p, [rr I L: p]) in (B, [rr I L: p])
The first schema shows the purpose of the subtype relation. The second
shows that an identifier assigned some bindable phrase type can be used as a
phrase of that type.
The next two schemas describe procedure application (i.e. call) and lambda
abstraction. We limit ourselves to procedures with a single parameter, since
multiple parameters can be eliminated by Currying [Reynolds, 1981bl.
130 Chapter 6. IDEALIZED ALeoL and its Specification Logic

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

Finally, we give the syntax of specifications:


(spec, TT) ::= {(Boolean exp, TT)} (comm, TT) {(Boolean exp, TT)}
I {(Boolean exp, TT)}
I (commlike, TT) # (explike, TT)
I (spec, TT) & (spec, TT)
I (spec, TT) => (spec, TT)
I (V 1: w) (spec, [TT I t: w])
I gvT( (T T var, TT) )
A specification of the form {P} C {Q} is true if and only if, for all states of
the store in which P is true executing C either does not terminate or terminates
with a state for which Q is true. This differs from Hoare's definition [l) in being
implicitly quantified over states of the store but not over environments, since
in our formalism {P} C {Q}, like any other kind of specification, is a predicate
about environments.
A specification of the form {P} is true if and only if P is true for all states
of the store; in this case we say that P is a static assertion.
A specification of the form C # E is true if and only if C does not interfere
with E. When C is a command and E is an expression this means that for any
state of the store, executing C will not, at any time during the execution, affect
the value of E. The meaning of this specification for other command-like and
expression-like phrases will be clarified by equivalences in Sections 5 and 6.
The operators &, =>, and V denote conjunction, implication, and universal
quantification of specifications. (In contrast to [Reynolds, 19S1a), we do not
combine & and => into a single operation.) Notice that quantification is over
meanings of a particular phrase type (not values of a particular data type).
A specification of the form gvT(V) means that V is a "good" variable, i.e. a
variable that will always possess the last value assigned to it. This will be
clarified by an equivalence in Section 7.
In conclusion, we state without proof a global property of our syntax. If
TT' ~ TT and;y' ~ ;y' then (;y, TT) ~ (;y', TT'). (When a phrase class name (;y, TT)
is used as a set, it denotes the set of all phrases derivable from (;y, TT).) When
TT' = TT this is an immediate consequence of the production schema describing
the subtype relation; however, the general case requires a structural induction
argument involving the entire syntax.

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

Although types can be broadened, they cannot always be "narrowed." For


example,
k > 4 =Boolean expo [k: integer exp] k ~ 5
is true, but
k > 4 =Boolean expo [k: real exp] k ~ 5
is false.
However, there is a special case in which type assignments can be nar-
rowed: if P =y.TT Q holds and rr' is a restriction of rr whose domain includes
all identifiers that occur free in P or Q, then P =y.TT' Q.
We will usually describe sets of equivalences schematically. For example,
to express the validity of beta reduction, suppose P E (82, [rr I t: 811) and
Q E (8 1, rr). Then
(3.1)
Here Plt-Q denotes the result of substituting Q for the free occurrences of 1
in P, with appropriate renaming of bound identifiers in P. More generally,
if llo ... , In are distinct identifiers, P E (y, [rr I ll: WI I··· I In: w n ]), and
Ql E (WI, rr), ... ,Qn E (w n , rr), then

Pltl ..... tn-Ql ..... Qn

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 E (8, rr) then


(3.4)
On the other hand,
At: 8 . ..l. p =IJ-P.TT ..l.1J-p • (3.5)
If P E (Boolean exp, rr), Qlo Q2 E (81 - 82, rr), and R E (810 rr), then
(if P then Ql else Q2)(R) = 1J 2. TT if P then Ql (R) else Q2(R) . (3.6)
On the other hand, if P E (Boolean exp, rr), Qlo Q2 E (82, [rr I t: 811) and
1 ([ dom(rr), then

At: 8 1• if P then Ql else Q2 =1- 2.


1J 1J TT if P then Al: 8 1. Ql else At: 8 1• Q2 . (3.7)
The let and letree constructions are described by equivalences that can be
regarded as aefinitions of these constructions in terms of application, abstrac-
tion, and ree. If P E (8 1, rr) and Q E (82, [rr I t: 811) then
let 1 be Pin Q = 1J 2.TT (At: 8 1• Q) (P) . (3.8)
134 Chapter 6. IDEAlizED ALGOL and its Specification Logic

If P E (p, [rr I L:pJ) and Q E (8, [rr I L:p]) then


letrec L:p be P in Q =e,1T (i\L:p.Q)(rec i\L:p.P) . (3,9)

Next, we give more specific equivalences describing commands. Suppose C,


C', C" E (comm, rr), P E (Boolean exp, rr), A, A' E (T ace, rr), and E,
E' E (T exp, rr). Then
C; skip =comm,1T C (3.10)

skip; C =comm,1T C (3.11)

C; (C' ; C") =comm,1T (C; C'); C" (3.12)

A := if P then E else E' =comm,1T if P then A := E else A := E' (3.13)

(if P then A else A') := E =comm,1T if P then A := E else A' := E (3.14)

(if P then C else C'); C" =comm,1T if P then (C; C") else (C'; C") (3.15)

while P do C =comm,1T if P then (C; while P do C) else skip. (3.16)

There are also obvious equivalences describing the conjunction and impli-
cation of specifications. Suppose S, S' ,S" E (spec, rr). Then

{true} & S =spec,1T S (3.17)

{false} & S =spec,1T {false} (3.18)

(S & S') & S" =spec.1T S & (S' & S") (3.19)

S & S' =spec,1T S' & S (3.20)

S&S =spec.1T S (3.21)

{true} => S =spec.1T S (3.22)

{false} => S =spec,1T {true} (3.23)

S => {true} =spec.1T {true} (3.24)

S => S =spec.1T {true} (3.25)

S => (S' => S") =spec.1T S & S' => S" (3.26)

S => S' & S" =spec,1T (S => S') & (S => S"). (3.27)

Finally, there are an indefinite number of equivalences of the form

P =Booiean exp,1T true (3.28)

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 Universal Specifications and Their Inference


Roughly speaking, a specification is said to be universal if it is true for all en-
vironments. However, much as with equivalences, the situation is complicated
by the fact that a specification can belong to (spec, TT) for several type assign-
ments TT, and that its universality can depend upon TT. To be precise, when
5 E (spec, TT) and the semantics of 5 for (spec, TT) maps all environments in
its domain into true, we write 5 :: TT and say that 5 is universal for TT.
For example,
{k > 4} => {k ~ 5} :: [k:integer exp] ,
but not
{k > 4} => {k ~ 5} :: [k:real exp] .
Specification logic is a formal system for inferring universal specifications.
Its rules of inference are schemas with the form "If 51 :: TTl and ... and
5 n :: TTn then 5 :: TT," usually prefaced by restrictions on metavariables.
The n ~ 0 specifications 51, ... , 5 n are called premisses and 5 is called the
conclusion.
Our first rule of inference captures the idea that a specification will remain
universal when any subphrase is replaced by another subphrase to which it is
equivalent. To state this preCisely, we must characterize "subphrase" syntac-
tically:
(4.1) Inference by Equivalence. Suppose 5 E (spec, TT),P =y,TT' Q, and there
is a derivation of 5 from (spec, TT) containing a subderivation of P from
(y,TT'). Let 5' be the specification derived from (spec, TT) by the deriva-
tion obtained from the derivation of 5 by replacing the subderivation of
P by a sub derivation of Q. If
5 :: TT
then
5' :: TT.
Next, we have two rules for broadening and narrOwing type assignments
in much the same way as for equivalences:
(4.2) Broadening Type Assignments. If 5 E (spec, TT) and TT' :5 TT and
5 :: TT
then
5 :: TT' .
(4.3) Narrowing Type Assignments. If 5 E (spec,TT) is a restriction of TT whose
domain includes all identifiers occurring free in 5, and
5 :: TT
then
5 :: TT' .
Three more rules are needed to capture the properties of & and =>. In
these rules we assume Sl,S2,S3 E (spec, TT):
136 Chapter 6. IDEALIZED ALeoL and its Specification Logic

(4.4) Truth.
{true} :: rr .
(4.5) Adding Assumptions. If

then

(4.6) Modus Ponens. If

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

{true} ~ (VLl:wd· .. (VL,,:W,,)S :: rr.

Then (4.8), with modus ponens, gives


{true} ~ SIIl ..... ln-P!. ....Pn :: rr,
and equivalence (3.22), via (4.1), gives the conclusion
SI11 ..... ln-Plo ....Pn :: rr .
John C. Reynolds 137

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.

Finally, we give a collection of rules which contain neither premisses nor


metavariables, and are therefore simply ax.ioms, i.e. particular specifications
that are universal. The first of these rules serves to "lift" reasoning about
assertions into reasoning about specifications. The next eight rules are refor-
mulations of various aspects of Hoare's logic [I], and the final rule says that a
command will preserve an assertion that it does not interfere with. For these
rules we assume 7T is a type assignment mapping p, q, r, i, PI, P2, ql, q2
and f into Boolean exp, and c, Cl, and C2 into comm:
(4.11) Static Implication.
{p} & {p implies q} ~ {q} :: 7T.

(4.12) Command Composition.


{p} Cl {q} & {q} C2 {r} ~ {p} Cl; C2 {r} .. 7T.

(4.13) Strengthening Precedent.


{p implies q} & {q} C {r} ~ {p} c {r} :: 7T.

(4.14) Weakening Consequent.


{p} c {q} & {q implies r} ~ {p} c {r} :: 7T.

(4.15) while Command.


{i and f} c {i} ~ Ii} while f do c {i and .f} :: 7T.

(4.16) Conditional Command.


{p and f} Cl {q} & {p and .f} C2 {q}
~ {p} if f then Cl else C2 {q} :: 7T.

(4.17) skip Command.


{p} skip {p} :: 7T.
(4.18) Specification Conjunction.
{pd c {qd & {P2} c {q2} ~ {PI and P2} c {ql and q2} :: 7T.
(4.19) Specification Disjunction.
{pd c {qd & {P2} c {q2} ~ {PI or P2} c {ql or q2} :: 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

5 Leftside Noninterference Decomposition


In this section and the next, we develop equivalences and inference rules de-
scribing noninterference. The overall effect of these rules is to allow any non-
interference specification C # E to be decomposed into nOninterference spec-
ifications in which only identifiers occur on the leftside, or rightside, of #. In
this section, we deal with leftside decomposition.
To begin, several equivalences serve to define noninterference for accep-
tors and proper procedures in terms of noninterference for commands. If
A E (T acc, IT),EE (explike, IT), and L ft dom(IT), then

A # E =spec,TT ('V L: T exp) (A := L # E) . (5.1)


If 0 1 i commlike, O2 :::; commlike, H E (0 1 - 02, IT), E E (explike, IT), and
L ft dom(IT), then
H # E =spec,TT ('V L: Od H(L) # E . (5.2)
If 0 1 :::; commlike, O2 :::; commlike, H E (0 1 - O2 , IT), E E (explike, IT), and
L ft dom(IT), then

H # E =spec,TT ('V L: Od (L # E => H(L) # E) . (5.3)


In addition, there are basic leftside decomposition rules for a variety of con-
structs in the language:
(5.4) If P E (p - p, IT) and E E (explike, IT) then

P#E=>recP#E" IT.

(5.5) If E E (explike, IT) then


.Lp # 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.7) If E E (explike, IT) then


skip # E :: IT.
(5.8) If C}, C2 E (comm, IT) and E E (explike, IT) then

Cl # E & C2 # E => Cl; C2 # E :: IT.

(5.9) If B E (Boolean exp, IT), C E (comm, IT), and E E (explike, IT) then

C#E => whileBdoC#E:: IT.

(5.10) If C E (comm, [IT I L: T T var]) and E E (explike, IT) and L ft dom(IT)


then
('VL:TTvar)(L#E=>C#E) => (newTvarLinC)#E:: IT.
John C. Reynolds 139

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).

(c) If C is '\1: e. CI then


fcornmlike(C) = fcommlike(CIl - {t} .
(d) If C is ree CI then
fcornmlike(C) = fcornmlike(CIl .
(e) If C is .Lp then
fcornmlike ( C) = {} .
(f) If C is if B then CI else C2 then
fcornmlike ( C) = fcornmlike ( C I) U fcommlike ( C2) .

(g) If C is let t be C I in C2 then there must be at least one pair eI.


e2 of phrase types such that e2 ::; eommlike, CI E (el.rr), and
C2 E W2, [rr I t: ell).

(i) If there is any such el for which e l i eommlike then


fcornmlike(C) = fcommlike(C2) - {t} .
(ii) Otherwise,
fcornmlike (C) = fcornmlike ( C Il u (fcommlike ( C 2) - {t}) .
(h) If C is lettee t: p be CI in C2 then
fcornmlike ( C) = (fcornmlike ( C Il U fcornmlike ( C2 » - {t} .
(i) If C is skip then
fcornmlike ( C) = {} .

(j) If C is CI := C2 then

fcornmlike (C) = fcornmlike ( C I) .


140 Chapter 6. IDEAliZED ALGOL and its Specification Logic

(k) If C is C1; Cz then


Fcommlike ( C) = Fcommlike ( Cd U Fcommlike ( C z) .

(l) If C is while B do CI then


Fcommlike ( C) = Fcommlike ( Cd.

(m) If C is new T var Lin CI then


Fcommlike(C) = Fcommlike(Cd - {L} .

This definition is the key to the following derived rule of inference:


(5.11) Leftside Noninterference Decomposition. Suppose C E (commlike, rr),
E E (explike, rr), and Fcommlike(C) = {LI, ... , Ln}. Then

L1 # E & ... & Ln # E = C # E :: rr.

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

L1 # E & ... & Ln # E = C1 # E :: rr.


But equivalence (5.2) gives
C1 # E = (YC8r)CI{t) # E :: rr,
where L is some identifier not in dom(rr), and quantifier removal
gives
(YC8r)C1(L)#E=C1(CZ)#E:: rr.
Thus by modus ponens,
L1#E&···&L n #E=C1(Cz)#E:: rr.
(ti) Otherwise, Fcommlike(Cd <;; Fcommlike(C) and Fcommlike(CZ) <;;
Fcommlike(C), so that the induction hypothesis gives (with added
assumptions)
L1 # E & ... & Ln # E = C1 # E :: rr ,
LI # E & ... & Ln # E = Cz # E :: rr .
John C. Reynolds 141

But equivalence (5.3) gives


C1#E=(VL:Od(L#E=CdL)#E):: rr,

where L is some identifier not in dom(rr), and quantifier removal


gives
(YL:Od(L#E = CdL) # E) = (C2 # E = C1 (C2) # E) :: rr.

Thus

(c) If C is.\L: O,C1 then


eommlike then FcommIike(Cll '" FcommIike(C), since L cannot
(i) If 0 :I;
belong to FcommIike(Cll, so that the induction hypothesis gives
L1#E&"'&Ln #E=C1#E:: [rrlcO].

Then quantifier introduction and reverse beta reduction give


L1 # E &. . . & Ln # E = (Y cO) (A cO. C1)( L) # E :: rr
since L occurs bound in C and therefore does not belong to
dom(rr). But equivalence (5.2) gives
(VcO)(.\L:O.Cll(L)#E = (.\L:O.Cll#E :: rr.
Thus
L1 # E & ... & Ln # E = (.\L: O. Cll # E :: rr .
(ti) If 0 ~ eommlike then FcommIike(Cll ~ FcommIike(C) U {d, so that
the induction hypothesis gives
L1 # E & ... & Ln # E & L # E =C # E :: [rr I cO].
1

Then quantifier introduction and reverse beta reduction give


L1#E&"'&L n #E =(YcO)(L#E=(ACO.C1 )(L)#E):: rr

since L occurs bound in C and therefore does not belong to


dom(rr). But equivalence (5.3) gives
(VL: O)(L # E = (.\L: O.C1)(L) # E) = (AC O.Cll # E .. rr.
Thus
L1 # E & ... & Ln # E = (.\L: O. Cll # E :: rr.

(d) If C is ree C1 then FcommIike(C1) = FcommIike(C) , so that the induction


hypothesis gives
L1 # E & ... & Ln # E = C1 # E :: rr.
Then rule (5.4) gives
C1 # E = ree C1 # E :: rr,
and the result follows by modus ponens.
142 Chapter 6. lDEAUZED ALGOL and its Specification Logic

(e) If C is .Lp then Fcommllke(C) = {}, so that the specification to be proved


is
.Lp # E :: 7T,
which follows from rule (5.5).
(f) If C is if CI then C2 else C3 then Fcommllke(C2) ~ Fcommllke(C) and
Fcommllke(C3) ~ Fcommllke(C), so that the induction hypothesis gives
LI # E & ... & Ln # E ~ C2 # E :: 7T,

LI # E & ... & Ln # E ~ C3 # E :: 7T.


Then rule (5.6) gives
C2 # E & C3 # E ~ if CI then C2 else C3 # E :: 7T ,

and the result follows by modus ponens.


(g) If C is let L be CI in C2 then there must be at least one pair
01, 02 of phrase types such that 02 !> commlike, C1 E (01. 7T), and
C2 E (02, [7T I L: OIl).
(i) If there is any such 01 for which 01 i;. commlike, then
Fcommllke(C) = Fcommllke(C2) - {L} = Fcommllke('\L: 0 1• C2) =
Fcommllke«'\L: OI.C2)(CI»). Then the argument for (b) and (c) shows
that
LI # E & ... & Ln #E ~ (.\L:OI. C2)(CIl #E :: 7T,
and the result follows from the defining equivalence (3.8) for let.
(ti) Otherwise, Fcommllke (C) = Fcommllke ( CIl u (Fcommllke (C2) - {L}) =
Fcommllke(CI) U Fcommllke('\L:OI.C2) = Fcommllke(M:OI.C2)(CIl).
Again the argument for (b) and (c) shows that
LI #E & ... & Ln # E ~ (.\L:OI. C2)(CIl #E :: 7T,

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 •

But equivalence (5.1) gives


CI # E ~ (V L: T exp) (CI := L # E) .. 7T,
John C. Reynolds 143

where l is some identifier not in dom(TT), and quantifier removal gives


(Vl:Texp)(Cl:=l#E) ~Cl:=C2#E:: TT.
The result follows by modus ponens.
(k) If C is Cl; C2 then the result follows from rule (5.8) by an argument
similar to (f).
(1) If C is while Cl do C2 then the result follows from rule (5.9) by an argu-
ment similar to (d).
(m) If C is new T var l in Cl, then fcommlike(Cl) ~ fcommlike(C) U {l}, so that
the induction hypothesis gives
ll#E&···&ln#E&l#E~Cl#E:: [TTll:TTvar].
Then quantifier introduction gives
II # E & ... & In # E ~ (V l: T T var) (l # E ~ Cl # E) :: TT
since l occurs bound in C and therefore does not belong to dom(TT). But
rule (5.10) gives
(VCT T var)(l # E ~ Cl #E) ~ (newT var l in Cl) # E :: TT,
so that the result follows by modus ponens.

6 Rightside Noninterference Decomposition


The development of rightside decomposition parallels that of the previous sec-
tion. We begin with equivalences that define noninterference for function pro-
cedures. If lh i;. explike, (h ~ explike, C E (commlike, TT), H E (lh - (h, TT),
and l ([ dom(TT), then
C#H =spec,7T (Vl:9dC#H(l). (6.1)

On the other hand, if 91 ~ explike, 92 ~ explike, C E (commlike, TT),


HE (91 - 9 2, TT), and l ([ dom(TT), then
C # H =spec,7T (V l: 9 1 )(C # l ~ C # H(l») . (6.2)

Next we give basic decomposition rules for a variety of constructs in the


language:
(6.3) If 9 ~ explike, C E (commlike, TT), B E (Boolean exp, TT), and Elo
E2 E (9, TT), then
C # B & C # El & C # E2 ~ C # if B then El else E2 :: TT.
(6.4) If C E (commlike, TT) then
C#O:: TT,
C#1::TT,
C # 0.5 :: TT,
C#true :: TT.
144 Chapter 6. IDEALIZED ALeoL and its Specification Logic

(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.

Then we define, for any expression-like phrase E, a subset fexplike(E) of the


identifiers occurring free in E. Let E E (explike, IT). Then
fexplike(E) ~ {t I t E dom(IT) and IT{t) ~ explike}
is the set of identifiers such that:
(a) If E is an identifier t then
fexplike(E) = It}.
(b) If E is EdE2) then there must be one or more pairs aI, 02 of phrase types
such that 02 ~ explike, El E (0 1 ~ 02, IT), and E2 E (Or. IT).

(i) If there is any such 0 1 for which 0 1 i explike then


fexplike (E) fexplike(El} .
(ti) Otherwise,
fexplike (E)
(c) If E is At: O.El then
fexplike (E) = fexplike (E 1) - {t} .
(d) If E is if B then E1 else E2 then
fexplike(E) = fexplike(B) u fexplike(Erl u fexplike(E2).
(e) If E is let t be El in E2 then there must be at least one pair
Or. 02 of phrase types such that 02 ~ explike, E1 E (aI, IT), and
E2 E (02, [IT I 1: ad).
(i) If there is any such 0 1 for which 0 1 i explike then
fexplike(E) fexplike(E2) - {t}.
(ti) Otherwise,
fexplike(E) fexplike(Erl u (fexplike(E2) - {t}).
(f) If E is 0, I, 0.5, or true, then

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

(h) IfEis (VL)E l then


fexplike{E) = fexplike{Ed - {L} .
Finally, we give a general derived rule for rightside decomposition:
(6.7) Rightside Noninterference Decomposition. Suppose C E (commlike, rr),
EE (explike,rr),and fexplike{E) = {Ll, ... ,L n }. Then

C # Ll & ... & C # Ln = C # E :: rr.


We omit the proof, which is similar in style and tedium to that in the previous
section.

7 Assignment and Simple Variable Declaration


To treat assignment, we begin with an equivalence that defines "good variable"
specifications: If V E (T T var, rr) and Land L' are distinct identifiers not
belonging to dom{rr), then
gvT{V) "'spec,IT (VL:Texp)(YL':Texp -Booleanexp) (7.l)
{V#L' = {L'{L)}V:=L{L'{V)}).

From this equivalence it is straightforward to derive an inference rule for


assignment statements:
(7.2) Assignment. Suppose V E (T T var, rr), E E (T exp, rr), P E
(Boolean exp, [rr I L: T exp]), and fexplike{P) - {L} = {Ll,"" Ln}. By
quantifier removal, using the substitution L, L' - E, At: T exp.P, we have
(V t: T exp) (Y L': T exp - Boolean exp)
{V#L' =
{t'{L)}V:=L{L'{V)})
= {V # i\t: T exp.P
= {(i\L:T exp.P){E)} V:= E {(i\L:T exp.P){V)}) :: rr.
Next beta reduction and rightside noninterference decomposition give
(V t: T exp) (V L': T exp - Boolean exp)
{V # L' = {L'{L)} V:= L {L'(V)})
= {V#Ll & .. ,&V#Ln = {Pll-d V:=E {Pit-V}) :: rr

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

(7.3) Simple Variable Declaration Axiom. Let rr be a type assignment that


maps e1, ..• ,em into explike, C1, ... , Cn into commlike, p, q into Boolean
exp, and h into T T var - comm. Then
(VX:T Tvar) (gvT(X) &x#e1 & .. ·&x#e m &C1 #x&·· ·&cn #x
=> {p} hex) {q})
=>{p} new T var x in hex) {q} :: rr.

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].

By quantifier introduction we have


S => (VL:T T var)
gvT(t) & L # E1 & ... & L # Em & C1 # L & ... & Cn # L
=>{P} B {Q} :: rr.

In Axiom (7.3) we rename x to be L (which can be assumed without loss of


generality to be distinct from the identifiers occurring free in the axiom),
and use the free substitution

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

produces a state in which


(if m = 1 then m else n) = 2 .
Thus gvinteger(if m = 1 then m else n) is false. (Another example of a bad
variable, discussed in [Reynolds, 1981al, is a self-subscripting array such as
X(X(l).)
The seriousness of the bad variable problem arises from the fact that such
a variable can be used as an actual parameter in a procedure call, where call
by name implies that its abnormal behavior is inherited by the corresponding
formal parameter.

8 let Commands and Function Procedures


We now consider the let construction, which can be used to define non-
recursive procedures. First we derive a rule for commands of the form
let L be M in B.
(8.1) let Commands. Suppose
L rt dom(TT)
S E (spec, TT)
Sproc E (spec, [TT I L: OJ)
P, Q E (Boolean exp, TT)
B E (comm, [TT I L: OJ)
ME (0, TT)
are such that
S => (Sprocl,-M) :: TT, (a)
S & Sproc => {P} B {Q} :: [TT I L: OJ . (b)

From (b), reverse beta reduction gives


S & Sproc => {P} (.\L: O. B)(L) {Q} .. [TT I L: OJ ,
and free substitution gives
S& (Sprocl'-M) => {P} (.\L:O.B)(M) {Q} :: TT
since L does not occur free in S, P, Q, or the lambda expreSSion. Then
modus ponens with (a) gives
S => {P} (.\t: O.B)(M) {Q} :: TT,
and the equivalence (3.8) defining let gives the conclusion
S ={P}letLbeMinB{Q}:: TT.
From this rule, one can derive more specific rules for nonrecursive proper
and function procedure declarations. We limit our discussion to the latter,
since the former is less interesting than the recursive case that will be consid-
ered later. For function procedures, however, only the nonrecursive case can
be treated by specification logic. The following is a derivation of a rule similar
to (R36') in [Reynolds, 1981al. (The notation TT1S denotes the restriction of TT
to S.)
148 Chapter 6. IDEALIZED ALGOL and its Specification Logic

(8.2) Nonrecursive Function Procedure Declaration. Suppose (for some n <': 0)

() = (}I ~ ( . . . ~ (en ~ T exp) ... )


L, LI, ••• , Ln are distinct identifiers
L (£ dom(rr)
rr' = rrl(dom(rr) - {LI, ... , Ln})
rr" = [rr' ILl: (}I I ... I Ln: (}n]
S E (spec, rr)
P, Q E (Boolean exp, rr)
Bproe E (T exp, rr")
BE (comm, [rr I t: ()])
{L~', •.. , L;;'} = fexplike(Bproe ) - {LI. ... , Ln} ~ dom(rr')
Le (£ {L} U dom(rr')
Sproe (V LI: (}d ... (V Ln: (}n) {L(Ld ... (Ln) = Bprocl
& (Y Le: commlike) (Le # L~ & . . . & Le # L~ = Le # L)
E (spec, [rr' , t: ()]) ,

and that

S&Sproe = {P} B {Q} :: [rr' t:e]. (a)

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

Le # L~' & . . . & Le # l;;' = Le # All: e l •... ALn: (}n. Bproe


:: [rr' , Le: commlike]
is a consequence of rightside noninterference decomposition and
fexplike(ALI:e I •... .\In: (}n.Bproe) = {L~, ••. ,L;;'}. Broadening rr' to rr and
adding the assumption S gives

(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.

(2) If L rt F+ (S)then the meaning of S, ordered by false r;; true, is continuous


in the meaning of L.
The asymmetriC equations for quantified specifications reflect the fact that
universal quantification preserves the first kind of continuity but not the sec-
ond.
Consider FE (p - p, rr). Suppose some property holds for 1. p and, when-
ever this property holds for L, it holds for F(L). Then the property holds for
each member of the sequence 1. p , F(1. p ), F(F(1. p », .... Moreover, ree F can
be regarded as the limit of this sequence, so that if the property is appropri-
ately continuous-in the sense of (1) above-it will hold for rec F.
This reasoning is formalized by the following rule for rec, in which the
property is expressed by a specification S regarded as a function of t:
(9.2) Recursion. Suppose FE (p - p, rr), S E (spec, [rr I t:p]), L rt dom(rr),
and L rt F- (S). Then
(SIL-.lp) & (Yt:p)(S = (SIL-F(L)) = (SIL-recF) :: rr.

It is illuminating to give an example showing the necessity of the con-


dition L rt F- (S). Such an example can be developed from the fact
that certain recursive procedures always terminate even though none of
their "finite approximations" always terminate. Under the type assignment
[p:integer exp - eomm I x: integer exp]:
150 Chapter 6. IDEAllZED ALGOL and its Specification Logic

{true}p(x){false} specifies p does not terminate for x.


{true} p(x){false} ~ {false} specifies that p terminates for x.
('Ix: integer exp) ({true}p(x){false} ~ {false}) specifies that p always ter-
minates.
S = ('Ix: integer exp) ({true}p(x){false} ~ {false}) ~ {false} specifies that
p does not always terminate.
Even though 5 holds for all finite approximations to p, it may fail to hold for
the limit. This is possible since p E F- (5) and, in fact, the meaning of 5,
ordered by true!;; false, is not continuous in the meaning of p.
In formalizing this example, it will be convenient to use the following de-
rived rule about ~:
(9.3) Reversal. Suppose 51. 52, 53 E (spec, IT) and

51 ~ 52 :: IT.

From (3.25)
(52 ~ 53) ~ (52 ~ 53) .. IT,
so by (3.26) and (3.20)
52 ~«52 ~53) ~53)" IT,

and by modus ponens


51 ~ «52 ~53) ~53) .. IT.

Then (3.26) and (3.20) give the conclusion


(52 ~53) ~ (51 ~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)

{true}p(x){false} ~ {true}p«x + 1) -1){false} :: IT (4.1)

{true}p(x){false} ~ {ox + 1 = O} p«x + 1) - l){false} ., IT (4.13)

{true}p(x){false} ~ {true}F(p)(x + l){false} :: IT (4.16, 3.1)


({true}F(p)(x + l){false} ~ {false})
~({true}p(x){false} ~ {false}) :: IT (9.3)
John C. Reynolds 151

('tx:integer exp) ({true} F (p)(x){false} => {false})


=>({true}F(p)(x + l){false} => {false}) :: rr (4.8)

('tx:integer exp)( {true}F(p)(x){false} => {false})


=>({true}p(x){false} => {false}) :: rr (4.6)

('tx:integer exp)({true}F(p)(x){false} => {false})


=> ('tx: integer exp) ({true}p(x){false} => {false})
:: [p:integer exp - comm) (4.7)

S => (Slp-F(P)) :: [p:integerexp -comm) (9.3)

('tp:integer exp - comm)(S => (Slp-F(p)) .. [) (4.7)

{true} .leomm false} :: [) (9.1)

{true} .linteger exp-eomm (x){false} :: [x: integer exp) (3.4)

('tx:integer exp)( {true} .lintegerexp-eomm (x){false} => {false})


=> ({true} .linteger exp-eomm (x) {false} => {false})
:: [x: integer exp) (4.8)

{true} .llntegerexp-eomm (x){false} => (SI P -J.lntegerexp-comm)


:: [x: integer exp) (3.26, 3.20)
SI P -J.lntegerexp-comm :: [x: integer exp) (4.6)

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.

10 letrec Commands and Proper Procedures


From the rules in the previous section, we can derive a rule for letrec com-
mands and a more specific rule for proper procedure declarations.
(10.1) letrec Commands. Suppose
L rt dom(rr)
S E (spec, rr)
Sproe E (spec, [rr I L:p»
P, Q E (Boolean exp, rr)
BE (comm,[rr I L:p)
ME (p, [rr I L:p)
L rt f- (Sproe>
152 Chapter 6. IDEALIZED ALGOL and its Specification Logic

are such that


S => (Sproc I,-.Lp) :: rr (a)
S &Sproc => (Sprocl'-M) :: [rr 1 ( : p] (b)
S&Sproc => {P} B {Q} :: [rr 1 (:p]. (c)

From (b), by backwards beta reduction, we have


S&Sproc => (Sprocl'-('\':P.M)(L)):: [rrIL:p),
so that introducing a quantifier gives
S => (V(:p)(Sproc => (Sprocl,-('\t:p.M)(L)) :: rr. (d)

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 ,

which, by modus ponens with (a) and (d), gives


S => (Sproc 1,-ree ,\,:p.M) :: rr. (e)

By backwards beta reduction in (c), we have


S&Sproc => {P} (?u:p.B)(L) {Q} :: [rr 1 t:p].
Then, since ( does not occur free in S, P, At: p.B, or Q, free substitution
gives
S & (Sprocl,-ree .\t:p.M)
=> {P} (At:p.B)(rec ?u:p.M) {Q} :: rr,
and modus ponens with (e), along with the equivalence (3.9) defining
letrec, gives the conclusion
S => {P} letrec t:p be M inB {Q} :: rr.
From (10.1) we can derive the following more specific rule, which is similar to
(R26) in [Reynolds, 1981a]:
(10.2) Proper Procedure Declaration. Suppose that (for some n, k ~ 0)
P = (0 1 - •.. (0 11 - comm) ... )
(, LIt .•• , (11, L~, ... , (" are distinct identifiers
( fl dom(rr)
rr' = rrl(dom(rr) - {Ll""(I1,(~, ... (D)
rr" = [rr' 1 (1: 01 I· . ·1 (11: 0 11 ]
rr'" = [rr" 1L~:Oi 1···1 L,,:O,,]
SI E (spec, rr')
Spa E (spec, rr''')
S2 E (spec, rr)
Pproc , Qproc E (Boolean exp, rr''')
John C. Reynolds 153

P, Q E (Boolean exp, rr)


Bproc E (comm, [rr" I t: p])
B E (comm, [rr I t: p] )
{l~, ... ,l~} = Fcommlike(Bproc)-{L,LI. ... ,ln} ~dom(rr')
le f/; {l} U dom(rr')
Sproc ('1 L1: Od ... ('tf In: On)( 'tf l~: O;J ... ('tf lk: Ok)
(Spa = {Pprocl dll)··· (Ln) {Qproc})
& ('1 le: explike) (t~ # le & ... & l~ # le = l # le)
E (spec, [rr' I t: p])
and that
Sl & Sproc & Spa = {Pprocl Bproc {Qprocl :: [rr'" I t: p] (a)
S2 &Sproc = {P} B {Q} :: [rr I t:p]. (b)

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) .

By introducing quantifiers into (a), we have


Sl &Sproc = ('tfLl:Od··· ('tfln:On)('tfL~:OD··· ('ILk: Ok)
(Spa = {Pproc } Bproc {Qprocl) :: [rr' I t:p] . (c)
Since Fcommlike(All: 0 1 ••.. ALn: On. Bproc) ~ {L~', ••. , L~, l}, leftside nonin-
terference decomposition gives
l~ # le & ... & L~ # le & l # Le
=All:Ol •... Aln:On.Bproc#le·· [rr' I t:p I Le:explike],
but by quantifier removal
Sproc = (l~ # le & ... & l~ # le = l # t e ) .. [rr' It:p Ile:explike],
so that modus ponens gives
Sproc & l~ # le & ... & l~ # le
=All:Ol .... ALn:On.Bproc#Le .. [rr' Il:P I Le:explike],
and quantifier introduction gives
Sproc = ('tfLe:explike)(L~#Le&·· ·&l~#le
= ALl:Ol •... Aln:On.Bproc # le) •. [rr' Il:p]. (d)
Then modus ponens of (c) and (d) gives
Sl &Sproc = (Sprocll-M) :: [rr' I t:p]. (e)
154 Chapter 6. IDEAUZED ALGOL and its Specification Logic

On the other hand, Sprocll-Lp E (spec, rr') is

(V(l:Od··· (V(n:On)(V(;:Oi)··· (Vlj,:Ok) (f)


(Spa ~ {Pprocl .Lp «(d· .. «(n) {Qproc})
& (V(e: explike)(t~ # le & ... & (~# (e ~.Lp # (e) :: rr" ,

whose universality can be inferred, by adding assumptions and intro-


ducing quantifiers, from
{Pprocl .Lp«(d ••• «(n) {Qprocl :: rr'"
and

.Lp # (e :: [rr' I (e:explike] ,

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

11 Some Questionable Rules of Inference


We conclude by introducing two rules of inference whose status, at this writ-
ing, is still in question. Although we have not been able to deduce a contradic-
tion from these rules, they are not valid for the kind of semantics discussed
in [Reynolds, 1981a], and the problem of constructing a semantics for which
they are valid has not been solved. However, they are extremely useful; in-
deed they-or something like them-seem to be necessary for certain aspects
of program prOving.
The first is a stronger version of (4.20):
(11.1) Strong Constancy. Let rr be a type assignment mapping c into comm
and p, q, and r into Boolean expo Then
c#p&({p} ~{q}c{r}) ~{qandp}c{randp}:: rr.

Roughly speaking, if p holds before executing c and is not interfered with by


c (at any time during its execution), one can assume that it is a static assertion
in reasoning about c. The difficulty is that it is not really static, i.e. there are
environments for which c # p is true yet {p} is false.
The second rule is a limited kind of converse to our rule for leftside non-
interference decomposition:
(11.2) Leftside Noninterference Composition. Suppose that C E (comm, rr),
E E (explike, rr), P, Q E (Boolean exp, rr), and fcommlike(C) = {(I, ... , (n}.
Then
«(1 # E & ... & (n #E ~ {P} C {Q}) ~ (C # E ~ {P} C {Q}) :: rr.
John C. Reynolds 155

To see the difficulty here, take IT to be [k:integer integer var], C to be


('\k: integer integer var. skip)(k), E to be k, P to be true, and Q to be false.
Then (11.2) gives, with beta reduction of C,
(k # k ~ {true} skip {false}) ~ (skip # k ~ {true} skip {false}) :: IT,

which in conjunction with (5.7) gives


(k # k ~ {true} skip {false}) ~ {true} skip {false} :: IT.

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)

w - '\k:integer exp.s = I.~=d(O ,


we have (with appropriate beta reduction):
Sproc & (Yk:integer exp) (s := s + (k) # k
k-l k
~ {s = I (0 and 1:;; k:;; 100} s:= s + (k) {s = If(O})
i=1 i=l
o 100
~ {s = If(O} iterate(,\k:integerexp.s:=s+(k» {s = I((i)}
i=l i=l
:: IT,

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

gv(s) & s # f => (s # k


k-l k
=> {s = I f(i) and 1 ::; k ::; lOO} s := s + f(k) {s = I f(i)}) :: IT.
i=1 i=1
The need for rule (11.2) arises from the discrepancy between s # k and
s := s + f(k) # k. If we can use (11.2), along with quantifier introduction, we
get
gv(s) & s # f => (Yk:integer exp)(s:= s + f(k) # k
k-l k
=>{s=If(Oandl::;k::;lOO}s:=s+f(k){s=If(O})" IT,
i=1 i=1
and modus ponens gives the desired result:
Sproc & gv(s) & s # f =>
o 100
{s = I f(O} iterate(,\k:integer exp.s:= s + f(k» {s = I f(i)}
i=1 i=1
.. IT.

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

A similar, slightly more interesting example illustrates some of the fea-


tures of the "pure" ALGoL-like dialect we consider here.

Example 2 The block below always diverges.

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

coincides with observational congruence. However, experience in domain the-


ory suggests that full abstraction is hard to achieve and may not even be ap-
propriate [1, 13]. Indeed, if the programming language in question suffers de-
sign weaknesses, it may be necessary to modify the language design to match
a clean semantics; this is an important message of [20], for example. In this pa-
per we describe several semantical models which are fully abstract for various
ALGOL-like sublanguages, though not for the full range of ALGOL-like features.
In this preliminary paper, we omit a precise definition of full syntax and
features of ALGOL-like languages, expecting that the examples below will be
fairly clear without formal definition. It is helpful, as explained in [5, 22, 28,
10, 19], to regard the "true" syntax of ALGOL-like languages as the simply
typed "-calculus over the base types
Loc, Val, Locexp, Valexp, Prog
denoting memory locations, storable values, location thunks, value thunks, and
programs. The calculus has fixed pOint, conditional, and other combinators
suitable for interpreting the ALGOL-like phrase constructors such as assign-
ments or command sequencing. Some theoretical ALGOL dialects restrict the
set of procedure types so all calls are of type Prog, viz., they return nothing,
and exclude parameters of type Locexp and Valexp. These restrictions have
little effect on our results.

2 The Usual CPO Based Models


Of course models must be computationally adequate, i.e., they must agree with
the standard operational semantics (copy-rule in the case of ALGOL) giving
the computational behaviour of completely declared program blocks. In any
adequate semantics, semantic equivalence implies observational congruence.
The usual cpo-based models are satisfactory in this respect.
For example, a typical cpo-based "marked store" model takes the base type
representing locations to be LOCL, the flat cpo over a countably infinite set
Loc, and the base type of storable values to be Vall. for some set Val. Let
t t
Stores = (Loc - Val) x P/in(Loc), where A - B denotes the set of all total
set-theoretic functions from set A to set B, and P/in(A) denotes the set of all
finite subsets of A. The intention is that when (c, m) E Stores, the set m !;;; Loc
denotes the marked locations and c(l) E Val gives the contents of location I.
Let the base type Valexp of value-thunks be interpreted as Stores -.!... Vallo,
partially ordered pointwise; similarly for the base type Locexp. Let the base
type Prog of programs be Stores!?" Stores, where!?" denotes the set of all set-
theoretic partial functions partially ordered under containment. Each of these
base types is now a cpo, and we interpret higher ALGOL-like functional types
by taking the continuous functions. Then we may summarize the introductory
discussion by:
Theorem 1 The "marked stores" cpo-based model for the ALGOL-like language
FROG of [10. 28J or the language Reynolds calls the "essence of ALGOL" [22J.
without goto's or jumps, is computationally adequate but not fully abstract.
160 Chapter 7. Towards Fully Abstract Semantics for Local Variables

In fact, without local variables, the simpler "continuous functions" model in


which Stores = Loc.1 ..s Val.1, Valexp = Stores..s Val.1, similarly for Locexp,
and Prog = Stores..s Stores, where ..s denotes total continuous functions,
is fully abstract after one modification. Namely, for reasons which will be
clear to readers familiar with [20], a "parallel-or" combinator for a function
II v E (Val.1 x Val.1) ..s Val.1 must be added to the language, where
IIv(l, v) 1, IIv(v,l) 1,
IIv(O,O) 0, Ilv(.L,.L) .L,

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].

3 Halpern-Meyer-Trakhtenbrot Store Models


To handle Examples 1 and 2, Halpern-Meyer-Trakhtenbrot proposed a formal
definition of the support of a function from Stores to Stores. Intuitively the
support of a store transformation p is the set of locations which p can read or
write. In the HMT store model [10], Prog is taken to be the set of p with finite
support. To model local variables, the notion of support is extended to the
type Loc - Prog of block bodies regarded as a function of their free location
identifier. The semantical space used to interpret such block body functions is
again restricted to be the elements in Loc.1 ..s Prog with finite support. Since
there are an infinite number of locations, this restriction guarantees that a
location can be found which is not in the support of any given block body.
Then local storage allocation for a block begin new x; body end is (uniquely)
determined by the rule that x be bound to any location not in the support of
the function denoted by '\x. body.
Thus the HMT model justifies the conclusion that Block 2 diverges: if P
denotes some state transformation p E Prog, then any location 11= support(p)
can be bound to x. This proves divergence of the block, because p-by
definition-cannot change the contents of locations outside its support.
The definition of support for block-body functions requires another ingre-
dient: locations which are "recognized" by the block body-even if they are
neither read nor written-must be counted in the support of the block body.
Thus:
Albert R. Meyer and Kurt Sieber 161

Example 3 The blocks


begin new x; newy; x:= 0; y:= 0; Q(x,Y) end
and
begin new x; newy; x:= 0; y:= 0; Q(y,x) end
are HMT equivalent.

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.

Example 4 The block below always diverges.

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.

4 The Invariant-Preserving Model


In order to handle Example 5 we must know that every procedure Q of type
Prog - Prog preserves invariants outside its support. This is expressed pre-
cisely by the following reasoning principle:
Let Q be of type Prog - Prog and P of type Prog. Let r be a
property of stores such that support(r) n support(Q) = 0. If r is
an invariant of P, then r is also an invariant of Q(P).
This principle implies divergence of Block 5 because, letting r be defined by
formula contents(x) mod 2 = 0, we see that support(r) = {x} and r is an in-
variant of Add2. Inside the block we may assume that x if:. support(Q), and
so the principle implies that r is also an invariant of Q(Add2). Thus, the con-
ditional test follOwing the call Q(Add2) will succeed leading to divergence.
The above reasoning principle is valid in the Invariant-Preserving model
(c{ the Appendix). Actually all the previous examples are handled successfully
by this model as a consequence of the following general result.
An ALGoL-like term is said to be closed iff its only free identifiers are of
type Loc. A semantics is said to be half-fully abstract for a language iff seman-
tic equality between two terms, one of which is closed, coincides with observa-
tional congruence.
Define the PAscAL-like sublanguage by the condition that procedure param-
eters are restricted to be of type Val, Loc, or VaIn x Locm - Prog (essentially
the language considered in [15,16]).
Theorem 5 The Invariant-Preserving Model is computationally adequate for
the full range of ALeoL-like language features. With an additional II v combina-
tor, it is fully abstract for the "first-order" sublanguage and is half-fully abstract
wrt to the PAscAL-like sUblanguage.
Since Example 5 involves observational congruence to a closed term which
identically diverges, the Invariant-Preserving Model handles it as well as the
following slightly more sophisticated variant. (Note that the test z = x below
indicates equality of locations, rather than their contents.)
Example 6 The block
begin
new x;
procedure AlmostAdd2(z);
begin if z = x then x := 1 else x := contents(x) + 2 fi end;
x:= 0;
P (AlmostAdd2);
if contents(x) mod 2 = 0 then diverge fi
end
always diverges.
164 Chapter 7. Towards Fully Abstract Semantics for Local Variables

The following example illustrates failure of full abstraction in the


Invariant-Preserving Model:

Example 7 The block


beginnewx; procedure Add_l; begin x := contents(x) + 1 end; P(Add_l) end
is observationally congruent to the block

begin new x; procedure Add2; begin x : = contents(x) + 2 end; P (Add2) end

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,

Lemma 2 The PAscAL-like blocks in Example 7 are observationally congruent


but not semantically equivalent in the Invariant-Preserving Model.

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.

[6] j. W. de Bakker. Mathematical Theory of Program Correctness. Prentice-Hall


International, London, 1980.
[7] S. M. German, E. M. Clarke, and J. Y. Halpern. Reasoning about procedures as
parameters. In E. M. Clarke, Jr. and D. Kozen, editors, Logics of Programs 1983,
pages 206-220, volume 164 of Lecture Notes in Computer Science, Pittsburgh, PA,
1983. Springer-Verlag, Berlin, 1984.

[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.

[9] M. J. C. Gordon. The Denotational Description of Programming Languages.


Springer·Verlag, New York, 1979.
[10] J. Y. Halpern, A. R. Meyer, and B. A. Trakhtenbrot. The semantics of local storage,
or what makes the free-list free? In Conference Record of the Eleventh Annual
ACM Symposium on Principles of Programming Languages, pages 245-257, Salt
Lake City, Utah, 1984. ACM, New York.
[11] Z. Manna and R. Waldinger. Problematic features of programming languages: a
situational-calculus approach. Acta Informatica, 16:371-426,1981.
[12] R. E. Milne and C. Strachey. A Theory of Programming Language Semantics.
Chapman and Hall, London, and Wiley, New York, 1976.
[13] K. Mulmuley. Full Abstraction and Semantic Equivalence. The MIT Press, Cam-
bridge, Mass., 1987.
[14] M. J. O'Donnell. A critique of the foundations of Hoare-style programming logiC.
Comm. ACM, 25:927-934, 1982.

[15] E. OIderog. A characterization of Hoare's logic for programs with PAscAL-like


procedures. In 15th ACM Symposium on Theory of Computing, pages 320-329.
ACM, New York, 1983.
[16] E. OIderog. Correctness of programs with PASCAL-like procedures without global
variables. Theoretical Computer Science, 30:49-90, 1984.
166 Chapter 7. Towards Fully Abstract Semantics for Local Variables

[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.

Appendix: Locally Complete CPO Models


For cpo's A, B, we write A <l B to indicate that A is a strict sub·cpo of B, i.e., A is a
sub-cpo of Band J.B EA.
Definition 1 Let (1,~) be a directed set. An I-lcpo is a partially ordered set D =
UtEl Di with a least element J.D, where D restricted to Di is a cpo, and Di <l Dj when-
ever i ~ j.
It follows from this definition that J.DI = J.D for every i E I.
Albert R. Meyer and Kurt Sieber 167

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

(D ..!s E)i = {f:D..!.. E I fj E Dj.E. Ej for all j ~ i}.

Then D .is E = UiEI(D .is E)i is called the set of locally continuous functions from D
toE.

Lemma 3 D.is E, partially ordered pointwise, is an I -lcpo.


Lemma 4 Every locally continuous function on an I -lcpo D has a least fixed point, which
is characterized as usual.
Definition 3 Let D be an I-lcpo and i E I. An n-ary relation R on D is called
i-admissible if R (d, ... ,d) holds for every dEDi, and the restriction of R to the cpo
D'j is admissible for every j E I.
Definition 4 A tag set over I is a set K such that every k E K has an associated number
nk ~ 1, and downward closed set, down(k) £: I.
Definition 5 Let K be a tag set over I. A K-relationally structured I-lcpo (short:
(1,K)-rcpo) is an I-lcpo D with, for each k E K, an nk-ary relation Rk on D, such
that Rk is i-admissible for every i E down(k).

For R £: Dn, S £: En we let R - S denote the lifted relation on D ..!.. E defined by


(R - S)(fI. ... ,(n) iff VdI. ... , dn.R(dl, ... , dn ) =S(fddIl, ... ,(n(dn»).
Definition 6 Let D, E be (1, K)-rcpo's. For every i define

(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.

Lemma 5 D !!!. E, partially ordered pointwise and K -relationally structured by (the


rp
restrictions of) the lifted relations Rf- E ) = (Rf - Rf), is an (1, K)-rcpo.
Theorem6 The category (1,K)-RCPO, whose objects are (1,K)-rcpo's and whose
morphisms are relation-preserving functions, is Cartesian closed. Hence, if every
ground type is interpreted as an (1, K) -rcpo, and D T1-T2 is defined inductively to be
DTI !!!. D T2, then {DT} is a model of the simply typed A-calculus. In this model, the
meaning of any pure A-term of type T is contained in D{ for all i. Hence there is a least
fixed point operator for each type T, which is contained in D}T-T)-T for all i.
Lemma 6 Let {DT} be an rcpo-model as in Theorem 6. Then RI (d, ... ,d) holds for all
dE D{ whenever i E down(k), and ifi,j !> h, then D;I-T2 (Djt) £: D~2 for all types
TI, T2.

A first application of Theorem 6 leads to a repaired version of the Halpern-


Meyer-Trakhtenbrot model. Namely, let Perm (Lac) be the set of strict permutations
/l: LocJ. ..!.. LocJ. and, for every /l E Perm(Loc), let Fix(/l) denote the set of fixed points
of /l. Further let Stores = Loc..!.. Val, and for every L £: Loc, let =L be the binary
relation on StoresJ. defined by
SI =L S2 iff SI = .L = S2 V (SI '* .L '* S2 /\ VI E L.sdl) = S2(/)).
168 Chapter 7. Towards Fully Abstract Semantics for Local Variables

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,

Dyalexp {f: Stores"!" Vall. I V Slo S2.S1 =L S2 ~ f(sll = f(S2)},

DLLocexp {
f:Stores~Locl.
r I (V s·f(s) E L U {..LLod) /\
VSloS2.S1 =Ls2~f(sll =f(S2)
}
,

(Vs·f(s) "'..L~ s =Loc-L f(s») /\ }


Dro9 = {f: Stores"!" Storesl.
VSl,S2.S1 =LS2~f(SI) =Lf(S2) ,
relationally structured by
R~al(Vlo V2) iff VI = V2,
RbOC(h,12) iff /1(h) = 12,

R~alexp(fl,f2) iff Vs.fds 0 /1) = f2(S),

R~ocexp (fl,f2) iff V s. fl (s 0 /1) = /1 (f2 (s»),

R:;09(fl,f2) iff Vs.fds 0 /1) = f2(S) 0/1.

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

assign(l) (f) (s) = { ~[f(S) jl] if 1 '" ..L,f(s) '"


otherwise.
..L,

It follows from the definitions that assign E D~c-valexp---Pro9; in particular, it has


empty support-as do all the necessary combinators. So we can define [Assign] =
assign in the model.
The combinator which causes the main semantical problem is New of type
(Loc - Frog) - Frog used to explain the semantics of block structure by translation
into ,\ -calculus:
Translation(begin new x; Cmd end) ::= (New ('\x: Loc. Translation (Cmd))).
The definition of [New] follows [10); we omit the details here. This completes our
sununary of the HMT semantics.
Albert R. Meyer and Kurt Sieber 169

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

This is a description of the programming language FORSYTHE, which is a


descendant of ALGOL 60 intended to be as uniform and general as pos-
sible, while retaining the basic character of its progenitor. (This report
supersedes the preIiminary description of FORSYTHE [11.)

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

This is the goal of the programming language FORSYTHE. We believe that


it retains the essence of ALGOL 60, yet is both simpler and more general. The
key to achieving this combination of simplicity and generality is to exploit the
procedure mechanism and the type system, in order to replace a multitude of
specialized features by a few general constructions.
The language is named after George E. Forsythe, founding chairman of
the Computer Science Department at Stanford University. Among his many
accomplishments, he played a major role in familiarizing American computer
scientists (including the author) with ALGOL.
Before considering FORSYTHE in detail, we specify its location in the design
space of programming languages. As illustrated below, FORSYTHE lies on one
side of each of three fundamental dichotomies in language design:
Untyped Typed
ISWIM
PAL ALGOL 68
GEDANKEN ML
SCHEME
Imperative
Functional

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.

2 From ALGOL to FORSYTHE: An Evolution of Types


The long evolution which has led from ALGOL 60 to FORSYTHE is too complex
to recount here in detail. However, to provide an overview of FORSYTHE and
reveal its relationship to ALGOL, it is useful to outline the development of
the heart of the language, which is its type structure. (In this introductory
account, we retain the familiar notations of ALGOL, rather than using the novel
notations of FORSYTHE.)
An essential characteristic of an ALGoL-like language is that the variety
of entities that can be the value of a variable or expression is different from
the variety of entities that can be the meaning of identifiers or phrases. We
176 Chapter 8. Design of the Programming Language FORSYTHE

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.

• A phrase type, or more simply a type, denotes a set of meanings appro-


priate to an identifier or phrase.
In ALGOL 60, there are three data types: integer, real, and boolean. In
FORSYTHE, we use more succinct names, int, real, and bool, and add a fourth
data type, char, denoting the set of machine-representable characters.
To capture the existence of an implicit conversion from integers to reals,
we define a partial order on data types called the subtype relation. We write
8 ~ 8', and say that 8 is a subtype of 8' when either 8 = 8' or 8 = int and
8' = real, i.e.
real
I bool char
int
In ALGOL 60, the phrase types are the entities, such as integer, real array,
and procedure, that are used to specify procedure parameters. However, the
phrase types of ALGOL 60 are not sufficiently refined to permit a compiler to
detect all type errors. For example, in both
procedure silly(x); integer x; y := x + 1
and
procedure strange(x); integer x; x := x + 1
the formal parameter x is given the type integer, despite the fact that an actual
parameter for silly can be any integer expression, since x is evaluated but never
assigned to, while an actual parameter for strange must be an integer variable,
since x is assigned to as well as evaluated.
To remedy this defect, one must distinguish the phrase types int(eger)
exp(ression) and int(eger) var(iable), writing
procedure si/ly(x); intexp x; y := x + 1
and
procedure strange(x); intvar x; x := x + 1 .
(In a similar manner, each of the other data types gives rise to both a phrase
type of expressions and a phrase type of variables.)
like data types, phrase types possess a subtype relationship. Semantically,
(} ~ (}' means that there is an implicit conversion from meanings of type (} to
meanings of type (}'. But the subtype relation can also be interpreted syn-
tactically: (} ~ (}' means that a phrase of type (} can be used in any context
requiring a phrase of type (}'. Thus, since a variable can be used as an expres-
sion, intvar ~ intexp, and similarly for the other data types. Moreover, since
John C. Reynolds 177

an integer expression can be used as a real expression, intexp ::5 realexp. In


summary:
realexp

/
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

Next, we consider the types of procedures. In ALGOL 60, when a parameter


is a procedure, one simply specifies procedure for a proper procedure (whose
call is a command), or integer procedure, real procedure, or boolean pro-
cedure for a function procedure (whose call is an expression). But to obtain
full compile-time typechecking, one must use more refined phrase types that
indicate the number and type of parameters, e.g.
procedure (intexp, intvar)
to denote a proper procedure accepting an integer expression and an integer
variable, or
real procedure (realexp )
to denote a real procedure accepting a real expression. (Note that this refine-
ment introduces an infinite number of phrase types.)
These constructions can be simplified and generalized by introducing a
binary type constructor - such that (J - (J' denotes the type of procedures
that accept (J and produce (J' or, more precisely, the type of procedures that
accept a single parameter of type (J and whose calls are phrases of type (J'.
For example, a real procedure accepting a real expression would have type
realexp - realexp.
To describe proper procedures similarly, it is necessary to introduce the
type comm to describe phrases that are commands (or in ALGOL jargon, state-
ments). Then a proper procedure accepting an integer variable would have
type intvar - comm.
The idea that commands are not a kind of expression is one of the things
that distinguishes ALGOL-like languages from languages such as SCHEME or
ML, where commands are simply expressions that produce trivial values while
performing side effects. This distinction is even sharper for FORSYTHE, where
expressions cannot have side effects.
To extend the typing of procedures to permit more than one parame-
ter, one might introduce a type constructor for products and regard, say,
procedure(intexp, intvar) as (intexp x intvar) - comm. However, as we
will see below, the product-like construction in FORSYTHE describes objects
whose fields are selected by names rather than position. Thus multiple-
parameter procedures are more easily obtained by Currying rather than
by the use of products. For example, procedure(intexp, intvar) becomes
intexp - (intvar - comm) or, more simply, intexp - intvar - comm, since
- is right associative.
In other words, a proper procedure accepting an integer expression and an
integer variable is really a procedure accepting an integer expression whose
calls are procedures accepting an integer variable whose calls are commands.
Thus the call p(al, a2) is written (p(al»(a2) or, more simply, p(ar)(a2), since
procedure application is left associative. (In fact, if the parameters are identi-
fiers or constants, one can simply write p al a2.)
In general, the type
procedure«(Jl, ... , (In)
becomes
(Jl - ... - (In - comm ,
John C. Reynolds 179

and, for each data type 8, the type


8 procedure(lh, ... , 9 n )
becomes
91 - . • • - 9 n - 8exp.
Moreover, this generalization includes the special case where n = 0, so that
parameterless proper procedures are simply commands and parameterless
function procedures are simply expressions. (Note that this simplification is
permissible for call by name, but would not be for call by value, where param-
eterless procedures are needed-as in LISP-to postpone evaluation.)
To determine the subtype relation for procedural types, suppose 9i ;s; 91
and (h ;s; 9 z. Then a procedure of type 9 1 - O2 can accept a parameter of type
0i (since this parameter can be converted to type ( 1 ) and its call can have type
Oz (since it can be converted from 02 to Oz), so that the procedure also has
type 9i - Oz. Thus
If 9i ;s; 0 1 and 02 ;s; Oz then 0 1 - 02 ;s; 0i - 9z,
i.e. - is antimonotone in its first operand and monotone in its second operand.
For example, since intexp ;s; realexp, we have
intexp - realexp

/
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.)

3 Types and the Subtype Relation


Having sketched its evolution, we can now define the type system of FORSYTHE
precisely. The sets of data types, primitive (phrase) types, and (phrase) types
can be defined by an abstract grammar:
8 ::= int I real I booll char (data types)
p ::= 81 value I comm I compl (primitive types)
8 ::= p I 8 - 8 It: 8 I ns 18 & 8 (types)
where the metavariable t ranges over identifiers.
Here there are three changes from the previous section. Expression types
are now named by their underlying data types; for example, intexp is now just
into A new primitive type value stands for the union of all the data types;
its utility will become apparent in Section 4. Finally, a new phrase type ns
(for "nonsense") has been introduced; it is possessed by all (parsable) phrases
of the language, and can be viewed as a unit for the operation &, Le. as the
intersection of the empty set of types.
182 Chapter B. Design of the Programming Language FORSYTHE

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

operand. The last four relationships have the following consequences:


L: (01 & O2) '" L: 0 1 & t: 02

o- (01 & O2) '" (0 - 0 1) & (0 - 02)


L:ns '" ns
O-ns",ns.
The first two of these equivalences show that intersection distributes with
object constructors (modulo "') and with the right side (but not the left) of
-. The last two equivalences are analogous laws for the intersection of zero
types.
It can be shown that every pair of types has a least upper bound (which is
unique modulo "'). In particular, the following equivalences suffice to compute
a least upper bound, 0 1 U 02, of any types 0 1 and 02:
01 U 02 '" 02 U 01

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.

4 The Semantics of Types


To describe the meaning of types, we will employ some basic concepts from
category theory. The main reason for doing so is that, by formulating succinct
definitions in terms of a mathematical theory of great generality, we gain an
assurance that our language will be uniform and general.
A second reason is that the abstract concept of a category establishes a
bridge between intuitive and rigorous semantics. Intuitively, we think of a
type as standing for a set, and an implicit conversion as a function from one
such set to another. But since our language permits nonterminating programs,
types must denote domains (i.e. complete partial orders with a least element)
and implicit conversions must be continuous functions. Moreover, a further
level of complication arises when one develops a semantics that embodies
the block structure of ALGOL-like languages; then types denote functors and
implicit conversions are natural transformations between such functors [5, 16,
17).
However, the choice between these three different views is simply a choice
between three different "semantic" categories:
• SET, in which the objects are sets, and the set of morphisms S - S' is
the set of functions from S to S'.
• DOM, in which the objects are domains, and D - D' is the set of contin-
uous functions from D to D'.
• PDOM~, in which the objects are functors from a category ~ of "store
shapes" to the category PDOM of predomains (complete partial orders,
possibly without least elements) and continuous functions, and F - F'
is the set of natural transformations from F to F'.
Therefore, if we formulate the semantics of types in terms of an arbitrary
category, assuming only properties that are possessed by all three of the above
categories (Le. being Cartesian closed and possessing certain limits), then we
can think about the semantics in the intuitive setting of sets and functions,
yet be confident that our semantics makes sense in a more rigorous setting.
Thus we will define types in terms of an unspecified semantic category,
while giving explanatory remarks and examples in terms of the particular cat-
egory SET (or occasionally DOM).
John C. Reynolds 185

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']

19~9] ~~ [(9' ~91


[9]
and
[9'] [9':$ 9], [9]

[9' ~ 9'] ~ 1~ [[9 ~ 9'1


[9']
both commute, so that [9] and [9'] are isomorphic, which we denote by
[9] "" [9']. (Note, however, that nonequivalent types may also denote iso-
morphic objects.)
Next, we define (up to isomorphism) the meaning of each type constructor:
• Procedures. To define -, we require the semantic category to be Carte-
sian closed, and define [9 - 9'] to be [9] => [9'], where => denotes
the exponentiation operation in the semantic category. In SET (DOM),
[9] => [9'] is the set (domain) of all (continuous) functions from [9]
to [9'].
• Object Constructors. We define [l: 9] to be an object that is isomorphic
to [9].
186 Chapter 8. Design of the Programming Language FORSYTHE

• Nonsense. We define [ns] to be a terminal object T, Le. an object such


that, for any object s, there is exactly one morphism from s to T. In SET
or DOM a terminal object is a set containing one element. (Thus even
nonsense phrases have a meaning, but they all have the same meaning.)

• Intersection. Because of its novelty, we describe the meaning of inter-


section in more detail than the other type constructors. Basically, the
meaning of fJ 1 & fJ 2 is determined by the meanings of fJ}, fJ 2, and their
least upper bound fJl U fJ 2. From fJ 1 & fJ 2, we can convert to fJ 1 and
from there to fJ 1 U fJ 2, or we can convert to fJ2 and from there to fJ 1 U fJ 2;
clearly the two compositions of conversions should be equal. Moreover,
whenever fJ :::; fJ 1 & fJ2, the composite composition from fJ to fJ 1 & fJ 2 to
fJ 1 should equal the direct conversion from (} to fJ 1, and similarly for (}2.
In other words, in the diagram

[(}]

the inner diamond must commute and, for all (} such that (} :::; fJ 1 & (}2,
the two triangles must commute.

However, these requirements are not sufficient to determine [fJ 1 & fJ 2 ].


To strengthen them, we replace [fJ] by an arbitrary object sand
[fJ :::; (}l] and [(} :5 fJ 2] by any functions fl and f2 that make the outer
diamond commute, and we require the "mediating morphism" from s to
[fJ 1 & fJ 2] to be unique. Specifically, we define [(}l & (}2] by requiring
John C. Reynolds 187

that, in the diagram

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

[91 & 92] "" {(X1,X2) I Xl E


[91] andx2 E [92]
and [91 ::;; 91 u 92]X1 = [92 ::;; 91 u 92]X2 } •

(For domains, one must require all implicit conversion functions to be


strict.) In other words, a meaning of type 9 1 & 9 2 is a meaning of type
91 paired with a meaning of type 92, subject to the constraint that these
meanings must convert to the same meaning of type 91 u 92.

The following are special cases of the definition of intersection. Although


we describe these cases in terms of SET and DOM, basically similar results hold
for any semantic category that is Cartesian closed and possesses the pullbacks
necessary to define intersection.
188 Chapter 8. Design of the Programming Language FORSYTHE

• If lh u (h "" ns then the constraint


[91 :$;91 U 9 2 ]X1 = [92 :$; 9 1 U 92]X2
always holds, since both sides of the equation belong to the one-element
set [ns]. Thus

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,

• If 91 :$; 92, so that 91 u 92 = 92, then


[91 & 92] ""
{{XloX2) I Xl E [91] and X2 E [92] and [91 :$; 92]X1 = X2}

"" [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}

"" [91] n [92] .


In this special case, the intersection of types does correspond to the
intersection of sets.
An example of this case arises when 91 and 92 are data types with no
implicit conversion between them, such as int and char. In this case,
their least upper bound is value, which stands for the union of the data
types, so that the implicit conversions into value are identity injections.
Thus,
[int & char] "" [int] n [char] .
This is the purpose of introducing the type value. Had we not done
so, we would have int u char = ns, which would give [int & char] ""
[int] x [char].
In FORSYTHE, the sets denoted by the data types real, bool, and char are
diSjoint (and the set denoted by int is a subset of that denoted by real),
so that intersections such as int & char denote the empty set. However,
this is a detail of the language deSign, while the preceding argument is
more general.
The above arguments are based on the intuition that data types denote
sets of values. In fact, however, data types such as int and char, when
John C. Reynolds 189

they are used as phrase types, denote sets of meanings appropriate to


kinds of expressions. Specifically, in a domain-theoretic model,
[int] = S - Zl. [char] = S - Cl. [value] = S - Vl. ,
where S is the set of states; Zl., Cl., and Vl. are set of integers, characters,
and values, made into flat domains by adding a least element; and Z and
C are subsets of V. Even in this richer setting, however, it is still true
that the conversion functions from [int] and [char] into [value] are
identity injections.
• Finally, we consider the intersection of procedural types. First, we must
define the implicit conversions between such types. If 9i ::;; 91 and 92 ::;;
9 2 then the conversion of f E [91 - 92] to [9i - 9;] is obtained by
composing f with appropriate conversions of its arguments and results:
[91 - 92::;; 9 1' - 9 '2]f
[9i] -~~--"--~----='-"-'...--. [9 ] 2
[OisO.I] [[0, sO,]
[9 1 ] _ _ _ _-"-f_ _ _ _• [9 2 ]
or as an equation,
[e 1 - 9 2 ::;; 9;' - 9;]f = [9;' ::;; 9 1] ; f; [92 ::;; 9;] ,
where; denotes composition in diagrammatic order.
From the definition of intersection, by substituting the equation for the
least upper bound of two procedural types, and using the above equa-
tion for the implicit conversion of procedural types, we obtain
[(91 - 9;') & (92 - 9;)] ""
{(fl,f2) I fl E [91 - 9i] andf2 E [92 - 9;]
and [91 - 9i ::;; (91 & 92) - (9;' u 92)]fl
= [92 - 92: ; (91 & 92) - (9i u 92)]f2 }
= { (fI, f2) I fl E [ 9 1 - 9;'] and f2 E [92 - 92]
and [91 & 92 ::;; 9 1 ] ; fl ; [9;' ::;; 9i u 9 2]
= [91 & 92 ::;; 92] ; f2 ; [9; ::;; 9;' u 92] }.
Here the constraint on fl and f2 is the commutativity of a hexagon:

[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

This constraint implies that the "versions" of a procedure whose type


is an intersection must respect implicit conversions-which is what dis-
tinguishes such a procedure from the usual notion of a "generic" proce-
dure. For example, since int & real = int and int ureal = real (taking =
rather than "" here simplifies the argument),
[(int - int) & (real - real)] ~
{ (fl, f2) I fl E [int - int] and f2 E [real - real]
and fl ; [int ::;; real] = [int ::;; real] ; f2 } .
Here the hexagon collapses into a rectangle, so that fl and f2 must sat-
isfy .
[int] _---'-f::....l-.... [int]

lint< reall j jlint ~ real]

[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.

5 Phrases and their Typings


We now introduce the phrases of FORSYTHE and give rules for determining
their types. Specifically, we will give inference rules for formulas called typ-
ings.
A type assignment is a function from a finite set of identifiers to types. If
7T is a type aSSignment, then [7T I L: (J 1 denotes the type assignment whose
domain is dom 7T U {L}, such that [7T I L: (J 1L = (J and [7T I L: (J]L' = TTL' when
L' '* L. We write [7T ILl: e 1 I ... I Ln: (In 1 to abbreviate [ ... [7T ILl: e 1 1... I
Ln: en J.
If 7T is a type aSSignment, p is a phrase, and (J is a type, then the formula
7T I- P: e, called a typing, asserts that the phrase p has the type e when its
free identifiers are assigned types by 7T.
An inference rule consists of zero or more typings called premisses fol-
lowed (after a horizontal line) by one or more typings called conclusions. The
rule may contain metavariables denoting type assignments, phrases, identi-
fiers, or types; an instance of the rule is obtained by replacing these metavari-
abIes by particular type assignments, phrases, identifiers, or types. (Some
rules will have restrictions on the permissible replacements.) The meaning of
a rule is that, for any instance, if all the premisses are valid typings then all of
the conclusions are valid typings.
First, we have rules describing the behavior of subtypes, the nonsense type,
and intersections of types:
John C. Reynolds 191

• Subtypes (often called subsumption)


TTl-p:9
when 9 ~ 9'
TT I- p: 9'
• Nonsense

• 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'

TT I- (i\L:9 I I·· ·19n • p): 9 j - 9'


In this rule, notice that 9, must be one of a list of types appearing explicitly
in the abstraction (separated by the alternative operator D. For example, under
any type assignment, the abstraction
i\x:int. x x x + 2
has type int - int, while the abstraction
i\x: int I real. x x x + 2
has both type int - int and type real - real, so that, by the rule for inter-
section, it also has type (int - int) & (real - real). (Note the role of the
colon, which is always used in FORSYTHE to specify the types of identifiers or
phrases.)
In contrast, in the case where the argument is implicitly typed, we have:
192 Chapter 8. Design of the Programming Language FORSYTHE

• Abstractions (with implicit typing)


[7T I L: ()] I- P : ()'
7T I- (i\(. p) : () - ()'

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

7T I- (real const) : real

7T I- (char const) : char

7T I- (string const) : charseq


John C. Reynolds 193

• 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-

7T I- PI < P2 : bool 7T I- PI < P2 : bool 7T I- PI = P2 : bool


7T I- PI ::s; P2 : bool 7T I- PI ::s; P2 : bool 7T I- PI * P2 : bool
7T I- PI > P2 : bool 7T I- PI > P2 : bool
7T I- PI ?; P2 : bool 7T I- PI ?; P2 : bool
• Boolean Expressions
7T I- p: bool
7T I- - P : bool
7T I- PI : bool
7T I- P2 : bool
P2 : bool
7T I- PI 1\
P2 : bool
7T I- PI V
7T I- PI => P2 : bool
7T I- PI P2 : bool =
Here the only real novelty is the provision of two operators for exponentiation:
t accepts a real and an integer, and yields a real, while accepts two integers,**
and yields an integer (giving an error stop if its second operand is negative).
The boolean operators => and =
denote implication and equivalence (if-and-
only-if) respectively.
As in ALGOL, the semicolon denotes sequential composition of commands.
But now it can also be used to compose a command with a completion, giving
a completion:
194 Chapter B. Design of the Programming Language FORSYTHE

• 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

Two iterative constructions are provided: the traditional while command,


and a loop construction which iterates its operand ad infinitum (Le. until the
operand jumps out of the loop by executing a completion):
• while Commands
TT I- PI : bool
TT I- P2 : comm
TT I- while PI do P2 : comm
• loop Completions
TT I- p: comm
TT I- loop P : compl
There is also a phrase whose execution causes an error stop, with a mes-
sage obtained by evaluating a character sequence:
• Error stops
TT I- P : charseq
TT I- error P : 0
Notice that error P is a phrase that can have any type.
In place of the procedure declarations of ALGOL, FORSYTHE provides the
more general let-definition construct invented by Peter Landin. In the implic-
itly typed case:
• Nonrecursive Definitions (with implicit typing)
TT I- PI : 0 1

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

Here. the explicit occurrence of : ef in the definition constrains the type ef to


be used in an application of the rule. while the absence of such an occurrence
allows 6 i to be chosen arbitrarily. (Notice that the alternative operator Icannot
be used in let constructions. nor in the recursive definitions discussed below.)
For example. in place of the procedure definition displayed earlier. one can
specify the type of the procedure in the definition (instead of specifying the
type of the procedure argument in the abstraction):
letp: 6 - eomm == Ax. BprocinB.

There is also an alternative form of the nonrecursive definition.


letinlineLd: ell? == Pl •...• Ln{: en}? == Pninp.
that has the same typing behavior and semantics as the let form. but causes
the procedures or other entities being defined to be compiled into inline code.
Recursion is provided in two ways. On the one hand. there is a fixed-point
operator ree:
• Fixed Points
ITl-p:6-e
IT I- ree: e. p: e
Notice that explicit typing is required here.
196 Chapter 8. Design of the Programming Language FORSY1HE

On the other hand, there is a form of recursive definition:


• Recursive Definitions
[TT Ill: lh I ... Iln: On] I- PI : 01

[TT Ill: 0 1 I ... I In: On ] I- Pn : On


[TT Ill: 0 1 I ... Iln: On ] I- P : 0
TT I-Ietrec ll: 01 , ••• , In: On where II == PI, ... , In == Pninp : 0

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

7T f- seq (Po, ... , Pn-I> : (int - e) & len: int


The effect of this construction is that, if e is an integer expression with value
k such that 0 ::; k < n, then
seq (Po, ... , Pn-l) e
has the same meaning as Pk (and thus can be used in the role of a case con-
struction). Moreover,
seq (Po, ... , Pn-d.len
is an integer expression with value n.
Finally, we introduce yet another form of definition, to permit the user to
let identifiers stand for types. The types occurring in phrases are generalized
to type expressions that can contain type identifiers, which are given meaning
by the inference rule
• Type Definitions
7T f- (p / Ll, ... ,Ln - el,iJl"" en,in ) : e

7T f- (lettype Ll == el,l 1.. ·1 el,m[l ... , Ln == en,l 1.. ·1 en,mn inp) : 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

the execution of a nonlocal completion causes control to escape from p so that


the new variable can no longer be assigned or evaluated.) Thus
newintvar init Ax. B
is equivalent to the ALGOL block
begin integer x; x := init; Bend.
The multiplicity of types of the new8var procedures permits variables to
be declared in completions and expressions as well as commands. Within
expressions, however, locally declared variables, like any other variables, can
be evaluated but not assigned.
Four analogous procedures are provided for declaring variable sequences:
new8varseq: int - (int - 8) -
(8varseq - comm) - comm & (8varseq - compI) - compi
& (8varseq - int) - int & (Ovarseq - real) - real
& (8varseq - booI) - bool & (8varseq - char) - char) .
The application new8varseq 1 init p causes a new 8 variable sequence of length
1 to be added to the state of the computation; the elements of this sequence
are initialized to values obtained by applying the procedure init, and then the
procedure p is applied to the sequence. Thus
newintvarseq 1 init Ax. B
is equivalent to the ALGOL block
begin integer array x(O: 1- 1);
begin integer i;
for i := 0 to 1 - 1 do x(i) := init(i)
end;
B
end.
In essence, this approach to the declaration of variables and sequences is a
syntactic de sugaring of the conventional form of declarations into the applica-
tion of a procedure; procedures such as newintvar init or newintvarseq 1 init
that are intended to be used this way are called declarators. The advantage
of this view is that the user can define his own declarators or declarator-
producing procedures. For example (as we will illustrate later), the user can
define his own declarators for any kind of array for which he can program the
index-mapping function.
Another declarator is provided for the declaration of completions that
cause control to escape from a command. The procedure
escape: (compi - comm) - comm
applies its parameter to a completion whose execution causes immediate ter-
mination of the application of escape. Thus
escape Ae. C
200 Chapter 8. Design of the Programming Language FORSYTHE

is equivalent to the ALGOL block


begin C'; e: end,
where C' is obtained from C by substituting goto e for e.
Facilities for input and output are also provided by declarators. For output,
there is
newoutchannel : charseq -
« characc - corom) - corom
& (characc - compI) - compI) .
The application newoutchannel s p opens the file named by s for output and
applies the procedure p to an output channel c, which has type characc. Each
time p assigns a character to c, the character is output to the file. When the
call of p is completed (or when the execution of a nonlocal completion causes
control to escape from p so that c can no longer be executed), the file is closed.
(Our choice of automatic file-closure upon block exit is based on a belief that,
in an ALGoL-like language, file buffers should obey the same stack discipline
as variables.)
Since output channels are character acceptors, one might expect input
channels to be character expressions, but this would violate the principle that
expressions must not have side effects. Instead, an input channel has type
(characc & eor:compl) - corom, so that the declarator for input has type
ne~nchannel:charseq-

««characc & eor:compI) - comm) - comm) - comm


& ( « characc & eor: compI) - corom) - compI) - compI) .
The application newinchannel s p opens the file named by s for input and
applies the procedure p to an input channel c. Each time p executes a call
c(a, eor == k) the next character is read from the file and passed as an argu-
ment to the character acceptor a, unless an end-of-file has occurred, in which
case the completion k is executed. When the call of p is completed (or when a
nonlocal completion causes an escape from p), the file is closed.
Standard input and output are provided by channels that are named by
predefined identifiers:
std_in: (characc & eor: compI) - corom std_out : characc .
Some obvious functional procedures are provided to convert between char-
acters and their integer codes:

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: ().

In each of these cases, the identifiers lI. ... , In must be distinct.


Also, to permit a more ALGoL-like appearance,
PI := P2 abbreviates PIP2.

Finally, although we treated them as independent constructions in Sec-


tion 5, the definitional forms can be regarded as abbreviations. First, recur-
sive definitions can be desugared in terms of nonrecursive definitions and the
fixed-point operator. For a single definition, one can give a straightforward
de sugaring:
letree lI: (}I where II == PI inp abbreviates let II == ree: (}I. All. PI inp.
However, a general rule that includes simultaneous recursion is more complex:
letreelI:(}I. ... ,In:(}nwherell == PI, ... ,In == Pninp abbreviates
lett == ree: ((1: (}I & ... & In: (}n). Al.
lettl == l.ll. ..• ,In == l.ln in(li == PI. ... ,In == Pn)
inletli == l.lI, ••• ,In == l.lninp,
where l is any identifier not occurring in the letree definition.
Second, nonrecursive definitions can be de sugared in terms of abstractions
and applications (following Landin):
lettI: (}I == PI, ... ,In: (}n == Pn inp abbreviates
(All: (}I .... Aln: (}n. P)PI ... Pn .
This rule continues to make sense if, for some i, the types (}j are omitted.
But in this case the rightside will contain too little information to satisfy the
typechecker, even though the leftside may be satisfactory.

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

This transfonnation is sufficiently complex that it is worthwhile to encapsulate


it as a procedure. We define
letinline newinlVarres: int - intacc - (intvar - comm) - comm =
i\init. Afin. Ab. newinlVar init Mocal. (b local; fin:= local)

Then to call f by result we define


let fact:int - intacc - comm =An. Af.
newinlVar nAn. newinlVarres 1 f Af.
newinlVar 0 Ak. while k '* n do (k := k + 1; f := k x f)
When placed within the scope of the definition of newinlVarres, this definition
of fact reduces to to the previous one (except for the names of bound iden-
tifiers) and therefore has the same meaning. Moreover, since newinlVarres
is defined by letinline, the two definitions of fact will compile into the same
machine code.
We can also define the traditional recursive function procedure for com-
puting the factorial. Here again we call n by value, illustrating the use of
newinlVar within an expression.

letrec fact: int - int


=
where fact An. newinlVar n An. if n = 0 then 1 else n x fact(n-1)

Next, we give some examples of procedures that take advantage of call by


name. In the following function procedure for integer multiplication, call by
name is used to provide "short-circuit" evaluation,
letinline multiply:int - int - int =Am. An. if m = 0 then 0 else m x n
i.e. n will not be evaluated when m is zero. In a proper procedure akin to the
Pascal repeat command,
letinline repeat:comm - bool- comm =Ac. Ab. (c ; while ~ b do c)

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.

10 Escapes and Completions


The procedure escape declares a completion whose execution causes an exit
from the call of escape. A simple example of its use is the following procedure
for searching an integer function X (which might be an integer sequence or
array) over the interval I to u for a value that is equal to y. If such a value
is found, the procedure sets present to true and j to the argument for which
X(j) = y; otherwise it sets present to false.
let linsearch: (int - int) - int - int - int - boolacc - intacc - comm ==
AX. i\l. AU. Ay. Apresent. Aj.
escape AOUt.
(for I u Ak.
if X(k) = Y then (present:= true; j := k; out) else skip;
present:= false)
An alternative version of this procedure branches to one of two parameters
depending upon whether the search succeeds. If the search fails, it goes to the
completion failure; if the search succeeds, it goes to the completion procedure
success, passing it the integer k such that X(k) = y.
let linsearch:
(int - int) - int - int - int - (int - compl) - compl - compl ==
AX. i\l. AU. Ay. Asuccess. Afailure.
(for I u Ak. if X(k) = Y then success k else skip; failure)
John C. Reynolds 207

This illustrates that, if a procedure always terminates by executing a com-


pletion, then a call of the procedure will itself be a completion. This fact is
relevant when the last action of a procedure is to assign to a parameter, since
an assignment to a parameter is syntactic sugar for a call of the parameter. For
example, the following is an alternative typing of the procedure newintvarres
introduced in the previous section:
letinline newintvarres:
int - (int - compl) - (intvar - comm) - compl ==
i\init. i\fin. i\b. newintvar init Mocal. (b local; fin := local)

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)

In addition to using escape, one can define completions recursively,


to obtain the equivalent of conventional labels. For example, the fol-
lowing procedure sets y to x" (in time log n), without doing unnecessary
tests:
let power: int - int - intacc - comm == i\x. i\n. i\y.
newintvar n i\k. newintvarres 1 y i\y. newintvar x i\z.
escape i\zr {k = O}.
letrec tr, nz, ev, od, nzev: compl
where
tr {true} == if k = 0 then zr else nz,
nz {k "* O} == if k rem 2 "* 0 then od else nzev,
ev {even k} == if k = 0 then zr else nzev,
od {odd k} == k := k - 1; Y := Y x z; ev,
nzev {k "* 0 /\ even k} == k := k -;- 2 ; z := z x z; nz
in tr
The invariant of this program, i.e. the assertion that holds whenever any com-
pletion is executed, is
y X Zk = x" /\ k ~ 0 .
The additional assertions given as comments at the binding of each comple-
tion hold whenever the corresponding completion is executed.
Notice that, in contrast to labels, one can never execute a completion by
"passing through" to its definition. Indeed, the meaning of the above program
is independent of the order of the definitions of completions.
208 Chapter 8. Design of the Programming Language FORSYIHE

11 Sequences and Arrays


As we remarked in Section 6, using the built-in procedures for declaring vari-
able sequences the programmer can define his own procedures for declaring
more complex kinds of arrays. For example, suppose we want ALGOL-like one-
dimensional integer arrays with arbitrary lower and upper bounds (denoted
by the field names II and uf). First we define abbreviations for the relevant
types:
Iettype intarray == (int - int & ll, ul: int),
intaccarray == (int - intacc & ll, ul:int),
intvararray == (int - intvar & ll, ul: int)
Then the following procedure serves to declare integer variable arrays, which
are defined in terms of integer sequences:
Ietinline newintvararray: int - int - (int - int) -
(intvararray - comm) - comm & (intvararray - compl) - compI
& (intvararray - int) - int & (intvararray - real) - real
& (intvararray - booI) - bool & (intvararray - char) - char) ==
M. AU. Mnit. Ab. newintvar 1 M. newintvar u AU.
newintvarseq(u -I + 1) (Ak. init(k + l) )i\X.
b(Ak. X(k -l),ll == I, ul == u)

The sixfold intersection that is the type of newintvararray is similar to


the type of built-in declarators such as newintvarseq. Equally well, one could
provide the necessary type information in the abstractions rather than the
definition, using the alternative operator:
Ietinline newintvararray == M, u: into Ainit: int - into
Ab: intvararray - comm Iintvararray - compII intvararray - int I
intvararray - real Iintvararray - booII intvararray - char.
newintvar 1 M. newintvar u AU.
newintvarseq(u -I + I)(Ak. init(k + l))i\X.
b(Ak. X(k -l),ll == I, ul == u)

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

letinline slicecheck: ((int - int) - int - int - intarray


& (int - intacc) - int - int - intaccarray) ==
AX. AI. AU. (Ak. if 1 ~ k /\ k ~ U then X k else error 'subscript error',
II == I, ul == u)

The type of slice and slicecheck is extremely general:


• If the first argument has type int - int, or any subtype, such as intseq
or intarray, then the call will have type intarray.
• If the first argument has type int - intacc, or any subtype, such as
intaccseq or intaccarray, then the call will have type intaccarray.
• If the first argument has type int - intvar, or any subtype, such as
intvarseq or intvararray, then both of the previous cases will apply,
and the call will have type intarray & intaccarray, which is equivalent to
intvararray.
The use of these procedures is illustrated by a program for sorting by
finding maxima. First we define a procedure that sets j to the subscript of a
maximum of an array X:
letinline max:intarray - intacc - comm == AX. Aj.
newintvar X.II Aa. newintvar X.ul Ab.
newintvarres a j Aj.
while a < b do (a := a + 1; if X a> X j then j := a else skip)
(Here giving X the type intarray rather than intvararray indicates that max
will examine X but not assign to it.) Next comes a procedure for exchanging a
pair of array elements:
letinline exchange: (int - intvar) - int - int - comm == AX. i\i. Aj.
newintvar i Ai. newinlvar j Aj.
newintvar(X i) At. (X i := X j; X j := t)

(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)

The above procedure contains a spurious initialization of the variable j.


The purpose of this variable is to accept the result of max, but newintvar re-
quires us to initialize it before calling max. However, this unpleasantness can
210 Chapter 8. Design of the Programming Language FORSYTHE

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

lettype trivararray == (int - int - realvar) & size: int) in


letinline newtrivararray: int -
(trivararray - comm) - comm & (trivararray - compl) - compl
& (trivararray - int) - int & (trivararray - real) - real
& (trivararray - bool) - bool & (trivararray - char) - char) ==
An. Ab. newintvar nAn. newrealvarseq( (nx(n+1)).;.-2) (Ak. 0) AX.
b(M. Aj. if 0 ~ j 1\ j ~ i 1\ i < n then X«i x (i + 1)) .;.- 2 + j)
else error 'subscript error',
size == n) .

12 Input and Output


The following program illustrates the facilities for input and output. It reads a
sequence of pairs of nonnegative integers from a file named 'infile' and writes
the real quotient of each pair in floating-point notation to a file named 'outfile'.
It is assumed that the integers in the input file are separated by sequences of
one or more nondigits; if there are an odd number integers, the last is simply
converted from integer to real.
Each number in the output is printed on a separate line, as a six-digit
number greater or equal to one and less than ten, times a power of ten. A
result of zero is printed as 0.0, while division by zero gives an error mes-
sage.
newinchannel 'infile' Me. newoutchannel 'outfile' Aoc.
letinline is_digit: char - bool == Ac. newcharvar c Ac. #0 ~ C 1\ C ~ #9,
writecharseq: charseq - comm == As. newintvar 0 Ai.
while i < s.len do (oc:= s i; i := i + 1)
in
letinline writereal: real - comm == Ar.
if r = 0 then writecharseq '0.0' else
reaLto_charseq r 6 As. Ax.
(oc:= sO; oc:= #.; writecharseq(Ai. s(i + 1), len == 5) ;
writecharseq '* 10 **' ;
inUo_charseq (x - 1) writecharseq) ,
readint:compl- intacc - comm == Anonumber. Aa.
newcharvarseq 30 (Ak. #0) As. newintvar 0 M.
(repeat (ic(s i, eor == nonumber)) (is_digit(s i)) ;
escape Ae.
repeat (i := i + 1; ic(s i, eor == e)) (- is_digit(s i)) :
a := charseq_to_int(s, len == i))
212 Chapter 8. Design of the Programming Language FORSYTHE

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.

13 Data Abstraction with Objects


Perhaps the most important way in which FORSYTHE is more general than
ALGOL is in its provision of objects, which are a powerful tool for data ab-
straction. One can write abstract programs in which various kinds of data are
realized by types of objects, and then encapsulate the representation of the
John C. Reynolds 213

data, and the expression of primitive operations in terms of this representa-


tion, in declarators for the objects.
To illustrate this style of programming, we will develop a program for
computing reachability in a finite directed graph. Specifically, we will define a
procedure reachable that, given a node x and a graph g, will compute the set
of nodes that can be reached from x.
Throughout most of this development we will assume that "node" is a new
data type; eventually we will see how this assumption can be eliminated. Given
node, we can define a "set" to be an object denoting a finite set of nodes,
whose fields (called methods in the jargon of object-oriented programming)
are procedures for manipulating the denoted set:
lettype set ==
(member: node - bool
& insertnew : node - comm
& iter: (node - comm) - comm
& pick: comm - (node - comm) - comm)
The intention is that, if s is a set, x is a node, d is a procedure of type
node - comm, and e is a command, then:
• s. member x gives true if and only if XES.
• s. insertnew x inserts x into s, providing x is not already in s.
• s. iter d applies d to each member of s.
• If s is empty then s. pick e d executes e; otherwise s. pick e d removes an
arbitrary member from s and applies d to the removed member.
In terms of set, we can give a naive version of the reachability procedure.
The procedure maintains a set t of all nodes that have been found to be reach-
able from x, and a set u of those members of t whose immediate successors
have yet to be added to t. (An immediate successor of a node y is a node that
can be reached from y in one step.) Thus its invariant is
x E t 1\ U !; t 1\ (Vy E t) Y is reachable from x 1\ (Vy E t - u) 9 Y !; t,
where 9 is a function of type node - set such that 9 y is the set of immediate
successors of y. This invariant implies that, when u is empty, t is the set of all
nodes reachable from x.
In writing reachable, we assume that the parameter 9 is the immediate-
successor function of the graph, and that the result is to be communicated by
applying a procedural parameter p to the final value of t:
let reachable: node - (node - set) - (set - comm) - comm ==
Ax. Ag. Ap. newset At. newset Au.
(t.insennew x; u.insennew x;
escape Aout.loop u.pick out Ay. (g y).iter Az.
if - t.member z then (t.insennew z; u.insennew z)
else skip;
p t)
214 Chapter 8. Design of the Programming Language FORSYTHE

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.

newintvar 0 ~a. newintvar 0 ~b.


p{member == c,
iter == ~d. for 0 (b - 1) ~k. d(w k»
(insertnew == ~n. (c n := true; w b := n; b := b + 1),
pick == ~e. ~d. if a ~ b then e else (d(w a); a:= a + 1»

Here N is an integer expression giving an upper bound on the number of


nodes, and dummynode is an arbitrary entity of type node used to give a
spurious initialization to w.
Next, we consider the representation of graphs. As far as reachable
is concerned, a graph is simply its immediate-successor function, of type
node - setg. But the part of the program that creates graphs must have
some primitive procedure for graph construction. Thus we make graph an
object type with a field named addedge, denoting a procedure that, given its
source and destination nodes, adds an edge to the graph:
lettype graph == (node - setg & addedge : node - node - comm)
Notice that the immediate-successor function is a "nameless" field of a graph,
so that a graph can be passed directly to reachable.
We choose to represent a graph by an integer variable array succlist, in-
dexed by nodes, such that succlist n is a list of the immediate successors of
n. The lists are represented by a node variable sequence car and an integer
variable sequence cdr. The integer variable k gives the number of active list
cells. The empty list is represented by -1.
216 Chapter 8. Design of the Programming Language FORSYTHE

Thus the declarator for graphs is:


letinline newgraph: (graph - comm) - comm == Ap.
newintvamodearray(An. - 1) Asucclist.
newnodevarseq E (Ak. dummynode) Acar.
newintvarseq E (?o.k. - 1) Acdr. newintvar 0 Ak.
p(An. (iter == Ad. newintvar(succlist n) M.
while I '*' -1 do (d(car l) ; I := cdr l)),
addedge == Am. An.
(car k:= n; cdr k:= succlist m; succlist m:= k; k:= k + 1))
Here E is an upper bound on the number of edges in the graph.
Next, we consider extending our program so that, in addition to determin-
ing the set of nodes that can be reached from x, it computes paths from x
to each of these nodes. We will alter reachable so that it gives its parame-
ter p an additional argument r of type paths, where an object of type paths
provides two procedures for iterating over paths in forward and backward
directions:
lettype paths == (forward, backward: node - (node - comm) - comm)
If r is an object of type paths, y is a node reachable from x, and d is a proce-
dure of type node - comm, then r. forward y d or r. backward y d will apply
d to each node on the path from x to y.
Within reachable, each time an immediate successor z of y is inserted in t
and u, the path to z formed by adding z to the already known path to y will
be recorded in r. Thus, within reachable, r will have the type
lettype pathsvar == (paths & record: node - node - comm)
where r. record is a procedure such that r. record z y records the path to z
formed by adding z to the path to y. (In choosing the name pathsvar we are
stretching the meaning of "var". Although an object of type pathsvar cannot
be assigned to, in the conventional sense, it still consists of an object of type
paths intersected with an operation that changes the state of the object.)
The new version of reachable is:
let reachable:
node - (node - setg) - (sett - paths - comm) - comm ==
AX. Ag. Ap. newdoubleset At. AU. newpathsvar x Ar.
(u.insertnew x;
escape Aout.loop u.pick out Ay. (g y).iter AZ.
if - t.member z then u.insertnew z; r.record z y else skip;
p t r)

The representation of paths is defined within the declarator newpathsvar.


A node variable array link, indexed by nodes, is used to record the calls of
John C. Reynolds 217

record, so that link z = y holds after a call of r. record z y. Then forward


scans link recursively, while backward scans link iteratively:
letinline newpathsvar: node - (pathsvar - comm) - comm == .\x. .\p.
newnodevarnodearray('\n. dummynode) '\link.
p{record == AZ. Ay. link Z := y,
forward == .\n. .\d.
letrec scan: node - comm
where scan == .\n. newnodevar n An.
if eqnode n x then d x else (scan(link n) ; d n)
in scan n,
backward == .\n. Ad. newnodevar n.\n.
(while - eqnode n x do (d n; n := link n) ; d x»)
Here eqnode is a primitive operation for comparing nodes. The initial param-
eter x of newpathsvar is the node from which all the paths emanate.
Finally, we must define the data type node. FORSYTHE lacks facilities for
defining new data types, but the effect of a new data type can be obtained by
defining the relevant phrase types, primitive operations, and declarators. This
is easy if we use a trivial representation, where a node is represented by an
integer n such that 0 :$ n < N:
lettype node == int,
nodeacc == intacc,
nodevar == intvar
in
letinline dummynode == -1,
eqnode == Am: node. An: node. m = n,
newnodevar == newintvar,
newnodevarseq == newintvarseq,
newboolvarnodearray == newboolvarseq N,
newintvarnodearray == newintvarseq N,
newnodevarnodearray == newintvarseq N
Unfortunately, this way of defining node is limited by the fact that lettype
definitions are transparent rather than opaque. Thus typechecking would not
detect an erroneous operation that treated nodes as integers.
To avoid this difficulty, one would like to have opaque type definitions in
FORSYTHE. However, even in the absence of opaque definitions, one can still
achieve a degree of data abstraction by defining nodes to be one-field objects
containing integers, rather than "raw" integers. This approach assures that
the integrity of the abstraction node will not be violated by the reachability
program providing this program does not contain any occurrence of the field
name used in the definition of node.
218 Chapter 8. Design of the Programming Language FORSYIHE

This approach is embodied in the following definitions, in which nn is used


as the "secret" field name:
lettype node == (nn: int) in
lettype nodeacc == node - comm in
lettype nodevar == (node & nodeacc) in
lettype nodevarseq == (int - nodevar & len: int),
boolvarnodearray == node - boolvar,
intvarnodearray == node - intvar,
nodevarnodearray == node - nodevar
in
letinline dummynode == (nn == -1),
eqnode:node - node - bool == Am. An. m.nn = n.nn,
newnodevar: node - (nodevar - comm) - comm ==
AinU. Ab. newintvar(init.nn) Ax. b(nn == x,Am. x:= m.nn),
newnodevarseq:
int - (int - node) - (nodevarseq - comm) - comm ==
i\l. AinU. Ab. newintvarseq I (Ak. (init k).nn) Ax.
b(Ak. (nn == x k, Am. x k := m.nn),Ien == x.len)
in
letinline newboolvarnodearray: (node - bool) -
(boolvarnodearray - comm) - comm ==
AinU. Ab. newboolvarseq N (Ak. init(nn == k») Ax. b(An. x(n.nn»),
newintvarnodearray: (node - int) -
(intvarnodearray - comm) - comm ==
Ainit. Ab. newintvarseq N (Ak. init(nn == k») Ax. b(An. x(n.nn»),
newnodevarnodearray: (node - node) -
(nodevarnodearray - comm) - comm ==
Ainit. Ab. newnodevarseq N (Ak. init(nn == k») Ax. b(lI.n. x(n.nn»)
This approach to defining a new data type by defining the relevant phrase
types can be used to provide more interesting representations. As a final
example, we define complex numbers:
lettype complex == (r : real & i : real) in
lettype complexacc == complex - comm in
lettype complexvar == (complex & complexacc) in
letinline newcomplexvar: complex - (complexvar - comm) - comm ==
Ainit. Abody. newrealvar init.r Arpart. newrealvar iniU Aipart.
body(r == rpart, i == ipart,
Ac. newrealvar c.r At. (ipart := c.i ; rpart := t») .
John C. Reynolds 219

Here newcomplexvar is a declarator that represents a complex variable by two


real variables rpart and ipart. The last line d~fines an acceptor that sets the
representation variables. (Note the use of the temporary t to insure that both
parts of the complex argument c are evaluated before either representation
variable is set.)
Of course, one would like to make real numbers a subtype of complex
numbers, with an implicit conversion that sets ipart to zero. In FORSYTHE,
however, this is not permitted. Unfortunately, there seems to be no way to
allow such user-defined implicit conversions while enforcing the relationships
between conversions and procedures with multiple types that are described in
Section 4.

14 Other Publications Related to FORSYTHE


The genesis of FORSYTHE lies in the author's general viewpoint about ALGOL-
like languages [5], and especially in the functor-category semantics of such
languages developed by F. Oles [16,17]. The language was first described in a
preliminary report [1], of which this document is a substantial revision. (The
most important change in the language is that the requirements for explicit
type information have been made more flexible.)
There are several technical problems associated with intersection types.
The semantics of intersection as a pullback is not syntax-directed, since the
meaning of 81 & (h depends upon then meaning of 81 u (h as well as of 81 and
of 82. A demonstration that the semantics is still well-defined was given in an
invited talk at the Logic in Computer Science Symposium [19], but was never
written up. A proof that the semantics is coherent, i.e. that different proofs of
the same typing do not lead to different meanings, was given in [20].
The functor-category semantics is the basis of a scheme for generating
intermediate code for ALGoL-like language [21].

15 Conclusions and Future Research


There are a number of directions in which it would be desirable to extend
FORSYTHE, providing such extensions do not impact the uniformity of the lan-
guage. We are currently investigating, or hope to investigate, the following
possibilities:
• Sums or disjunctions of phrase types. Unfortunately, sums of phrase
types interact with the conditional construction in a counterintuitive
manner. Suppose, for example, that comm + int is the binary sum of
comm and int, with injection operations inl of type comm - (comm +
int) and in2 of type int - (comm + int), and a case operation Ell such
that, for any phrase type 8, if PI has type comm - e and P2 has type
int - 8 then PI Ell P2 has type (comm + int) - e, with the reduction rule
(PI Ell P2)(inl x) ==> PI x.
If application of the conditional construction to sum types is to make
sense, then the reduction
(PI Ell P2)(if b then inl c else in2 e) ==> if b then PI c else P2 e
220 Chapter 8. Design of the Programming Language FORSYTHE

must hold. Then, if the language is to exhibit reasonably uniform behav-


ior, the similar reduction
(PI E9 P2)(ifbthen ini celse ini c') => ifbthenpi ceisepi c'
must also hold. But then, the reduction
ini (if b then c else c' ) => if b then ini c else ini c'
cannot hold, for otherwise we would have both
(PI E9 P2) (indifb thenc else c'») => pdifbthencelsec')
and
(PI E9P2) (indif b thenc else c'») => (PI E9P2)(if b then ini c else ini c')
ifbthenpi ceisepi c' ,
=>
which reduce the same phrase to two phrases that will have different
meanings if the procedure PI changes the value of b before executing its
parameter. In particular, the falsity of the reduction rule for injections
and conditionals implies that injections cannot be treated as implicit
conversions.
• Polymorphic or universally quantified phrase types [22], possibly with
bounded quantification in the sense of [23]. In addition to providing
polymorphic procedures, this extension would also provide opaque type
definitions. This kind of extension has been investigated by B. C. Pierce
[18,24], who found that there is no decision procedure for type-checking
bounded quantification, even in the absence of intersection types. On
the other hand, Pierce gave a practical algorithm for type-checking
the combination of bounded quantification and intersection types that
seems to terminate in cases of practical interest. He also gave a number
of interesting examples of the descriptive power of such a type system.
• Recursively defined phrase types.
• Enriched data types. Although the data types of ALGOL (and so far of
FORSYTHE) are limited to primitive, unstructured types, there would be
no inconsistency in providing a much richer variety of data types. The
real question is which of the many possible enrichments would provide
additional expressive power without degrading efficiency of execution.
• Coroutines.
• Alternative treatment of arrays. Array facilities along the lines of those
described in [25] would serve to avoid spurious array initializations,
such as the initialization of w in newdoubleset in Section 13. But it is
not clear how this approach can be extended to encompass multidimen-
sional arrays.
On the other hand, there is also a direction in which it might be fruitful to
restrict FORSYTHE: to impose syntactic restrictions so that one can determine
syntactically (in a fail-safe manner) when phrases cannot interfere with one
John C. Reynolds 221

another. (Two phrases interfere if their concurrent execution is indeterminate.


For example, aliased variables interfere, as do procedures that assign to the
same global variables.) Such a restriction would open the door for the concur-
rent, yet determinate, execution of noninterfering commands, as well as for a
form of block expression (in the sense of ALGOL W) that is restricted to avoid
side effects.
Nearly two decades ago, I wrote a paper [26] proposing a scheme for re-
stricting ALGoL-like languages for this purpose. At the time, certain syntactic
anomalies (described in the final section of [26]) discouraged me from pursu-
ing the matter further. But it is now clear that these anomalies can be avoided
[27, 28]. Moreover, it appears that this approach does not raise insuperable
type-checking complications.
However, the syntactic discipline described in these papers would still re-
strict FORSYTHE uncomfortably. For example, one could not regard a := e as
a e when a and e interfere, nor could one write newintvar in it b when in it and
b interfere. There are also problems with recursive procedures that assign to
global variables, and the use of completions is precluded.

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.

[4] Wirth, N. and Hoare, C. A. R. A contribution to the development of ALGOL. Com-


munications of the ACM, vol. 9 (1966), pp. 413-432.

[5] Reynolds, J. C. The essence of ALGOL. In Algorithmic Languages, Proceedings


of the International Symposium on Algorithmic Languages, Amsterdam, October
26-29, edited by J. W. de Bakker and J. c. van Vliet. North·Holiand, Amsterdam,
1981, pp. 345-372. See Chapter 3.
[6] Landin, P. J. The next 700 programming languages. Communications of the ACM,
vol. 9 (1966), pp. 157-166.
[7] van Wijngaarden, A., Mailloux, B. J., Peck, J. E. 1.., Koster, C. H. A., Sintzoff, M.,
Lindsey, C. H., Meertens, 1.. G. 1.. T., and Fisker, R. G. Revised report on the
algorithmic language ALGOL 68. Acta Informatica, vol. 5 (1975), pp. 1-236.
[8] Sussman, G. J. and Steele Jr., G. 1.. SCHEME: An Interpreter for Extended Lambda
Calculus. AI Memo no. 349, Massachusetts Institute of Technology, Artificial
Intelligence Laboratory, December 1975,42 pp.
[9] Milner, R., Tofte, M., and Harper, R. W. The Definition of Standard ML. MIT Press,
Cambridge, Massachusetts, 1990, xi+10l pp.
[10] Reynolds, J. C. The Craft of Programming. Prentice-Hall International, London,
1981, xiii+434 pp.
222 Chapter 8. Design of the Programming Language FORSYTHE

[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.

[I6] Oles, F. J. A Category-Theoretic Approach to the Semantics of Programming Lan-


guages, Ph. D. Dissertation. Syracuse University, August 1982, vi+240 pp. See
Chapter 11.
[I 7] Oles, F. J. Type algebras, functor categories, and block structure. In Algebraic
Methods in Semantics, edited by M. Nivat and J. C. Reynolds. Cambridge Univer-
sity Press, Cambridge, England, 1985, pp. 543-573. See Chapter H.
[18] Pierce, B. C. Programming with Intersection Types and Bounded Polymorphism,
Ph. D. Dissertation. Carnegie Mellon University, December 1991, viii+175 pp.
Report No. CMU-CS-91-205.
[19] Reynolds, J. C. Conjunctive types and ALGOL-like languages (abstract of invited
lecture). In Proceedings Symposium on Logic in Computer Science, Ithaca, New
York, June 22-25. 1987, p. H9.
[20] Reynolds, J. c. The coherence of languages with intersection types. In Theoretical
Aspects of Computer Software, International Conference TACS '91, Proceedings,
Sendai, Japan, September 24-27, 1991, edited by T. Ito and A. R. Meyer. Lecture
Notes in Computer SCience, vol. 526, Springer-Verlag, Berlin, 1991, pp. 675-700.
[21] Reynolds, J. C. Using functor categories to generate intermediate code. In Confer-
ence Record of POPL '95: 22nd ACM SIGPLAN-SIGACT Symposium on Principles
of Programming Languages, San Francisco, January 22-25. 1995, pp. 25-36. See
Chapter 12.
[22] Reynolds, J. C. Towards a theory of type structure. In Programming Symposium,
Proceedings, Colloque sur la Programmation, Paris, April 9-11, edited by B. Robi-
net. Lecture Notes in Computer Science, vol. 19, Springer-Verlag, Berlin, 1974,
pp.408-425.
[23] Cardelli, 1. and Wegner, P. On understanding types, data abstraction, and poly-
morphism. ACM Computing Surveys, vol. 17 (1985), pp. 471-522.
[24] Pierce, B. C. Bounded quantification is undecidable. In Conference Record of the
Nineteenth Annual ACM SIGPLAN-SIGACT Symposium on Principles of Program-
ming Languages, Albuquerque, New Mexico, January 19-22. 1992, pp. 305-315.
John C. Reynolds 223

[25) Dijkstra, E. W. A Discipline ofProgramming. Prentice-Hall, Englewood Cliffs, New


Jersey, 1976, xviii+217 pp.

[26) Reynolds, J. C. Syntactic control of interference. In Conference Record of the


Fifth Annual ACM Symposium on Principles of Programming Languages, Tucson,
Arizona, January 23-25. 1978, pp. 39-46. See Chapter 10.
[27) Reynolds, J. C. Syntactic control of interference, part 2. In Automata, Lan-
guages and Programming, 16th International Colloquium, Stresa, Italy, July Il-
lS, edited by G. Ausiello, M. Dezani-Ciancaglini, and S. Ronchi Della Rocca. Lec-
ture Notes in Computer Science, vol. 372, Springer-Verlag, Berlin, 1989, pp. 704-
722.

[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.

(29) Hopcroft, J. E. and Ullman, J. D. Introduction to Automata Theory, Languages,


and Computation. Addison-Wesley, Reading, Massachusetts, 1979, x+418 pp.

Appendix A Lexical Structure


A FORSYTHE program, in the form actually read by a computer, is an ASCn character
string that is interpreted as a sequence of lexemes and separators. (After eliminating
the separators, the sequence of lexemes is interpreted according to the concrete syntax
given in Appendix B.)
A lexeme is one of the following:
• A keyword, which is one of the following character strings:
and begin div do else
end error if iff implies
in let letinline letrec lettype
loop or rec rem seq
then where while

• 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 special symbol, which is one of the following characters or strings:


, ( ) &
I \ A
*
/ + <
> ->
** <= >= .-
A separator is either a blank, a tab, a newline, or a comment, where a comment is
either:
• a sequence of characters enclosed in the braces { and }. If the braces { and }
occur within the sequence of characters, they must be balanced.
• a percent sign % followed by a sequence of characters not containing a newline,
followed by a newline.
Except that they separate lexemes that would otherwise combine into a single
lexeme (when, for example, an identifier is followed by another identifier or a natural-
number constant), separators have no effect on the meaning or translation of a pro-
gram. More precisely, the program is interpreted by scanning the input characters
from left to right, repeatedly removing the longest string that is a lexeme or separator,
and then eliminating the separators from the resulting sequence.
John C. Reynolds 225

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

AppendixB Concrete Syntax


We will specify the concrete syntax of FORSYTHE by giving a context-free grammar that
defines "parsable" programs as sequences of lexemes. (The conversion of a string of
input characters into a sequence of lexemes, and also the transliteration of lexemes
needed to fit the constraint of the ASCH character set, are defined in Appendix A.)
Notice that a parsable program is not necessarily typable; typing is specified by the
inference rules given previously and is independent of the concrete syntax.
The one novelty of this syntax is its treatment of "heavy prefixes" such as the
conditional phrase. Such phrases are permitted to follow operators even when those
operators have high precedence. For example one can write
A x ifBthenCelseD + E
instead of
A x (ifBthenCelseD + E).
To illustrate the treatment of heavy prefixes, consider augmenting a simple lan-
guage of arithmetic expressions,
(factor) ::= (id) I «expression»
(term) ::= (factor) I (term) x (factor)
(expression) ::= (term) I (term) + (expression)
with a conditional expression, treated as a heavy prefix. The resulting grammar is:
(factor) ::= (id) I «general expression»
(heavy factor) ::= if(general expression) then (general expression) else
(general expression)
(term) ::= (factor) I (term) x (factor)
(heavy term) ::= (heavy factor) I (term) x (heavy factor)
(expression) ::= (term) I (term) + (expression)
(heavy expression) ::= (heavy term) I (term) + (heavy expression)
(general expression) ::= (expression) I (heavy expression)
In this simple example, a phrase beginning with a heavy prefix will extend to the
next right parentheSis (or to the end of the text). In the actual syntax of FORSYTHE,
such phrases extend to the next semicolon, comma, right parenthesis, or end.
226 Chapter 8. Design of the Programming Language FORSITHE

The grammar of FORSYTHE is given by the following productions, in which we


use (type n) to denote type expressions, (p n) to denote phrases, and (hp n) to
denote heavy phrases. The integer n indicates the precedence level (with small n
for high precedence). The symbols (id), (nat const), (real const), (char const), and
(string const) denote the lexeme classes for identifiers and various kinds of constants,
as defined in Appendix A.
(program) ::= (p 16)

(type id) ::= (id)


(id list) ::= (id) I (id), (id list)

(type 0) ::= (type id) I «type 3» I begin(type 3) end


(type 1) ::= (type 0) I (type 0) - (type 1)
(type 2) ::= (type 1) I (id list) : (type 2)
(type 3) ::= (type 2) I (type 2) & (type 3)
(alt type) ::= (type 1) I (type 1) I (alt type)

(let list) ::= (id) = (p 15) I (id) : (type 1) = (p 15)


= =
I (id) (p 15), (let list) I (id) : (type 1) (p 15), (let list)
(letrec list) ::= (id list) : (type 1) I (id list) : (type I), (letrec list)
(where list) ::= (id) = (p 15) I (id) = (p 15), (where list)
= =
(lettype list) ::= (type id) (alt type) I (type id) (alt type), (lettype list)
(seq list) ::= (p 15) I (p 15), (seq list)

(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

(p 3) ::= (p 2) I (p 3)(exp op)(p 2)


(hp 3) ::= (hp 2) I (p 3)(exp op)(hp 2)
(exp op) ::= 1 I **
(p 4) ::= (p 3) I (p 4)(mult op) (p 3)
(hp 4) ::= (hp 3) I (p 4)(mult op)(hp 3)
(mult op) ::= x I / I + I rem
(p 5) ::= (p4) I (add op)(p 4) I (p 5) (add op)(p 4)
(hp 5) ::= (hp 4) I (add op) (hp 4) I (p 5) (add op)(hp 4)
(add op) ::= + I -
(p 6) ::= (p 5) I (p 6) (reI op) (p 5)
(hp 6) ::= (hp 5) I (p 6)(rel op) (hp 5)
(reI op) ::= = I *' I ~ I < I 2: I >
(p 7) ::= (p 6) I -(p 6)
(hp 7) ::= (hp 6) I -(hp 6)
(p 8) ::= (p 7) I (p 8) A (p 7)
(hp 8) ::= (hp 7) I (p 8) A (hp 7)
(p 9) ::= (p 8) I (p 9) v (p 8)
(hp 9) ::= (hp 8) I (p 9) v (hp 8)
(p lO) ::= (p 9) I (p 10) => (p 9)
(hp lO) ::= (hp 9) I (p 10) => (hp 9)
(p 11) ::= (p 10) I (p 11) <=> (p 10)
(hp 11) ::= (hp lO) I (p 11) <=> (hp 10)
(p 12) ::= (p 11) I (p 12) := (p 11)
(hp 12) ::= (hp 11) I (p 12) := (hp 11)
(p 13) ::= (p 12) I (hp 12)
(p 14) ::= (p 13) I (p 14) ; (p 13)
(p 15) ::= (p 14) I (id) == (p 15)
(p 16) ::= (p 15) I (p 16), (id) == (p 15)
I (p 16), .\(id list) : (alt type). (p 13)
I (p 16), .\(id list). (p 13)

Appendix C Type Checking


It is well-known that there is no algorithm for the complete inference of intersection
types [13,14,151. Thus, FORSYTHE must require the programmer to provide a degree
of explicit type information. We have attempted to make this requirement as flexible
as possible; as a consequence, a precise description of where explicit types must occur
is rather complicated.
The following is a grammatical schema (Le. a van Wijngaarden grammar) for the
abstract syntax of a sublanguage of the language described earlier, such that every pro-
gram in the sublanguage contains enough type information to be typechecked (even
though the program may not be type-correct). The converse is nearly true, though
(using phrases of type ns) one can contrive programs that type check even though they
do not satisfy this schema.
228 Chapter 8. Design of the Programming Language FORSYTHE

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) .

Since C is not expliCitly typed, this leads to the call


tC(1T, newintvar 0 Ax. B, T) .

In turn, assuming that newintvar 0 has the type


(intvar - comm) - comm & (intvar - compI) - compi &
(intvar - int) - int & (intvar - real) - real &
(intvar - booI) - bool & (intvar - char) - char ,
this leads to the call
tc( 1T, Ax. B, intvar - comm & intvar - compl & intvar - int &
intvar - real & intvar - bool & intvar - char) .
Naively, one would expect this to leads to six calls that all typecheck the same abstrac-
tion body:
tC([1T I x:intvar],B,comm) tC([1T I x:intvar],B,compl)
tC([1T I x:intvar],B,int) tC([1T I x:intvar],B,real)

tc([ 1T I x:intvar ],B, bool) tc( [1T I x: intvar], B, char) .


John C. Reynolds 231

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

(BI /\ B2)* = AndBi Bf


(BI v B2)* = Or Bi Bf
(-,B)* = NotB*
(V'x)B)* = Forall(l\x: tlf. B*)
(3x)B) * = Exists (l\x : t If. B*)
x* = x,
where t and f are distinct types, neither of which is a subtype of the other, and And,
Or, Not, Forall, and Exists are identifiers not occurring in the original expression. (For
the particular typechecker described in this appendix, one can omit the alternative
type expressions t If.) In addition a truth value b is translated into a type b* by
true* = t faJse* = f .
Let f3 be an assignment of truth values to the free variables of a quantified Boolean
expression, and let rr be the type assignment that maps each of these variables x into
(f3x) *, and maps the additional variables of B* as follows:
rr(And) (f - f - f) & (f - t - f) & (t - f - f) & (t - t - t)
=

rr(Or) = (f - f - f) & (f - t - t) & (t - f - t) & (t - t - t)


rr(Not) = (f - t) & (t - f)
rr(Forall) = (f - f & t - f) - f) & «f - f & t - t) - f)
& (f - t & t - f) - f) & (f - t & t - t) - t)
rr(Exists) = (f - f & t - f) - f) & (f - f & t - t) - t)
& (f - t & t - f) - t) & (f - t & t - t) - t) .
Then it is easy to see that B evaluates to b under the truth-value assignment f3 if and
only if the typing rr f- B* : b* is valid.
One might object that this reduction maps closed quantified Boolean expressions
into open (and type-open) phrases of FORSYTHE, and thus might not imply the in-
efficiency of typechecking closed phrases. This objection can be overcome, how-
ever, by enclOSing B* in the following declarations (which are based on the clas-
sicallambda-calculus encoding of boolean values by the projections l\x. l\y. x and
l\x. l\y. y):
lettype t '" int - ns - int, f '" ns - int - int
in
let And: (f - f - f) & (f - t - f) & (t - f - f) & (t - t - t) '"
l\p. l\q. l\x. l\y. p (q x y) y
Or: (f - f - f) & (f - t - t) & (t - f - t) & (t - t - t) '"
l\p. l\q. l\x. l\y. P x (q x y)
Not: (f - t) & (t - f) '"
l\p. l\x. l\y. pyx
in
let Forall: (f - f&t - f) - f) & (f - f&t - t) - f)
& (f - t & t - f) - f) & (f - t & t - t) - t) '"
l\h. And (h l\x. l\y. x) (h l\x. l\y. y)
Exists: (f - f & t - f) - f) & (f - f & t - t) - t)
& (f - t & t - f) - t) & (f - t & t - t) - t) '"
l\h. Or (h l\x. l\y. x) (h l\x. l\y. y)
in ....
John C. Reynolds 233

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

is a consequence of expressions representing static values: values that do not


change over time. Thus, an expression's value is independent of the order
in which its sub-expressions are evaluated. Side-effects are incompatible with
these properties, as side-effects change the values of other expressions, mak-
ing the order of evaluation important.
Assignments are a means of describing dynamic data: data whose values
change over time. In their conventional form, assignments have side-effects on
their environment, making their order of evaluation important. Not only are
such assignments incompatible with laziness, but they also destroy the nice
mathematical properties of pure languages. Hence lazy functional languages
shun assignments.
However, since assignments directly model the dynamic behavior of a
physical computer's store, they yield efficient implementations of dynamic
data. In contrast, one models dynamic data in functional languages by repre-
senting the state explicitly or, pOSSibly, by creating streams of states. Com-
pilation techniques and language notations have been proposed to permit ex-
plicit state manipulation to be implemented efficiently [HB8S, GH90, Wad90b,
Wad90a]. Unfortunately, these methods do not achieve all the effects of true
dynamic data. For instance, dynamic data may be "shared", i.e., embedded
in data structures and accessed via different access paths. When shared dy-
namic data are updated using assignments, the change is visible to all program
points that have access to the data. In contrast, when state is being manipu-
lated explicitly, updating shared data involves constructing a fresh copy of the
entire data structure in which the data are embedded, and explicitly passing
the copy to all program points that need access to the data. This tends to be te-
dious and error-prone, and results in poor modularity. One particularly faces
this difficulty while encoding graph traversal algorithms such as topological
sort, unification and the graph reduction execution model of lazy functional
languages.
In this paper, we propose a theoretical framework for extending functional
languages with dynamic data and assignments while retaining the desirable
properties of static values. The resulting language has the following key prop-
erties:
Expressions have static values. State-dependent and state-independent ex-
pressions are distinguished via a type system. The former are viewed
as functions from states to values and the functions themselves are
static. (Such functions are called observers and resemble classical con-
tinuations [Sto77, SW74j). The type system ensures that this view can be
consistently maintained, and limits the interaction between observers in
such a way that expressions do not have side-effects 1.
The language is a strict extension of lambda calculus. Function abstraction
and application have precisely the same meaning as in lambda calcu-
1In the contemporary functional-programming community, the terms "assignment" and "side-
effect" are sometimes used synonymously. We use the term "side-effect" in its original meaning:
an expression has a side-effect if, in addition to yielding a value, it changes the state in a manner
that affects the values of other expressions in the context. Assignments in our proposed language
do not have such side-effects. Similar comments apply to terms like "procedure" and "object".
Vipin Swamp, Uday S. Reddy, and Evan Ireland 237

Ius. This is a key property that is not respected by call-by-value lan-


guages like SCHEME (even in the absence of side-effects). The operational
semantics is presented as a reduction system that consists of the stan-
dard reduction rules of lambda calculus together with a set of additional
rules; these rules exhibit symmetries similar to those of lambda calcu-
lus. The reduction system is confluent (or, equivalently, Church-Rosser),
and recursion-free terms are strongly normalizing.
This work can also be given a logical interpretation: it extends the
correspondence between logic and programming to include dynamic data.
The language described in this paper is the language of constructions for
a suitably formulated constructive logic. This dual aspect is treated else-
where [SR92, Swa92].
The utility of the language is characterized by the following properties:
Shared dynamic data are available. Dynamic data are represented by typed
objects called references. References can refer to other references as
well as to functions. They can be embedded in data structures and used
as inputs and outputs of functions.
Dynamic data may be implemented by a store. This is achieved by a type
system that sequentializes access to regions of the state, much as in
effect systems [GL86] and the languages based on linear logic [GH90,
Wad90b, Wad91].
The language is higher-order. References, data structures, functions and ob-
servers are all permissible as arguments and results of functions. This
permits, for instance, the definition of new control structures, new stor-
age allocation mechanisms, and an object-oriented style of program-
ming.
The language is integrated symmetrically. The applicative sublanguage and
the imperative sublanguage are equally powerful and they embed each
other. Not only can applicative terms be embedded in imperative terms,
but imperative terms can also be embedded in applicative terms. This
allows the definition of functions that create and use state internally but
are state-independent externally.
The remainder of this paper is organized as follows. Section 2 presents a
core formal language called Imperative Lambda Calculus (ILC) that is an exten-
sion of the typed lambda calculus. Section 3 studies ILC's use in programming.
Section 4 discusses the motivation and design issues behind ILC's type system.
Section 5 gives an improved type system for ILC. Section 6 presents the formal
semantics of ILC. This includes a typed denotational semantics and an opera-
tional semantics presented as reduction rules. Various formal properties are
established, such as type soundness, confluence and strong normalization.
Section 7 demonstrates the utility of ILC with an extended example: the unifi-
cation of first-order terms. Finally, Section 8 compares ILC with related work
in the literature.
The revisions with respect to the 1991 version of this paper include the
new type system described in Section 5, and new semantic models in Section 6
238 Chapter 9. Assignments for Applicative Languages

that take into account this type structure as well as some subtle issues in
modelling variable allocation.

2 Imperative Lambda Calculus


Imperative Lambda Calculus (ILC) is an abstract formal language obtained by
extending the typed lambda calculus [Mit90j with imperative programming
features. Its main property is that, in spite of this extension, its applicative
sublanguage has the same semantic properties as the typed lambda calculus
(e.g., confluence and strong normalization). Furthermore, these same proper-
ties also hold for the entire language of ILC

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

a value of type T. Observers differ from commands in ALGoL-like languages in


that they return values in addition to modifying the state and, secondly, they
do not have a sequential composition operator. Thus, observers resemble
command continuations rather than commands. Ot is also possible to devise
variants based on commands instead of continuations; see Section 3.3.)
It is significant that the value returned by an observer is of an applicattve
type T. Since a state exists only to be observed, all information about the
state is lost when its observation is completed. So, the values observed in this
fashion should be meaningful independent of the state, Le., they should be
applicative. An observer type Obs T may be viewed as an implicit function
space from the set of states to the type T.
The product and function space constructions have the same meaning in
all three layers (cf. Section 6). Thus, there is no ambiguity involved in treating
T types as also being e and 00 types. The name "Imperative Lambda Calculus"
is justified by the property that the semantics of functions in all three layers
is the same as that of lambda calculus.

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

Variable hypothesis Constant


f- k: T
Z:Wf-Z:W
(if k is a constant of type T)

--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

Figure 1: Type inference rules

derivation, then we say that (r f- e: w) is a term or that e is a term with typing


r I- e:w.
The first few rules of Figure 1 deal with the simply typed lambda-calculus
part of ILC. In addition, ILC contains five new constructs to form trivial ob-
servers (Obs-Intro), to run observers (Obs-elim), to create a new reference (Cre-
ation), to access a reference's content (Dereference), and to modify a refer-
ence's content (Assignment). We now discuss these terms in more detail.
An observer is a computation that carries out state-manipulation and re-
turns a value. The construct
return(u)
builds a trivial observer that simply returns a value without carrying out any
state-manipulation (rule Obs-Intro). Conversely, if an observer has no free
variables of mutable or imperative types then it cannot manipulate any part
Vipin Swarup, Uday S. Reddy, and Evan Ireland 241

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

This reflects the requirement that manipulations of state should be performed


in a sequential fashion, similar in spirit to the proposal of single-threaded
lambda calculus [GH90]. Even though it is possible to express functions that
accept more than one observer, the state manipulations of such observers
have to be eventually sequentialized because there are no multi-ary primitives
on observers. (Recall that there are no constants of mutable and imperative
types). This fact has two consequences. First,programming in the imperative
sublanguage of ILC resembles the continuation-passing style. Second, the state
can be implemented efficiently by means of a global store. We return to these
issues in Section 4.
The type system of ILC presented above has a paradoxical flaw: the sub-
stitution property fails. If e is a term with a free variable x: 00, then the term
e[u/x] obtained by substituting an w-typed term u, should be well-typed. This
is not always the case. The problem is with the Obs-elim rule. For the term
run(t) to be well-typed, all the free variables of t should be of applicative
types. However, there are applicative-typed terms with non-applicative free
variables, a simple example being:
z: int x Obs int I- z.l : int
The term run(t[z.l/x]), obtained by substituting z.l, is not well-typed be-
cause it has a free variable z of a non-applicative type. This problem is famil-
iar from Reynolds' Syntactic Control of Interference [Rey78] as well as linear
logic-based languages [Wad93]. We present a refined type system for ILC in
Section 5 where this problem is resolved.

3 n.C as a Programming Language


ILC can be used as a programming language in different styles. It can be used
as a purely applicative language by restricting oneself to applicative types. It
can be used as a purely imperative language by mainly using observers (this re-
quires a continuation-passing style of programming). These styles correspond
to traditional programming paradigms.
ILC also permits an interesting new style of programming. It permits
closed imperative observers to be embedded in applicative terms (via the rule
Obs-elim). Applicative terms can be freely embedded in imperative observers
(via the rule Obs-intro). Higher-order functions and laziness can be used to
glue together both imperative and applicative subcomputations, though im-
perative computation is restricted to continuation-passing style.
One extreme of this paradigm is to use ILC with imperative observers at the
top level, but with nontrivial applicative subcomputations involving higher-
order functions. This use is similar to that of HASKELL where state-oriented
input/output operations are usually carried out at the top level. More gen-
erally, ILC can be used with imperative observers embedded in applicative
expressions (via the rule Obs-elim). This corresponds to the use of side-effect-
free function procedures in ALGOL-like languages.
The examples in this paper further illustrate this style of programming.
Example 1 (factorial) displays how imperative computations can be embedded
Vipin Swarup, Uday S. Reddy, and Evan Ireland 243

in applicative terms. Example 2 (a movable-point object) exhibits how imper-


ative computations can be encapsulated as closures. The unification example
of Section 7 demonstrates how the laziness of observers permits them to be
passed to functions and returned as results.

3.1 Syntactic sugar for dereferencing


The need to use get's for dereferencing is rather tedious: it forces us to choose
new names and, more importantly, it clutters up the code. The tedium can be
alleviated to a large extent through a simple notational mechanism.
Abbreviation: If (get x ¢= e in t[x)) is an observer term with no occurrence of
x in a proper Obs-typed subterm of t, then we allow it to be abbreviated
as t[et]. The notation el maybe read informally as "the current content
of reference e".
Expansion: If t is an observer term with a particular occurrence of e I, then it
is expanded by introducing a "get" at the smallest observer subterm of
t containing the occurrence of e I .
The intuition behind this abbreviation is as follows: an observer term
(get x ¢= e in t) is a program point at which the content of e is observed, while
occurrences of x in t are program points at which that content is used. If e
is never modified between the point of dereference and a point of use, then
we can safely view the dereference as taking place at the point of use. For
example,
(p:= nl * pI; n:= nl -1; c)
= get x n in get y <= p in
¢=

p:= x * y;
get z ¢= n in
n:= z -1; c

III = (get x ¢= I in xl) = (get x ¢= I in get y <= x in y)

f(ll) = (get x <= I in f(x» if f: Tl - Obs T2

f(lI) = f(get x ¢= 1 in x) if f: Obs Tl - Obs T2


The third example is reminiscent of "call by value" of LISP-like languages and
the fourth is reminiscent of "call by name" of ALGoL-like languages.

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

Point = {x_coord: Cmd Real


y _coord: Cmd Real
move: (Real x Real) - Cmd unit
equal: Point - Cmd BooI}
mkpoint: Real x Real - Cmd Point
= i\(x,y).
ref x t> i\xc.
ref y t> i\yc.
result{x_coord = deref xc,
y_coord = deref yc,
move = i\(dx, dy).
deref xc t> i\x.
deref yc t> i\y.
assign xc (x + dx) t> i\_.
assign yc (y + dy),
equal = i\{x.-coord,y_coord, move, equal}.
x_coord t> i\x' •
y_coord t> i\y'.
deref xc t> i\x.
deref yc t> i\y.
result (x' = x and y' = y)

Figure 2: Point example using command notation

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

the restriction on storable values with its attendant limitations of expressive-


ness.
On the other hand, we envisage that any practical programming language
based on ILC would relax this restriction and make the expressiveness of
storable objects available to the programmer. TIlis is easy enough to do. One
simply erases the distinction between mutable and imperative types and de-
fines:
00 ::= f3 I Ref 00 lObs T I WI X 002 I WI - 002

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

form, just as a lambda abstraction is in head normal form. This is a form of


laziness and, in fact, directly corresponds to ALGOL'S call by name.
Finally, we wish the type system to ensure that all recursion-free terms are
strongly normalizable, i.e., their evaluation always terminates. The implica-
tions of this criterion have been mentioned in Section 3.4.
The type system described thus far is overly restrictive. It prohibits all
non-sequential combinations of observers in order to ensure that the state is
never copied. For example, a term of the form
(v := 0; (get x <= v in x) + (get x <= v in x»
is excluded because the observer arguments of + are combined non-
sequentially. However, this term does not require the state to be copied since
the arguments of + do not locally modify the state. This suggests that the
type system could be relaxed by distinguishing between:
• creators, that extend the state;
• readers, that observe the state without extending or modifying it; and
• mutators, that modify the state.
The type system could then permit certain state-dependent terms to be com-
bined. For example, two readers could be safely combined. To be effective,
such a solution would also have to incorporate a comprehensive type system
that captures "effects" of expressions on "regions" of references (similar to
that of FX [LG88)). This would permit combining mutators that mutate disjoint
regions of the state. We do not explore this solution in this paper because it
is orthogonal to the issues considered here. It is also clear that such a type
system does not completely eliminate the need for sequencing.

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

Variable hypothesis Weakening


IIlff-e:w
Iz:wf-z:w II,II' I f,r' f- e: 00

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

Figure 3: Salient type rules of ILCR

does not satisfy the criterion. There is no applicative-typed subterm enclosing


the occurrence of z.
Figure 3 shows the salient type rules of ILCR: All other type rules of the
original ILC type system are adapted to ILCR by merely adding an applicative
zone in all typing contexts. For example, the Creation rule becomes:
II I r, v: Ref 8 f- e: 8 II I r, v: Ref 8 f- t: ObsT
II I r f-Ietref vII := e in t : Obs T
The Obs-elim rule is modified to allow the running of any term of type
App (Obs T), using the intuition that such a term is state-independent. The
rule for the introduction of App is Promotion. It says that any term of
type w can be regarded as being of type App w ("promoted") provided
all its free variables have been applicatively used. For example, the term
retum(z.l) of judgment (1) can be promoted to App (Obs int) because z is
applicatively used in that term. The function while can be given the type
App (Cmd Bool - Cmd unit - Cmd unit) because it has no free variables and
so, vacuously, all its free variables are applicatively used. The rule Dereliction
allows the reverse conversion from App w to w. There are no constraints on
this conversion.
The rules Co-promotion and Co-dereliction are the only rules that manip-
ulate the applicative zone in an essential way. 3 By Co-promotion, any free
variable z of an applicative-typed term can be moved to the applicative zone.
Given that this is the only way for a variable to enter the applicative zone
(other than Weakening), we have a formalization of the intuition that all the
variables in the applicative zone are applicatively used. The rule Co-dereliction
30'Hearn et al. [OPTI95) call similar rules in their type system "Passification" and "Activation."
The names "Co-promotion" and "Co-dereliction" are motivated by imagining that all types in the
applicative zone have an implicit type constructor, say 0/. Then, Co-promotion introduces 0/ and
Co-dereliction eliminates 0/ in a fashion similar to Promotion and Dereliction, but to the left of f-.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 251

allows a variable z to be moved from the applicative zone to the unrestricted


zone (without any constraints). This rule is necessary, for instance, for writing
pair terms (t1, t2) where z may be used applicatively in t1, but not in t2.
Finally, the rule Weakening allows one to add variable hypotheses to both
of the zones.
This type system has the substitution property. A direct proof of the re-
sult is somewhat complicated. The following two-step approach is easy to
follow. Recall that an inference rule is said to be admissible if whenever all
its antecedents are derivable in the original inference system, the consequent
is also derivable in the original inference system. An admissible rule can be
safely added to the inference system without altering the set of derivable judg-
ments.
Lemma 1 (Contraction) The following rules are admissible:

TI,Z:w' I f I- e[zjZ1.zjz2]: w TI I z:w',f I- e[zjZ1.zjz2]: w


The proof is by simultaneous induction on the derivation of e. Note that we
do not use contraction in the unrestricted zone. Since distinct reference vari-
ables must denote distinct references, contraction of reference variables is not
meaningful in general. However, reference variables in the applicative zone
can be contracted as they carry no (applicative) information.
Lemma 2 (Semi-linear substitution) The following rules are admissible:
TIl I f1.f,z:w' I- e: w TI2 I f2,f I- t: w'
TI1.TI2 I f1,f2,f I- e[tjz] : w

TI1.z:w' I f1.f I- e: w TI2 I f2,f I- t: w'


TI1.TI2,f2 I f1.f I- e[tjz] : w
The proof is again by simultaneous induction on the derivation of e. Note that
when substituting for an applicative free variable (the second rule), the free
variables introduced by the substitution (f2) can be regarded as applicative
free variables.
Theorem 3 (Substitution) The following rules are admissible:
TIlf,z:w'l-e:w TIlfl-t:w' TI,z:w'lfl-e:w TIlfl-t:w'
TI I f I- e[t j z] : w TI I f I- e[t j z] : w

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

(1) (.:\xw. el )(e2) ede2/x]


(2) (elo e2}.i ej for i = 1,2
(3) run (return(u) ) u
(4) lettef vO := e in return(u) u ifvf/;V(u)
(5) lettef vO := e in lettef vO := e in
getxO = yin t in t[e/x]
(6) lettef vO := e in getxO' = w in
get xO' = w in t lettef vO := e in t
(7) v := e; return(u) u
(8) v:= e; v := e; t[e/x]
getxO = vint
(9) v:= e; getxO = w in
getxO = w int v:= e; t
(10) rec(e) e(rec(e) )

Figure 4: Reduction rules

6.1 Reduction semantics


The operational semantics of applicative languages are usually understood in
terms of reduction. Reduction is a binary relation - on terms (with identical
typings) such thatwhenevere - e', we have t[e] - t[e'] for all term contexts
t[]. A reduction relation serves as an operational semantics if every closed
term of a primitive type (a "program") either reduces to a constant of that
type or every reduction sequence for that term diverges. When applicative
languages are extended with side effects, often the reduction approach is given
up and the operational semantics is described by other means (such as state
transition relations). An important property of ILC is that it has a reduction
semantics, even though it contains assignments and other state-manipulation
operations.
The reduction semantics of ILC is axiomatized in Figure 4. We assume that
all terms are in hygienic form (all bound variables are pairwise distinct and dis-
tinct from free variables), and use V(t) to denote the set of free variables of a
term t. The reduction rules propagate get terms outward until they encounter
a letref or assignment, and then discharge the get construct. The letref and
assignment constructs can be discharged only after the state observation in
their body is completed, i.e., after the body reduces to a return-term. Rules (4)
and (7) handle this situation.
Let"!' be the reflexive, transitive closure of - . We can easily show that
one-step reduction preserves types, and by induction, so does ..!..
Lemma 4 (Subject reduction) Treat the reduction relation of Figure 4 as act-
ing on pre terms. If (II I r I- s: w) is a term, and s - t, then (II I r I- t: w) is a
term.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 253

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

6.2 Denotational semantics


The first concern in giving a denotational semantics to the ILCR type system
is to make explicit the fact that a computation of an applicative type cannot
"use" the non-applicative information of its free variables. This requires us to
give semantic content to the informal intuitions of "applicative computation"
and "applicative information." We use the notion of projections to formalize
these ideas.
A projection on a Cp04 D is a continuous function 7T: D - D such that
idD !;;; 7T
7T07T=7T
where idD is the identity function on D. The image of D under 7T, denoted
7T(D), is then a sub-cpo of D. The restriction 7T t 7T(D) is the identity on
7T(D). If D is a (Scott) domain, and 7T is such that 7T(D) is also a domain, then
7T is called a finitary projection. All our projections will be finitary.
The intuition in using projections is that the sub-cpo 7T(D) contains all the
elements of D that satisfy some property. The projection 7T associates, to each
xED, a unique element 7T(X) E 7T(D) that captures all the information of x
consistent with the property. For example, consider the projection 7T: 71.1. - 71.1.
that sends all even numbers to themselves and the odd numbers to .L. The pro-
jection captures the "evenness" property: 7T(D) contains only even numbers
and 7T identifies all the "even information" of any given element. Projections
and their cousins, closures, have been used to model types [Sc076, ABL86J and
static-analysis properties [WH87, Lau91J.
If (D, 7TD) and (E, 7TE) are cpo's with chosen projections, a continuous func-
tion f: D - E is said to be projection-reflecting if
7TE 0 f 0 7TD = 7TE 0 f
This captures the intuition that, for any element xED, the "7TE-information"
of f(x) comes from the "7TD-information" of x. The function f is said to be
projection-preserving if f 0 7TD = 7TE 0 f. All projection-preserving functions are
projection-reflecting, but the converse fails in general.
Lemma 8 Cpo's with projections and projection-reflecting functions form a
Cartesian closed category. The full subcategory of pointed cpo's forms a Carte-
sian closed subcategory with least fixed point operators fix D : (D - D) - D for
each (D, 7TD).
The products of projections are given by
(D,7TD) x (E,7TE) = (D x E, 7TD x 7TE)
The function space of (D, 7TD) and (E, 7TE) is [D - EJpr consisting of all
projection-reflecting functions with the associated projection 7TD-E (f) =
7TE 0 f. The least fixed point operator fixD: [D - DJpr - D is the same as for
cpo's and is projection-reflecting. Note that this gives the structure needed to
interpret a typed lambda calculus with recursion.
4We use the term ·cpo" to mean a directed-complete partial order. A cpo with a least element
is termed a ·pointed" cpo.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 255

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)

The "empty state" with dom(a) = 0 is denoted ao.


The cpo DObsw is the continuous function space [State - (Xw(D w )]. Asso-
ciate the projections £XObs w = 110bs w = T. So, an observer has no mutable or
applicative information.
A product type 001 x 002 is interpreted as the domain DWI x DW2 with
associated projections £X WI X £X W2 and I1wl x I1 w 2' A function type 00 - 00' is
interpreted as the projection-reflecting function space [Dw - D w ' ]pr with the
associated projections given by (Xw-w' (f) = £Xw' 0 f and I1w-w' (f) = I1w' 0 f.
The type App 00 is interpreted as the imperative domain DAPPW =
(D w , id,id). All the information of this domain is applicative.
To define meanings of terms (II I r I- e: 00), we define a notion of environ-
ments. An environment 17 for (II I n is a finite map from variables typed in
(IT In to values (in Uw Dw) such that
• for all (z:w) E IT, 17(z) E (Xw(D w ), and

• for all (z:w) E r, 17(Z) E Dw.

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

Strx = { td~-E' ifx=l.


otherwise
The use of Str allows all the state effects of observers to be carried out imme-
diately without delay. However, the values stored in or retrieved ffom the state
require delayed evaluation. The semantic equations associated with structural
rules define the meaning of the consequent of the rule in terms of that of the
antecedent. For the Weakening, Co-promotion, Promotion and Dereliction rules,
the meaning is not altered except for a change of type. For the Co-dereliction
rule, the applicative information of the variable z is projected.

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

[I f-- k: TIll = Const(k)


llz:wf--Z:W]11 = I1(Z)
[ill f f-- (.\xw. e): 00 - 00']11
= .\d E Dw. [ill f,x: 00 f-- e: w'](I1[x - d])
[n I ff-- ele2:w'JI1 = ([n Iff-- el:W - W'I11)([n Iff-- e2:wl11)
[n Iff-- (el. e2): WI x 002111
(llli f f-- el: wdl1, [n Iff-- e2: 002]11)
[ill f f-- e.1: Wdl1 fst([n Iff-- e: WI x 002] 11)
[lll f f-- e.2: 002]11 snd([n Iff-- e: WI x 002] 11)
[n Iff-- rec(e):w]11 fixvw([n Iff-- e:w - wJI1)
[n Iff-- run(e): T]11 [n Iff-- e:App (Obs T)]11 0-0
[n Iff-- retum(e):Obs T]11
= .\0-. [n Iff-- e: T]11
[n Iff-- (letref vO := e in t):Obs T]11
.\0-. [ill f, v: Ref 8 f-- t:ObST](I1[V -1])(0-[1- d])
where I = alloco(dom(o-»
and d = [ill f, v: Ref 8 f-- e:8](I1[v-1])
[n Iff-- (get x O <= e in t): Obs T]11
= .\o-.Str I ([ill f,x: 8 f-- t: Obs T](I1[X - 0-(1)])0-
where I = [lll f f-- e: Ref 8]11
[n Iff-- (el := e2 ; t): Obs T]11
.\0-. Str I ([n Iff-- t: ObST]11 (o-[l - d])
where 1= [lll f f-- el:Ref8]11
and d = [n Iff-- e2: 8]11
Weakening:
lll,n' I f,f' f-- e:w]11 [n Iff-- e:w](11 t (n If))
Co·promotion:
[ll,z:w Iff-- e:T]11 llli z:w,f f-- e:T]11
Co-dereliction:
[n I z:w,f f-- e:w'JI1 lll,z:w Iff-- e:w'](I1[Z - /Xw(I1(Z»])
Promotion:
llli f-- e:ApPw]11 [n I f-- e:w]11
Dereliction:
[n Iff-- e:w],., = [lll f f-- e:App wJI1

Figure 5: Denotational semantics


258 Chapter 9. Assignments for Applicative Languages

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).

7 Extended Example: Unification


To illustrate the expressive power and usability of the language, we implement
unification by an algorithm that performs shared updates on a data structure.
Unification [Rob65) is a significant problem that finds applications in diverse
problems including type inference, implementation of PROLOG and theorem
provers, and natural semantics.
Figure 6 contains an ILC program that computes the most general common
instance of two terms tl and t2. A term is either a variable or a pair denoting
the application of a function symbol to a list of sub terms. A variable is repre-
sented by a reference. The reference may contain either a term (if the variable
is already bound), or the special value Unbound. This representation of terms
illustrates the notion of shared dynamic data mentioned in Section 1; a func-
tion accepting a term has indirect access to all the references embedded in the
term.
6Thus the claimed soundness result in the original version of this paper is false.
Vipin Swamp, Uday S. Reddy, and Evan Ireland 259

datatype term Var of Refvar


Apply of (symbol x list term)

and var Unbound I Bound of term

unify: term x term x Obs T x Obs T - Obs T


= A(t, u, sc, fc). unify·aux(t, u, sc, undo(fc»)

unify-aux: term x term x Obs T x Obs T - Obs T


= A(t, u, sc, fc).
case (t, u) of
I (Var(vl), u) => bind(vl, U, sc, fc)
I (Apply(f, ts), Var(v2») => bind(v2, t, sc, fc)
I (Apply(f, ts), Apply(g, us») =>
if (f = g) then unify-lists(ts, us, sc, fc) else fc

unify-lists: list term x list term x Obs T x Obs T - Obs T


= A(lt, lu, sc,fc). case (It,lu) of
([], []) => sc
(t :: ts, u:: us)=>
unify-aux(t, u, unify-lists(ts, us, sc, fc), fc)
C,) => fc

bind: Ref var x term x Obs T x Obs T - Obs T


= A(v, u, sc, fc). case v! of
Unbound => occurs (v, u, fe, (v := Bound(u);
sigma:= v:: sigma!;
sci)
Bound(t) => unify·aux(t, u, sc, fc)

undo: Obs T - Obs T


= Afc. case sigma! of
[] => fc
v:: vs => v:= Unbound;
sigma:= vs;
undo (fc)

occurs: Ref var x term x Obs T x Obs T - Obs T

Figure 6: Unification of first-order terms

We assume the existence of a global reference called sigma that accumu-


lates the list of references bound during an attempt at unification. If the unifi-
cation is successful, this yields the most general unifier, while the representa-
tions of the terms tl and t2 correspond to the most general common instance.
On failure, this list is used to reset the values of the references to Unbound.
The function unify attempts to compute the most general common in-
stance of two terms t and u. If the unification is successful, it instantiates
both t and u to their most general common instance (by updating the ref-
260 Chapter 9. Assignments for Applicative Languages

erences embedded in them), and evaluates the success continuation sc. If


the unification is unsuccessful, it leaves the terms unchanged and evaluates
the failure continuation {c. Internally, it uses the auxiliary function unify-aux
which updates the terms in both cases. By providing the failure continuation
(undo (c) to this function, terms are restored to their original values upon fail-
ure. The function unify-lists unifies two lists of terms (it, lu), bind unifies a
variable v with a term u, occurs checks whether a variable v occurs free in a
term u, and undo resets the values of variables that have been bound during
a failed attempt at unification. The definition of occurs is straightforward and
has been omitted. '
The significant aspect of this program is that when an unbound variable is
unified with a term that does not contain any free occurrence of the variable,
unification succeeds by assigning the term to the reference that represents the
variable (see function bind). This modification is visible via other access paths
to the reference. It is this information sharing that affects the unification of
subsequent subterms, even though no values are passed between these pro-
gram points. In contrast, every computed value in a pure functional language
needs to be passed explicitly to all program points that need it. In the unifi-
cation example, this means that whenever a variable is modified, the modified
value needs to be passed to all other subterms that are yet to be unified, an
expensive proposition indeed.

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.

Lambda-var and state threads

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.

Acknowledgements We thank John Gray, Jim Hook, Matthias Felleisen, Sam


Kamin, Dave MacQueen, Ian Mason, Martin Odersky, Peter O'Hearn, Dan Ra-
bin, John Ramsdell, John Reynolds, Peter Sestoft, Harald Sondergard, Jonathan
Springer, Carolyn Talcott, Satish Thatte and Phil Wadler for numerous discus-
sions which led to improvements in our presentation.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 263

Appendix: Possible-World Semantics


The basic idea is that all meanings must be parameterized by sets of locations cur-
rently in use (referred to as worlds). The meaning of a term [ll I r I-- e: w] in world X
maps environments 17 using locations in X to w-typed values using locations in X. For
this to make sense, the interpretations of types must be parameterized by worlds X.
When a new location is allocated using letref, the world needs to be expanded. The
meaning [letrer v O := e in t] in a world X must be defined in terms of the meaning
[t] in world X u {alloco(X)}. For this to work, we must specify how the values of each
type are affected by world expansion.
As pointed out by Reynolds and Oles [Rey8l, Ole82] (see also [OT92]), this can be
aptly formalized by treating worlds as forming a small category, interpreting types
as functors from the category of worlds to semantic domains, and interpreting terms
as natural transformations between such functors. By interpreting types w as func-
tors Fw , we specify a semantic domain Fw(X) for each world X, and a function
Fw (i): Fw (X)-Fw(X') for each world-expansion i:X -X'. The natural-transformation
meaning of a term, say cp:F"':' G, associates a function cpx:F(X) - G(X) with each
world X such that the functions behave consistently with world expansions.
Our category of worlds, World, has sets of locations X s;; Loc as the objects. The
morphisms i: X - X' are injective partial functions. The intuition is that an injective
function i: X - X' denotes the expansion of world X to X' with potential renaming of
locations in X. Since our morphisms are partial functions, they also allow locations
to be dropped, amounting to a "restriction" of the world. Such restrictions play a
role in interpreting promoted terms which must not access any locations. The idea of
combining expansions and restrictions in world morphisms appears in [Ten90]. It also
plays a crucial role in [OP1T95].
If i: X - X' is a morphism, we write dom(i) for the subset of X for which i is
defined, and range(i) for the image of dom(i) in X'. Note that i reduces to a bijection
between dom(i) and range (i). If i: X - X' is a morphism, there is a unique morphism
iR:X' - X in the reverse direction such that dom(iR) = range(i), range(i R ) = dom(i)
and the action of i R on dom(i R ) is the inverse of i. There is a definedness partial order
S;;x -x' on morphisms X - X', which is preserved by the ( )R operation.
For modelling ILC, we must associate projections with functors. Define an
"imperative functor" to be a triple (F,IXF,/JF) where F:World - Cpo is an order-
preserving functor and IXF,/JF:F ...:. F are natural projections, i.e., for each world X,
IXF[X], /JF[X]:F(X) - F(X) are projections, such that
• each F(X) is a pointed cpo and each F(i:X - X') is a strict function,
• IXF[X) ~ /JF[X] and
• the condition (3) below holds.
The role of projections IXF and /JF is the same as in the denotational model.
The fact that they are natural transformations means that every F(i:X - X') is
projection-preserving and, hence, also projection-reflecting. Terms will be modelled by
projection-reflecting natural transformations cp: F ...:. G such that IXG 0 cp 0 IXF = IXG 0 cp
and /JG 0 cp 0 /JF = /JG 0 cpo
Notice that, for each world X, the projection IXF[X] identifies the applicative in-
formation of the elements of F(X). There is another notion of "applicative" in the
possible-world model; For each world X, there is an "empty" morphism zerox:X - X
with dom(zerox) = 0. This morphism makes all the locations in X inaccessible.
Hence, one would expect that F(zerox):F(X) - F(X) also identifies the applicative
information of the elements of F(X). (It is easy to verify that it is a natural projection.)
However, there is a subtle distinction between DCF[X] and F(zerox). The former iden-
tifies the information that may be used in applicative computations whereas the latter
264 Chapter 9. Assignments for Applicative Languages

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)

F(Y') cp[koj]. G(Y')

These families are ordered pointwise (cp !;!fJ =


V' j:X - Y. cp[j] !; !fJ[j]) to form a
cpo. The action of F '* G on morphisms i: X - X' is
(F,* G)(i):[V'Y - X.F(Y) - G(Y)] - [V'Z - X'.F(Z) - G(Z)]
(5)
(F,* G)(i)(cp) = {!fJ[j']}j':x'-z where !fJ[j'] = cp[j' 0 i]
This functor is associated with the natural projections DeF=>G[X](cp)[j] = DeG[Y] ocp[j]
and IlF=>G[X](CP)[j] = JlG[Y] 0 cp[j]. The fixed point operator fixF: (F '* F) ....:.. F is
given by (fixF)X(CP) = fixF(X) (.\x E F(X). cp[idx ](x».
We interpret ILC types w as imperative functors Fw = (Fw , Dew, Jlw). The Cartesian
closed structure interprets the typed lambda-calculus part: FOOl XW2 = FOOl X FW2 and
Fw - w' = Fw '* Fro"
For primitive types 13, the interpretation is the constant functor FfI(X) = DfI,
FfI(i) = idDJl with identity projections: Defl[X] = Jlfl[X] = idD/l' Note that the in-
terpretation Is independent of the world. This will be the case for all applicative types.
7The fact that F(zerox) is a natural projection and. hence. a natural split-idempotent means
that it gives rise to a bireflective subcategory of World-Cpo [FOP+951. which is in fact equivalent
to Cpo. We could. in principle. identify ocF[Xl with F(zerox) and use this bireflective structure
for modelling the structural rules of n.c. Such a model would be less accurate in that it makes no
distinction between "applicatively definable" and "applicatively usable.'
Vipin SwaruP. Uday S. Reddy. and Evan Ireland 265

For reference types Ref 00, the interpretation is


FRefw(X) (XnLocwh

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»),

• for all (Z: W) E r, I1(Z) E Fw (X), and


• for all (v: Ref 6), (v': Ref 6) E r, if l7(v) *' .L and l7(v') *' .L then l7(v) *' l7(v').
Define a functor Fnlr such that FllIr (X) is the imperative domain of such environments
and FnlrCi) is the pointwise extension of Fw's.
The meaning of a term n I r f- e: 00 is a natural transformation [n I r f- e: w]
from Fnlr to Fw. That is, for every world X, there is a projection-reflecting function
266 Chapter 9. Assignments for AppJicative Languages

['\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'/

Figure 7: Selected equations of possible-world semantics

[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)

Fnlr(X') [eh'. Fw(X')


The naive denotational semantics of Figure 5 can now be restated in this setting.
The mcyority of the semantic equations are simply restated as natural transformations
as they do not involve any change of world, e.g.,
[IT ! r f- (el. e2): WI x W2]x 1'/ = ([IT! r f- el: wdx T/, [IT ! r f- e2: w2h 1'/)
The equations that differ significantly are shown in Figure 7. For readability, we intro-
duce some abbreviations. The meaning function [IT! r f- e:w] is abbreviated to [e].
The functions Fw(j) corresponding to world morphisms j are abbreviated to jw, or
simply j. The naturality equations (4-7) are stated in this notation as:
k(cf>[j](x») cf>[k 0 j](kx) (8)
i(cf»[j'] cf>[j' 0 i) (9)
k( f/I[j)(u}) ;;! f/I[k 0 j](ku) (10)
i([e]xl'/) [eh' (il'/) (11)
Vipin Swarup, Uday S. Reddy, and Evan Ireland 267

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

[OPTI95) P. W. O'Hearn, A. j. Power, M. Takeyama, and R. D. Tennent. Syntactic con-


trol of interference revisited. In Brookes et al. [BMMM95). See Chapter 18.
[ORH93) M. Odersky, D. Rabin, and P. Hudak. Call by name, assignment and the
lambda calculus. In Twentieth Annual ACM Symp. on Principles of Pro-
gramming Languages ACM, 1993.
[OT92) P. W. O'Hearn and R. D. Tennent. Semantics of local variables. In M. P. Four-
man, P. T. Johnstone, and A. M. Pitts, editors, Applications of Categories in
Computer SCience, pages 217-238. Cambridge University Press, 1992.
[Per90) N. Perry. The Implementation of Practical Functional Programming Lan-
guages. PhD thesis, Imperial College, London, 1990.
[PL95) S. L. Peyton Jones and J. Launchbury. State in HASKELL. ]. of LIsp and
Symbolic Computation, 8(4):293-341, 1995.
[Pra70) D. Prawitz. Ideas and results in proof theory. In Proc. Second Scandinavian
Logic Symposium. Springer-Verlag, 1970.
[Rab96) D. Rabin. Calculi for Functional Programming Languages with Assignment.
PhD thesis, Yale University, 1996.
[Red96) U. S. Reddy. Global state considered unnecessary: An introduction to
object-based semantics. ]. of LIsp and Symbolic Computation, 9:7-76, 1996.
See Chapter 19.
[RC85) J. Rees and W. Clinger. The Revised 3 report on the algorithmic language
SCHEME. Technical report, MIT AI Laboratory, 1985.
[Rey78) J. c. Reynolds. Syntactic control of interference. In ACM Symp. on Principles
of Programming Languages, pages 39-46. ACM, 1978. See Chapter 10.
[Rey81) J. c. Reynolds. The essence of ALGOL. In J. W. de Bakker and j. C. van Vliet,
editors, Algorithmic Languages, pages 345-372. North-Holland, 1981. See
Chapter 3.
[Rey82) J. C. Reynolds. IDEALIZED ALGOL and its specification logic. In D. Neel,
editor, Tools and Notions for Program Construction, pages 121-161. Cam-
bridge Univ. Press, 1982. See Chapter 6.
[Rey88) J. c. Reynolds. Preliminary design of the programming language FORSYTHE.
Technical Report CMU-CS-88-159, Carnegie-Mellon University, June 1988.
See Chapter 8.
[Rey89) J. c. Reynolds. Syntactic control of interference, Part II. In Intern. Col/oq.
Automata, Languages and Programming, volume 372 of Lecture Notes in
Computer Science, pages 704-722. Springer-Verlag, 1989.
[Rob65) J. A. Robinson. A machine-oriented logic based on the resolution principle.
J. ACM, 12:23-41, 1965.
[Sc076) D. Scott. Data types as lattices. SIAM J. Computing, 5(3):522-587, Sept.
1976.
[SK96) J. Springer and S. N. Kamin. Strictness analysis in the imperative lambda
calculus. J. of LIsp and Symbolic Computation, 9(1):109-143, Feb 1996.
[SR92) V. Swamp and U. S. Reddy. A logical view of assignments. In J. P. Myers
and M. j. O'Donnell, editors, Constructivity in Computer Science, volume
613 of Lecture Notes in Computer Science. Springer-Verlag, 1992.
[Sto77) j. E. Stoy. Denotational Semantics: The Scott-Strachey Approach to Pro-
gramming Language Theory. The MIT Press, 1977.
Vipin Swarup, Uday S. Reddy, and Evan Ireland 271

[SW74) c. Strachey and C. P. Wadsworth. Continuations - a mathematical seman-


tics for handling full jumps. Tech. Monograph PRG-H, Programming Re-
search Group, University of Oxford, 1974.
[Swa92) V. Swarup. Type Theoretic Properties of Assignments. PhD thesis, Univ.
Dlinois at Urbana-Champaign, 1992.
[Tai67) W. W. Tait. Intensional interpretation of functionals of finite type I. J. of
Symbolic Logic, 32:662-675, 1967.
[Ten90) R. D. Tennent. Semantical analysis of specification logic. Information and
Computation, 85(2):135-162, 1990. See Chapter 13.
[Tof88) M. Tofte. Operational Semantics and Polymorphic Type Inference. PhD
thesis, University of Edinburgh, 1988.
[Wad90a) P. Wadler. Comprehending monads. In ACM Symp. on LIsp and Functional
Programming, 1990.
[Wad90b) P. Wadler. linear types can change the world! In M. Broy and C. B. Jones,
editors, Programming Concepts and Methods. North-Holland, Amsterdam,
1990. (Proc. IFIP TC 2 Working Conf., Sea of Galilee, Israel).
[Wad91) P. Wadler. Is there a use for linear logic? In Proc. Symp. on Partial Eval-
uation and Semantics-Based Program Manipulation, pages 255-273. ACM,
1991. (SIGPLAN Notices, Sep. 1991).
[Wad92) P. Wadler. The essence of functional programming. In ACM Symp. on
Principles of Programming Languages, 1992.
[Wad93) P. Wadler. A syntax for linear logic. In S. Brookes, editor, Mathemati-
cal Foundations of Programming Semantics: 9th International Conference,
1991, volume 802 of Lecture Notes in Computer Science. Springer-Verlag,
1993.
[WH87) P. Wadler and R. J. M. Hughes. Projections for strictness analysis. In
G. Kahn, editor, Conf. on Functional Programming Languages and Com-
puter Architecture, pages 385-407. Springer-Verlag, 1987.
Chapter 10
Syntactic Control of Interference
John C. Reynolds
In programming languages which permit both assignment and proce-
dures, distinct identifiers can represent data structures which share stor-
age or procedures with interfering side effects. In addition to being a
direct source of programming errors, this phenomenon, which we call
interference, can impact type structure and parallelism. We show how
to eliminate these difficulties by imposing syntactic restrictions, with-
out prohibiting the kind of constructive interference which occurs with
higher-order procedures or SIMULA classes. The basic idea is to prohibit
interference between identifiers, but to permit interference among com-
ponents of collections named by single identifiers.

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

Suppose nand f are called by name as in ALGOL, or by reference as in


FORTRAN, and consider the effect of a call such as fact(z, z), in which both
actual parameters are the same. Then the formal parameters nand f will be
aliases, i.e. they will interfere in the sense that assigning to either one will
affect the value of the other. As a consequence, the assignment f := 1 will
obliterate the value of n so that fact(z, z) will not behave correctly.
In this case the problem can be solved by changing n to a local variable
which is initialized to the value of the input parameter; this is tantamount to
calling n by value. But while this solution is adequate for simple variables, it
can become impractical for arrays. For example, the procedure
procedure transpose(X, Y); real array X, Y;
for i := 1 until 50 do for j := 1 until 50 do
Y(i,j) := XU, i)
will malfunction for a call such as transpose(Z, Z) which causes X and Y to
be aliases. But changing X to a local variable only solves this problem at the
expense of gross inefficiency in both time and space. Certainly, this ineffi-
ciency should not be imposed upon calls which do not produce interference.
On the other hand, in-place transposition is best done by a completely differ-
ent algorithm. This suggests that it is reasonable to permit procedures such
as transpose, but to prohibit calls of such procedures with interfering param-
eters.
Although these difficulties date back to ALGOL and FORTRAN, more recent
languages have introduced new features which exacerbate the problem of in-
terference. One such feature is the union of data types. Suppose x is a variable
whose value can range over the union of the disjoint data types integer and
character. Then the language must provide some construct for branching on
whether the current value of x is an integer or a character, and thereafter
treating x as one type or the other. For example, one might write
unioncase x of (integer: 5; character: 5') ,
where x may be used as an identifier of type integer in 5 and as an identifier
of type character in 5'. However, consider
unioncase x of (integer: (y :="A"; n := x + 1); character: noaction) .
It is evident that aliaSing between x and y can cause a type error in the ex-
pression x + 1. Thus, in the presence of a union mechanism, interference can
destroy type security. This problem occurs with variant records in PASCAL [11,
and is only avoided in ALGOL 68 [21 at the expense of copying union values.
The introduction of parallelism also causes serious difficulties. Hoare [3,41
and Brinch Hansen [5] have argued convincingly that intelligible programming
requires all interactions between parallel processes to be mediated by some
mechanism such as a critical region or monitor. As a consequence, in the
absence of any critical regions or monitor calls, the parallel execution of two
statements, written 51 II 52, can only be permitted when 51 and S2 do not
interfere with one another. For example,
x := x + 1 II y := y x 2
would not be permissible when x and y were aliases.
John C. Reynolds 275

In this paper, we will not consider interacting parallel processes, but we


will permit the parallel construct S1 II S2 when it is syntactically evident that
S1 and S2 do not interfere. Although this kind of determinate parallelism is
inadequate for practical concurrent programming, it is sufficient to make the
consequences of interference especially vivid. For example, when x and yare
aliases, the above statement becomes equivalent to
Z := Z + 1 II Z := Z x2
whose meaning, if any, is indeterminate, machine-dependent, and useless.
These examples demonstrate the desirability of constraining a language so
that variable aliaSing is syntactically detectable. Indeed, several authors have
suggested constraints which would eliminate aliaSing completely [6,7].
However, aliasing is only the simplest case of the more general phe-
nomenon of interference, which can occur between a variety of program
phrases. We have already spoken of two statements interfering when one can
perform any action which affects the other. Similarly, two procedures inter-
fere when one can perform a global action which has a global effect upon the
other.
Interference raises the same problems as variable aliaSing. For example,
P(3) II Q(4) is only meaningful if the procedures P and Q do not interfere.
Thus the case for syntactic detection extends from aliasing to interference
in general. However, the complete prohibition of interference would be un-
tenably restrictive since, unlike variables, interfering expressions, statements,
and procedures can have usefully different meanings.
Both the usefulness and the dangers of interference between procedures
arise when procedures are used to encapsulate data representations. As an
example, consider a finite directed graph whose nodes are labelled by small
integers. Such a graph might be represented by giving, for each node n, a
linked list of its immediate successors nI, ... , nk:
nodelist item link

)
n1

I--------l~
n 1-----1
n2

nk 0

This representation is used by the procedure


procedure itersucc(n, p); integer n; procedure p;
begin integer k;
k := nodelist(n);
while k f= 0 do begin p(item(k»); k:= link(k) end
end
276 Chapter 10. Syntactic Control of Interference

which causes the procedure p to be applied to each immediate successor of


the node n.
If the graph is ever to change, then something-probably a procedure such
as "addedge" or "deleteedge" -must interfere with itersucc by assigning to the
global arrays node list, item, and link. On the other hand, the correct operation
of itersucc requires that the procedure parameter p must not assign to these
arrays, i.e. that p must not interfere with itersucc. Indeed, if itersucc involved
parallelism, e.g. if the body of the while statement were
begin integer m; m:= item(k);
begin p(m) II k:= link(k) end
end,
then nOninterference between p and itersucc would be required for meaning-
fulness rather than just correctness.
Of course, the need for interfering procedures would vanish if the graph
representation were a parameter to the procedures which use it. But
this would preclude an important style of programming-epitomized by
SIMULA 67 [B)-in which data abstraction is realized by using collections of
procedures which interfere via hidden global variables.
In summary, these examples motivate the basic goal of this paper: to de-
sign a programming language in which interference is possible yet syntacti-
cally detectable. To the author's knowledge, the only current language which
tries to meet this goal is EUCliD [7). The approach used in EUCliD is quite dif-
ferent than that given here, and apparently precludes procedural parameters
and call-by-name.

2 The Basic Approach


Before proceeding further, we must delineate the idea of interference more
precisely. By a phrase we mean a variable, expreSSion, statement, or proce-
dure denotation. In the first three cases, we speak of exercising the phrase P,
meaning: either assigning or evaluating P if it is a variable, evaluating P if it is
an expression, or executing P if it is a statement.
For phrases P and Q, we write P # Q to indicate that it is syntactically
detectable that P and Q do not interfere. More precisely, # is a syntactically
decidable symmetric relation between phrases such that:
1. If neither P nor Q denotes a procedure, then P # Q implies that, for all
ways of exercising P and Q, the exercise of P will have no effect on the
exercise of Q (and vice-versa). Thus the meaning of exercising P and Q
in parallel is well-defined and determinate.
2. If P denotes a procedure, AI, ... ,An are syntactically appropriate actual
parameters, P # Q, and Al # Q, ... , An # Q, then P (AI, ... ,An) # Q.
(Thus P # Q captures the idea that P cannot interfere with Q via global
Variables.)
It should be emphasized that these rules have a fail-safe character: P # Q im-
plies that P and Q cannot interfere, but not the converse. Indeed, the rules are
John C. Reynolds 277

vacuously satisfied by defining # to be universally false, and there is a proba-


bly endless sequence of satisfactory definitions which come ever closer to the
semantic relation of noninterference at the expense of increasing complexity.
Where to stop is ultimately a question of taste: P # Q should mean that P and
Q obviously do not interfere.
Our own approach is based upon three principles:
(I) If I # J for all identifiers I occurring free in P and J occurring free in Q,
thenP#Q.
In effect, all "channels" of interference must be named by identifiers. For the
language discussed in this paper, this principle is trivial, since the only such
channels are variables. In a richer language, the principle would imply, for
example, that all I/O devices must be named by identifiers.
(II) If I and J are distinct identifiers, then I # J.
This is the most controversial of our principles, since it enforces a particular
convention for distinguishing between interfering and noninterfering phrases.
Interfering procedures (and other entities) are still permissible, but they must
occur within a collection which is named by a single identifier. (An example
of such a collection is a typical element in a SIMULA [8] class. Indeed, the
idea of using such collections was suggested by the SIMULA class mechanism,
although we will permit collections which do not belong to any class.)
(ill) Certain types of phrases, such as expreSSions, and procedures which do
not assign to global variables, are said to be passive. When P and Q are
both passive, P # Q.
Passive phrases perform no assignments or other actions which could cause
interference. Thus they cannot interfere with one another or even with them-
selves, although an active phrase and a passive phrase can interfere.

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

<00'> ::= <00 - 00'> «w»


<I1(il:Wl •...• in:wn}> ::= (h:<Wl> •...• in:<wn»
<Wk> ::= <I1(il:Wl •...• in:wn}>.ik
<00> ::= if <Boolean exp> then <00> else <00>
<00> ::= Y«w - w>}
Although a formal semantic specification is beyond the scope of this paper.
the meaning of our language can be explicated by various reduction rules. For
lambda expressions. we have the usual rule of beta-reduction:
(AI.P)(Q) => PII-Q
where the right side denotes the result of substituting Q for the free occur-
rences of I in p. after changing bound identifiers in P to avoid conflicts with
free identifiers in Q. Note that this rule implies call by name: if P does not
contain a free occurrence of I then (AI.P)(Q) reduces to P even if Q is non-
terminating or causes side effects. For collection expressions. we have
(h:Plr ...• in:Pn).ik => Pk.
For example.
(inc:n:= n + 1. val:n x n).inc => n:= n + 1.
Again. there is a flavor of call-by-name. since the above reduction would still
hold if n x n were replaced by a nonterminating expression. The fixed-point
operator Y can also be elucidated by a reduction rule:
Y(f) => f(Y(f)} .
In addition to lambda expressions. the only other binding mechanism in
our language is the declaration of new variables. The statement

new I : [~:ger
Boolean
1 in S

has the same meaning as the ALGOL statement

begin [::ger
Boolean
1 I; Send.

By themselves. lambda expressions and new variable declarations are an


austere vocabulary for variable binding. But they are sufficient to permit other
binding mechanisms to be defined as abbreviations. This approach is vital for
the language constraints which will be given below. since it insures that all
binding mechanisms will be affected uniformly.
Multiple-parameter procedures are treated following Curry [9):
P(Alr'" .An} :; P(Ad'" (An)
Mh •...• In}.B :; lI.h .... AIn.B
and definitional forms. including procedure declarations. are treated following
Landin [10):
280 Chapter 10. Syntactic Control of Interference

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

6 Some Unresolved Questions


Our abstract syntax is ambiguous, in the sense that specifying the type of a
phrase does not always spedfy a unique type for each subphrase. For example,
in the original illustrative language, the subphrase if p then x else y might be
either a variable or an expression in contexts such as
z := if p then x else y
(a:if p then x else y, b: 3).b
Similarly, the introduction of passive procedures permits the subphrase
i\s: sta. (s ; s) to have either type sta - sta or sta - p sta in the context
(i\s:sta. (s; s»)(x:= x + 1) .
Although these ambiguities could probably be eliminated, our intuition is to
retain them, while insisting that they must not lead to ambiguous meanings.
Indeed, it may be fruitful to extend this attitude to a wider variety of implicit
conversions.
In normal usage, a procedure call will be active if and only if either the
procedure itself or its parameter are active. Although other cases are syntac-
tically permissible they seem to have only trivial instances. Thus it might be
desirable to limit the program types of procedures to the cases:
DC - p DC' DC - DC'

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.

7 Directions for Further Work


Beyond dealing with the above questions, it is obviously essential to extend
these ideas to other language mechanisms, particularly arrays.
In addition, the interaction between these ideas and the axiomatization of
program correctness needs to be explored. We suspect that many rules of
inference might be Simplified by using a logic which imposes #-preservation
upon substitutions.
A somewhat tangential aspect of this work is the distinction between data
and program types, which obviously has implications for user-defined types.
(Note the absence of this distinction in ALGOL 68 [2].) In less ALGOL-like lan-
guages, data types might have as much structure as program types, and user
definitions might be needed for both "types" of type. Indeed, there may be
grounds for introducing more than two "types" of type.
Finally, these ideas may have implications for the optimization of call-by-
name, perhaps to an extent which will overcome the aura of hopeless ineffi-
ciency which surrounds this concept. For example, when an expression is a
single parameter to a procedure, as opposed to a component of a collection
which is a parameter, then its repeated evaluation within the procedure must
yield the same value (although nontermination is still possible). This suggests
a possible application of the idea of "lazy evaluation" [11, 12].

Appendix: Classes as Syntactic Sugar


In a previous paper, we have argued that classes are a less powerful data abstrac-
tion mechanism than either higher-order procedures or user-defined types [14]. The
greater generality of higher-order procedures permits the definition of classes (in the
reference-free sense of Hoare [13] rather than SIMULA itselO as abbreviations in our
illustrative language. In fact, the basic idea works in ALGOL, although the absence
there of lambda expressions and named collections of procedures makes its applica-
tion cumbersome.
We consider a class declaration with scope S of the form:
class C(DECL; INIT; h : PI. ... ,In: Pn ) in S (1)
which defines C to be a class with component names h, ... ,In. Here DECL is a list
of declarations of variables and procedures which will be private to a class element,
INIT is an initialization statement to be executed when each class element is created,
and each Pk is the procedure named by h, in which the private variables may occur as
globals.
John C. Reynolds 285

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:

let C = '\b:I1(h: WI ••.. • In: Wn) - sta.


(DECL; INIT; b«h:Pl. ...• In:Pn »)
inS.
where b is an identifier not occurring in the original class declaration. and where DECL
must be expressed in terms of new and let declarations. Then we define (2) to be an
abbreviation for
C(,\X:Il(h:WI •...• ln:wn).S') .
As an example. where for simplicity PI and P2 are parameterless procedures:
class counter(integer n; n := 0; inc: n := n + 1. val: n)
in ... newelement k: counter in ... (k. inc; x := k. val)
is an abbreviation for
let counter = '\b: 11 (inc: sta. val: integer exp) - sta.
new n: integer in (n := 0; b( (inc: n := n + 1. val: n»)
in ... counter(,\k:Il(inc: sta. val: integer exp) .... (k. inc; x := k. val)) •
which eventually reduces to
new n:integer in (n:= 0; ... (n:= n + 1; x:= n») .
In the process of reduction. identifiers will be renamed to protect the privacy of n.
The only effect of our interference-controlling constraints is that C must be a
passive procedure. i.e. INIT and Pl •...• Pn cannot assign to any variables which are
more global than those declared by DECL. This ensures that distinct class elements will
not interfere with one another. Otherwise. if C is not passive. then S' in the definition
of (2) cannot contain calls of C. so that multiple class elements cannot coexist.

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

[4] Hoare, C. A. R. Monitors: an operating system structuring concept. Communica-


tions of the ACM 17 (October 1974), pp. 549-557.
[5] Brinch Hansen, P. Structured multiprogramming. Communications of the
ACM 15 (July 1972), pp. 574-577.
[6] Hoare, C. A. R. Procedures and parameters: an axiomatic approach. In Sympo-
sium on the Semantics of Algorithmic Languages (ed. E. Engeler). Springer, Berlin-
Heidelberg-New York, 1971, pp. 102-116.
[7] Popek, G. J., Horning, j. j., Lampson, B. W., Mitchell, J. G., and London, R. L.
Notes on the design of EUCLID. In Proceedings of an ACM Conference on Language
DeSign for Reliable Software, SIGPLAN Notices 12, no. 3 (March 1977), pp. 11-18.
[8] Dahl, O.-j. and Hoare, C. A. R. Hierarchical program structures. In Structured
Programming (O.-J. Dahl, E. W. Dijkstra, and C. A. R. Hoare), Academic Press, New
York 1972, pp. 175-200.
[9] Curry, H. B., and Feys, R. Combinatory Logic, vol. I. North-Holland, Amsterdam
1958.
[10] Landin, P. J. A correspondence between ALGOL 60 and Church's lambda notation.
Communications of the ACM 8 (February and March 1965), pp. 89-101 and 158-
165.
[11] Henderson, P., and Morris, Jr., J. H. A lazy evaluator. In Conference Record of the
Third Annual ACM Symposium on Principles of Programming Languages, Atlanta,
Georgia, January 1976. ACM, New York, 1976, pp. 95-103.
[12] Friedman, D. P., and Wise, D. S. CONS should not evaluate its arguments. In Third
Int'l Colloquium on Automata, Languages, and Programming (eds. S. Michaelson
and R. Milner), Edinburgh, Scotland, July 1976. Edinburgh University Press, Edin-
burgh,1976,pp.257-284.
[13] Hoare, C. A. R. Proof of correctness of data representations. Acta Informatica 1
(1972), pp. 271-281.
[14] Reynolds, j. C. User-defined types and procedural data structures as comple-
mentary approaches to data abstraction. In New Directions in Algorithmic Lan-
guages 1975 (ed. S. A. Schuman). INRIA, Rocquencourt, France, 1975, pp. 157-
168.
Contents of Volume 2
Contributors vii

Part IV FUNCTOR-CATEGORY SEMANTICS 1

11 Functor Categories and Store Shapes 3


Frank]. ales

12 Using Functor Categories to Generate Intermediate Code 13


John C. Reynolds

Part V SPECIFICATION LOGIC 39

13 Semantical Analysis of Specification Logic 41


Robert D. Tennent

14 Semantical Analysis of Specification Logic, 2 65


Peter W. O'Hearn and Robert D. Tennent

Part VI PROCEDURES AND LOCAL VARIABLES 95

15 Full Abstraction for the Second-Order Subset 97


Kurt Sieber

16 Parametricity and Local Variables 109


Peter W. O'Hearn and Robert D. Tennent

17 Operationally-Based Reasoning About Local Variables 165


Andrew M. Pitts

Part VII INTERFERENCE, IRREVERSmILITY, AND CONCURRENCY 187

18 Syntactic Control of Interference Revisited 189


Peter W. O'Hearn, A. John Power, Makoto Takeyama and
Robert D. Tennent

19 Global State Considered Unnecessary 227


Uday S. Reddy

20 Linearity, Sharing and State 297


Samson Abramsky and Guy McCusker

21 The Essence of PARALLEL ALGOL 331


Stephen Brookes

Contents of Volume 1 349

You might also like