Type Decl Inference Smalltalk
Type Decl Inference Smalltalk
for Smalltalk
Alan H. Borning
Computer Science Dept., University of Washington
Daniel H. H. Ingalls
Xerox Palo Alto Research Center
Abstract
An experimental system for declaring and inferring type in inappropriate class will only result in a run-time error of the form
Smalltalk is described. (In the current Smalltalk language, the “message not understood”, it is nevertheless advantageous for
programmer supplies no type declarations.) The system provides the programmer to be informed of such a problem when the code
the benefits of type declaration in regard to compile-time checking in question is being compiled, rather than later when it is being
and documentation, while still retaining Smalltalk’s flexibility. A used.
type hierarchy, which is integrated with the existing Smalltalk
class hierarchy, allows one type to inherit the traits of another In this paper we present an experimental system for declaring
type. A type may also have parameters, which are in turn other and inferring ripe in Smalltalk. In adding type declarations to the
types. language, we do not wish to give up its current flexibility. The
principal feature of our scheme that allows it to retain this
flexibility is the hierarchical description of types, and the
1. Introduction integration of this hierarchy with the existing Smalltalk class
Smalltalk is a powerful interactive language based on the idea hierarchy. The type of the actual contents of a variable or field
of objects that communicate by sending and receiving messages may be the same as or any subtype of its declared type. (In
[Ingalls 78, LRG 81, Goldberg 81]. In Smalltalk, the user supplies contrast, in e.g. Pascal the types would have to match exactly.)
no type declarations. This results in greater flexibility than in a For example, one might have a declaration for a message
typed language such as Pascal; however, in almost all cases, the ‘addWlndow:’ that specified that its argument was to be of type
programmer in fact knows what sorts of objects are expected as Window. When actually sending the message ‘addWindow:’, any
the arguments of a message, and what sort of object will be subtype of Window--a code editing window, a debugging window,
returned, Since at present one must glean this information from a painting window, or whatever-would be acceptable as the
comments, external documentation, or by inspecting the code, a actual argument. Correspondingly, in the code that implements
type declaration mechanism would provide a valuable form of the ‘addWindow:’ method, the programmer could only send to that
machine-checkable documentation. While Smalltalk is a “type- argument messages that all windows understand. Analogously,
safe” language in the sense that encountering an object of an one could declare that the argument of some other message was
of type Object. Since every type is a subtype of Object, the
argument could be any object--but in the code for that method, the
*--- ----- ---- .—-— —.. — --- programmer could send it only messages that all objects
This research was funded primarily by the Xerox Corporation, understand.
with additional funding from the National Science Foundation
under Grant No. MCS-80041 I I. In the sections that follow, we describe the notions of type,
subtype, and parameterized type in Smalltalk, along with
Permission to copy without fee all or part of this material is granted techniques for declaration, checking, and infe~ence of type. In
provided that the copies are not made or distributed for direct addition, we present a user interface that allows a programmer to
commercial advantage, the ACM copyright notice and the title of the view and edit type declarations conveniently. The type system is
publication and its date appear, and notice is given that copying is by
permission of the Association for Computing Machinery. To copy written in the Smalltalk-80 version of the language.
otherwise, or to republish, requires a fee and/or specific permission.
133
2. Definition of Type in Smalltaik The type of an argument maybe specified as the gfobal name of
A type specifies the messages that an object of that type
some type, such as Integer or Window, or by the name of a
understands. Thus, a type is abstract: it describes an object’s
parameter, e.g. as e/ementType for a method defined by
interface with the outside world, but not its internal representation.
Collection. In addition, an argument type specification may be
Each type specifies a set of message declarations. In turn, each
message declaration gives the name of the message, the formal
argument names, the types of the arguments, and the type of the
result. 1For emphseie, in this paper we italicize all expressions designating types.
However, when we simply name a class without using it in its role as a type, we
leavethe namein the normaltypeface.
Types come in hierarchies, so that one type maybe declared as
constructed by giving new parameters to one of the above
being a subtype of another, A subtype inherits the message
expressions, or by getting a parameter from one of them.
declarations of its supertype, and may add additional declarations
of its own. In addition, it may refine an inherited message
The “algebra of types” for specifying the type of a result is
declaration by indicating that the result is some subtype of what
somewhat richer. A result type specification may be any of the
was previously declared, At the top of the hierarchy is type
possibilities for an argument type. In addition, the result type may
Object, which declares messages such as ‘ =‘, ‘copy’, and
be specified as self, meaning the type of the object that is
‘printOn:’ that are understood by all objects.
executing the method, as being the same as the type of one of the
arguments to the method, or as being the nearest common
Types can have parameters. For example, rather than just
supertype of two other result type expressions. (The nearest
saying that something is of type Collection, it may be described as
common supertype of two types is that type which is a supertype
being a collection of some other type of object, e.g. as being of
of each and which is closest to them in the type hierarchy. The
type Co//cc/ion of: Window.
algorithm for finding nearest com’mon supertypes is given in
3. Implementation of Types Section 5).
We define a Smailtalk class Type. This class is abstract: it
declares no instance state, but only serves as a superclass for In addition to the declarations for messages, the programmer
other classes. One subclass of Type is class Class -- a class is a can declare the types of temporary, instance, class, and global
perfectly good specification of a type.l ParameterizedType is variables. The types of temporary variabfea can be inferred by the
another subclass of Type, which is used to hold copies of existing compileL for the others, declarations are required.
types with new parameters substituted for the original ones. For
example, the type Collection declares that it has a single We now present some examplea of type declarations. (These
parameter named e/ernen(Type, which is the type of the elements aren’t the complete type definitions for any of the types listed;
in the collection. A type such as Co//ecf(on of: Window is rather, we list only some representative message declarations.)
represented as an instance of ParameterizedType. (The type The type of each argument is declared by writing a type
Co//ection itself declares that its element type is Object.) expression in angle brackets after the argument name, while the
type of the result is decfared by an uparrow folfowed by a type
We leave open the possibility of additional subclasses of Type, expression in angle brackets. Comments are surrounded by
for example, a class Protocol that would describe a collection of double quotes.
related messages independently of any concrete implementations. Object
ssrpertype: none
“AS previously mentioned, Object is the
In the default, the type of an object will be the same as its class,
super type of all other types. ”
but this is not universally the case. For example, the Smalltalk ❑ x <Object> “am I equal to x?”
class Integer has three concrete subclasses: Small Integer, t<Bool can>
copy “return a copy of myself”
Larg@Negativelnteger, and LargePositiveInteger, but the type of
t<self>
instances of any of these is simply /nfeger. On the other hand, if c “Here, the type of the object
is a collection of windows, its type will be Co//ection of: Window, returned is the same as that of
the receiver. The specification
but its class will be Collection.
of the object that is returned is
evaluated in ‘type space’ rather
4. Making Declarations
than ordinary ‘object Spa Ce’ , so
As mentioned above, a message declaration gives the name of
that self here means the receiver’s
the message, the formal argument names, the types of the type. ”
arguments, and the type of the result Here, we will give an printOn: strm <Stream of: Character>
“This is the standard message
informal description of the ways in which the argument and result
asking an object to print itself
types can be specified, followed by some examples. A formal BNF on an output stream. Streams are
description is given in Appendix 1, parametrized by the type of
object in the stream. ”
~<self>
134
type “return my type” Boolean
t<Type> supertype:Object
class &b<Boolean>
“Every types is regarded as being tzBooleanz
parametrized by its class.” and:aBlock<Block to: Boolean>
tsself class> “This version of ‘and’ evaluates
Number its argument only if necessary,”
supertype:Object t<Boolean>
“A!urnber is a supertype for numbers of ifTrue: t <Block to: Object>
all sorts: integers, fractions, if False: f <Block to: Object>
floating point numbers, etc.” t<t resultType nearestCommonSupertype:
abs “return my absolute value” f resultType>
*<self> ifTrue: t <Block to: Object>
aslnteger “convert to an integer” ?<t, resultTypez
?<Integer> “If the receiver is ‘false’, then
asFloat “convert to a real number” the result is nil. (As described
t<Float.> in Section 5, nil is allowed as an
+ n<Number> instance of any type, and hence it
*<self nearestCommonSupertype: n> satisfies the declaration
“As far as the type system is tresu/tType.)”
concerned, 3+4 yields something of if False: f <Block to: Object>
type Integer, while 3.14+4 yields ?Zf resultType>
something of type Number. (In Type
fact, the result in the latter supertype:Object
case will be of class Float, but “A type is itself an object that can
the type system only knows, that it respond to various messages. ”
will be some subtype of Number.)” parameters
“Return a dictionary of my
Collection
parameters. ”
supertype:Object
r<Dictionary keys: Symbol values: Type>
parameters: ‘elementType’ ‘
supertype “answer with my supertype”
“e/ernentType is the type of the elements
tsType~
of the collection. The elements in a
given instance can be subtypes of Class
e/errrent7’ype; however, users of the supertype:Type
collection can only count on the new “create a new instance”
elements being of type e/ernerrtType. “The type Class is regarded as being
The class Collection responds to the parametrized by the type of
message ‘of:’ by returning a new instance that it creates.”
instance of ParameterizedTypa with the t<self instanceType>
parameter elementType set
appropriately. ”
add: x<elementType>
?<self> 5.TypeChecking andTypelnference
remove: x <elementType> This section describes the type checking and inference
tzselfz mechanism. When the user compiles a method, the system
addAll: c<Collection of:elementType>
tzsel f> checks at compile time that the messages that might be sent
do: b <Block from: elementType to: Object> during execution of the method body would be understood by
“This message takes a block as an their receivers, that the message arguments will be of the correct
argument, and evaluates the block types, that only objects of the correct type will be assigned to
for each element in the
collection. The results of the variables, and that an object of the correct type will be returned.
evaluation are discarded. (A Part ofthisprocess requires type inference aswellaschecking,
block is an object representing a
since the types of expressions must be inferred, and the system
piece of code to be evaluated at
an appropriate time; in this specifications allow the types of temporary variables to be inferred
example the block takes one if the user so desires.
argument. The type Block is
discussed further in Section
A fundamental operation in type checking ismatchinq, that is,
7.1.)”
t<sel f> checking that anactual type matches a declared type, An actual
collect: b< Block from: elementType to: Object> typef matches adeclared typed if t isthesame type asor isa
“This nfessage takes a block as an
subtype of d. Ifdhasparameters, then for each parameter of d,
argument, evaluates the block for
each element in the collection, thecorresponding parameter oftmust match it. Finally, the type
and returns a new collection Ur?defirredObject matches any type. (UndefinedObject istheclass
consisting of tha Pesult$ 6f of nil, to which all variables are initialized in Smalltalk. )
evaluating the block. Lisp fans:
this is similar to ‘mapcar’, ”
?Zself of: b resultType>
135
Another operation required in type checking is finding the For example, the programmer might declare that a temporary
nearest common supertype of two types t7 and f2. To find this variable t is of type Number, but the type inference mechanism
nearest common supertype, t7 searches upitschain of supertypes could infer that in fact t must be an integer. In such a case, the
until it finds the first supertype s that is also a supertype of t2. type system does not refine the declaration; thus, sending a
(Such a supertype always exists, since all types are subtypes of message to t that is understood by integers, but not numbers in
Object.) Ifshasparameters, thevalues of each parameter of the general, would yield an error during type-checking. If the
nearest common supertype is found by computing the nearest programmer wants to send such messages to t, he or she should
common supertype of the corresponding parameters from f7 and declare t to be of type Integer, or else let the system infer its type.2
?2
136
and ‘whileFalse:’ only to a block that takes zero arguments and
Blockl
that also evaluates to a Boolean; to represent this, a subtype supertype: Block
/300/earrB/ock of the type of blocks taking zero arguments is parameters: ’arglType resultType’
“This type of block takes one
defined.
argument. ”
Block value: arg<arglType>
supertype: Object t<resultType>
parameters: ‘resultType’
“The type of any actual block will be Block2
supertype: Block
some subtype of this type. ”
parameters: ’arglType arg2Type resultType’
new Process
“This type of block that take two
“Create a new process. All blocks
arguments. ”
can do this, so this message is
value: argl <arglType>value: arg2<arg2Type>
declared in B/ock.”
tzresultType>
t<Process>
Biock3
supertype: Block
parameters: ’arglType arg2Typearg3Type
3 resuitType’
It would of course be easy todefinefurrher measages for blocks taking four
arguments, and so forth. “This type of block that take three
arguments. ”
BlockO
value: argl <arglType>value: arg2<arg2Type>
supertype: Block
value: arg3<arg3Type>
parameters: ‘resultType’
*<resultType>
“This type of blocks takes no
arguments.”
value In writing type declarations, the programmer makes use of a set
r<resultType>
of messages understood by the class Block.
BooleanBlock
supertype:BlockO to: rType returns a BlockO parameterized by the
parameters: ’resultType’ resultType rType. As a special case, if rrype
“This is a subtype of BlockO with matchesBoo/ear?, this returns a BooleanBlock
‘resultType’ set to Boolean, (B/ockO instead.
just declares that it produces
something of type Object.)”
from: al Type to: rType
whileTrue: anotherBlock <Blockto: Object>
?<self>
returns a Blockl parametrized by the
whileFalse: anotfserBlock <Block to: Object>
rfself> argument type a7Type and the resultType
r Type
137
Object
from: al Type and: a2Type to: rType supertype:none
returns a Block2 parameterized by the be:aType<Type>
argument types alType and a2Type, and the tzaType>
resultType rType
The corresponding method simply checks if the receiver’s type
from: al Type and: a2Type and: a3Type to: rType matches ‘aType’. If it does, it returns the receiver; otherwise, it
returns a Block3 parameterized by the
notifies the user that atype error has occurred (and then returns
argument types al Type, a2Type, and a3Type,
nil if the user chooses to proceed).
and the resultType rType
138
at a time, and need only examine the declarations for the
may aid in the production of faster, more compact code by
messages that it invokes, but not their code. This simplifies our
allowing better factoring of information. Also, the type system may
program considerably, and makes type checking in the presence help in tracing control flow, thus making it possible to produce
of edits to a running system much easier,
A less important
application modules containing just the code needed to run a
difference is that Suzuki’s system uses unions of types, whereas
particular application,
we use a single type, taking the nearest common supertype as
necessary. For example, Suzuki’s system would infer that the type
Acknowledgements
of the expression
x = y ifTrue: [3] ifFalse: [3.1416] We would like to thank all the members of the Learning
was {/rNeger, F/oaf}, whereas ours would infer it to be Number, the
Research Group at Xerox PARC for help and suggestions, in
nearest common supertype of /rrteger and F/oat. particular Peter Deutsch and Phil Klein.
139
1. A BNF Description of Type Declarations
In this appendix we present a formal definition of type declarations in our system, using the “human
engineered variant” of BNF described in [Ledgard 81]. Terminals in the grammar are underlined,
optional items are surrounded by square brackets, and repetition is denoted by ‘.,.’.
argument-type ..=
. . simple-type
get-parameter ..=
. . simple-type parameter-name
The category names urtary-se/ecfor, binary -selector, and keyword are as in Smalltalk-80; definitions
maybefoundin[LRG 81].
140
[Liskov 77] Liskov, B., Snyder, A,, Atkinson, R, and Shaffert,
References
c.
Abstraction Mechanisms in CLd.
Communications of the ACM 20(8):564-576,
August, 1977.
141