* This material is based upon work supported by the National Science Foundation under Grant MCS75-06678 A01.

Abstract: The notion of a class, introduced in SIMULA, has become a key concept in data structuring. This note describes a simple interpretation of classes as syntactic sugar in SCHEME, and extended lambda calculus due to Steele and Sussman. Key Words and Phrases: Classes, clusters, data abstractions, lambda-calculus, LISP, SCHEME, SIMULA CR Categories: 4.20, 4.22, 4.12, 5.20

This material is based upon work supported by the National Science Foundation under Grant MCS75-06678 A01.

Author's address: Computer Science Department, Indiana University, Bloomington, IN 47401

Introduction

The notion of a class , introduced in SIMULA 67 [1], has become a key concept in data structuring [2, 5, 11]. Interpretation of classes in the lambda calculus have been given by Sandewall [8], Steele and Sussman [10, inter alia ], and Reynolds [7]. In this note we will give a particularly simple interpretation of classes† as syntactic sugar in SCHEME, an extended lambda calculus due to Steele and Sussman [9].

SCHEME is a LISP-like language with static scoping and full FUNARGS. The following samples give a flavor of the syntax:

(LET ((v 1 e 1 )…(v n e n )) exp)

is equivalent to

((LAMBDA (v 1 …v n ) exp) (e 1 …e n ))

and

(LABELS ((v 1 e 1 )…(v n e n )) exp)

is equivalent to ISWIM's [4]

letrec v 1 =e 1 ,…, v n =e n in exp

where the e i are lambda expressions. Here, as below, key words appear in upper case and metavariablies appear in italics. Conditionals may be written as (IF pred then-part else-part) . An association-list, as in LISP [6] may be searched by RASSOC , defined as follows:

(DEFINE RASSOC (LAMBDA (KEY LIST) (IF (NULL LIST) NIL (IF (EQ KEY (CAAR LIST)) (CAR LIST) (RASSOC KEY (CDR LIST)) ) ) ))

The semantics of the language allow the recursive call to be implemented iteratively.

Code and Commentary

A possible concrete syntax for classes is as follows:

(CLASS ((loc 1 val 1 )…(loc n val n )) ((procname 1 λ-exp 1 ) ⋮ (procname m λ-exp m )))

An instance of the class defined in this manner should have n local variables, named loc 1 ,…, loc n and initialized to val 1 ,…, val n respectively. Associated with the class should be m class procedures, named procname 1 ,…, procname m , with procedure bodies λ 1 ,…, λ m . These procedures may refer to the lcoal and to each other (possibly recursively), but, in keeping with current thinking, the locals should be accessible to the user of a class instance only through the class procedures. To achieve this, the definition is expanded as follows:

(LET ((loc 1 val 1 )…(loc n val n )) (LABELS ((procname 1 λ-exp 1 )…(procname m λ-exp m )) (LET ((D (LIST (CONS (QUOTE procname 1 ) procname 1 ) ⋮ (CONS (QUOTE procname m ) procname m )))) (LAMBDA (Z) (RASSOC Z D)) )))

If X is an instance of a class with a procedure named P , a call written conventionally as X.P(t 1 ,…,t n ) is expanded as

((X (QUOTE P)) t 1 …t n )

Execution of the code for (CLASS locals procs) proceeds as follows:

an environment is created in which the locals are bound tot he appropriate values. an environment is craeted in which the procedure names are recursively bound to the bodies, referencing the variables of (i) as non-locals. an explicit association list (called D ) is created in which each procedure name is associated with its corresponding closure (created in (ii)). a function is created which takes as its argument a procedure name and returns the corresponding procedure.

This function is returned and is the class instance. Thus the procedure call above retrieves procedure P of class instance X , and invokes that procedure with arguments t 1 ,…,t n . Since fresh closures are created for each class instance, this procedure work's on X 's local variables.

This code is similar in its effect to the implementation of classes in SMALLTALK [3] and in its use of closures to Sandewall's code [8]. Our code extends Sandewall's by allowing multiple class procedures; this is the primary source of the complexity in the code. We differ from SIMULA and most implementation fo classes by making a class definition an expression, which can appear anywhere in a program. For example:

(DEFINE CELL (LAMBDA (X) (CLASS (( CONTENTS X )) (( CONT (LAMBDA () CONTENTS )) (UPD (LAMBDA (VAL) (ASETQ CONTENTS VAL )))))))

A cell may then be created as:

(LET ((Z (CELL 3))) (BLOCK (PRINT ((Z (QUOTE CONT)))) ((Z (QUOTE UPD)) 4) (PRINT ((Z (QUOTE CONT)))) ))

which creates a cell initialized to 3 , calls it Z , and changes its contents to 4 , printing out 3 and 4 .

Note that class parameters are introduced by creating a function

(LAMBDA (class-parameters) (CLASS locals procs))

which may be bound to a variable name of any lexical scope, and that procedure names (since they are quoted) need not be declared at all. Instances of the class, however, may be passed outside this scope; this makes the code more general than any special naming scheme. Steele and Sussman's transcription [10] requires the procedure names to be declared wherever a class instance is used; this prevents classes from sharing procedure names, a necessity for concatenated classes [1]. Unlike Reynolds [7], we also avoid major transformations in the program structure; imperative features are entirely optional.

At the expense of additional syntax, a variety of additional features could be added to the framework. Some functions could be hidden by suitable editing of the association list D . Direct access to some of the locals could be added similarly. Concatenated classes could be done by specifying one of the locals as an instance of the base class and changing the function returned so that if the desired procedure is not found locally, it is passed along to the base instance, i.e.,

(LET ((the-bases (base-class base-class-params)) …) (LABELS (…) (LET ((D…)) (LAMBDA (Z) (IF (IS-PRESENT Z D) (RASSOC Z D) (the-basis Z))))))

Since the locals are initialized, no class body is usually needed; one could easily be added if desired.

Conclusion

Operationally, classes are just syntactic sugar—their operational semantics requires no new concepts. We believe the significance of the notion of classes is as a syntactic structuring device. Structuring devices such as strong typing or good loop structures play an important role in ease of program writing and debugging, as shown by PASCAL, by turning run-time errors into compile-time errors. The significance of classes, we believe, is as a structuring device which allows better checking at compile time and verification time.

† except for resume and detach , which have not been adopted in the literature on data types. ↪