Here’s another interesting thing I’ve learned about Clascal and Object Pascal: It went through exactly the same evolution from combining object allocation & initialization to separating them that Objective-C did a decade later!
In early 1983 Clascal, classes were expected to implement a New
method as a function returning that type, taking zero or more parameters, and returning an instance of that type by assigning to SELF
—sound familiar? This was always implemented as a “standard” method (one without dynamic dispatch) so you couldn’t call the wrong one. A cited advantage of this is that it would prevent use of the standard Pascal built-in New()
within methods—which I suspect turned out not to be what people wanted, since it would prevent interoperability.
A class could also choose to implement an OVERRIDE
of the also-included Free
method to release any resources it had acquired, like file handles or other object instances. And each overridden Free
method had to include SUPERSELF.Free;
after it did so in order to ensure that its superclass would also release any resources it had acquired.
INTERFACE TYPE Object = SUBCLASS OF NIL FUNCTION New: Object; STANDARD; PROCEDURE Free; DEFAULT; END; Person = SUBCLASS OF Object name: String; FUNCTION New(name: String): Person; STANDARD; PROCEDURE Free; OVERRIDE; END; VAR gHeap: Heap; IMPLEMENTATION METHODS OF Object; FUNCTION New{: Object} BEGIN SELF := Object(HAllocate(gHeap, Size(THISCLASS))); END; PROCEDURE Free BEGIN HFree(Handle(SELF)); END; END; METHODS OF Person; FUNCTION New{(theName: String): Person;} BEGIN SELF := Person(HAllocate(gHeap, Size(THISCLASS))); IF SELF <> NIL THEN name := theName.Clone; END; PROCEDURE Free BEGIN name.Free; SUPERSELF.Free; END; END;
By mid-1984, Clascal changed this to the CREATE
method, which was declared as ABSTRACT
in the base class. Note that it still doesn’t use the standard Pascal built-in New()
to create object instances. However, it takes a potentially-already-initialized object so that it’s easier for a subclass to call through to its superclass for initialization, since CREATE
is still not a dynamically-dispatched method. Also, instead of referencing a global variable for a heap zone in which to perform allocation, it takes the heap zone, providing some amount of locality-of-reference that may be helpful to the VM system.
There was also a change in style to prefix class names with T
.
INTERFACE TYPE TObject = SUBCLASS OF NIL FUNCTION CREATE(object: TObject; heap: THeap): TObject; ABSTRACT; PROCEDURE Free; DEFAULT; END; TPerson = SUBCLASS OF TObject name: TString; FUNCTION CREATE(theName: TString; object: TObject; heap: THeap): TPerson; STANDARD; PROCEDURE Free; OVERRIDE; END; IMPLEMENTATION METHODS OF TObject; PROCEDURE Free BEGIN FreeObject(SELF); END; END; METHODS OF TPerson; FUNCTION CREATE{(theName: TString; object: TObject; heap: THeap): TPerson;} BEGIN IF object = NIL object := NewObject(heap, THISCLASS); SELF := TPerson(object); WITH SELF DO name := theName.Clone(heap); END; PROCEDURE Free BEGIN name.Free; SUPERSELF.Free; END; END;
This is starting to look even more familiar to Objective-C developers, isn’t it?
The final form of the language, Object Pascal, actually backed off on the Smalltalk terminology a little bit and renamed “classes” to “objects” and went so far as to introduce an OBJECT
keyword used for defining a class. It also changed SUPERSELF.
to INHERITED
—yes, with whitespace instead of a dot!—as, again, developers new to OOP found “superclass” confusing.
Object Pascal also, at long last, adopted the standard Pascal built-in New()
to perform object allocation (along with its counterpart Free()
for deallocation) directly instead of introducing a separate function for it, since the intent can be inferred by the compiler from the type system. It also removed the need to use the METHODS OF
construct to add methods, instead just prefixing the method with the class name and a period.
The final major change from Clascal to Object Pascal is that, with New()
used for object allocation, the CREATE
methods were changed into initialization methods instead since they just initialize the object after its allocation. They were also made procedures rather than functions returning values, and since the standard Pascal built-in New()
is being used they no longer take a potentially-already-allocated object nor do they take a heap zone in which to perform the allocation. The convention is that for a class TFoo
the initialization method has the form IFoo
.
There was also another stylistic change, prepending field names with f
to make them easy to distinguish from zero-argument function methods at a glance.
There was also a switch from not including the parameter list in the IMPLEMENTATION
section to including it directly instead of in a comment.
Here’s what that looks like:
INTERFACE TYPE TObject = OBJECT PROCEDURE IObject; ABSTRACT; PROCEDURE Free; DEFAULT; END; TPerson = OBJECT(TObject) fName: TString; PROCEDURE IPerson(theName: TString); STANDARD; PROCEDURE Free; OVERRIDE; END; IMPLEMENTATION PROCEDURE TObject.Free BEGIN Free(SELF); END; PROCEDURE TPerson.IPerson(theName: TString) BEGIN fName := theName.Clone(); END; PROCEDURE TPerson.Free BEGIN fName.Free; INHERITED Free; END;
Based on the documentation I’ve read, it wouldn’t surprise me if the only reason initialization methods aren’t consistently named Initialize
is that the language design didn’t support an OVERRIDE
of a method using a different parameter list.