Ernest R. Tello is an AI consultant, lecturer, and writer of publications in the field of artificial intelligence. His latest book is titled Mastering AI Tools And Techniques. He can be reached at 1518 W. Cliff Dr., Santa Cruz, CA 95060.
One good indication that object-oriented programming is here to stay would be the appearance of some serious delivery environments for applications developed with this programming paradigm. So far, Objective-C from Stepstone (formerly PPI) looks the most promising of the tools in this category. What I find interesting here is that this system was developed by C programmers who were looking primarily at the advantages object-oriented programming affords for handling conventional programming projects. In spite of this emphasis on the part of its developers, I believe that Objective-C may hold some promise as a delivery environment for AI applications as well.
The release of Objective-C that I used for my evaluation was the Version 3.31 implementation ported to the IBM PC AT. The language can also run on VAXes, Sun workstations, and the HP-9000 series. The AT version of Objective-C comes on one high-density disk and includes the compiler, libraries, and the main classes also in source form. At this point, the only C compiler on PCs supported is Version 4.0 of the Microsoft C compiler; support for Version 5.0 is planned.
Version 3.31 represents a relatively mature implementation of Objective-C that reflects a year or two's experience with problems that programmers have encountered. Because of difficulties that arose with some of the classes in the foundation library, although they have been included for the benefit of those who have already written code that uses them, the manual warns about the problems and suggests that they not be used. The classes in this category are the BytArray and Bag classes.
The Objective-C compiler consists of two executable files-- the driver program, objec.exe, and the actual Objective-C program itself, objc.exe. The driver program first calls the Microsoft cl.exe program to check syntax and then calls the Objective-C compiler. There is no need to specify libraries at link time, except if their paths cannot be found. The library references are embedded in the .obj files by Objective-C.
One of the things that puts Objective-C potentially in the category of a suitable delivery environment for Al applications is its feature of dynamic run-time binding for all objects. It is able to accomplish this even though it compiles its own code into C for subsequent compilation by a C compiler.
An important difference between Objective-C and other object-oriented systems such as Smalltalk is that it is really a hybrid language. So, just as with object-oriented Lisp systems, programmers always have the option of writing code in conventional C. Another important difference between Objective-C and Smalltalk is the difference in the size of the class libraries. Smalltalk-80 comes with a substantial amount of code available for reuse in source code form. Objective-C, although it offers considerably more in this department than does C ++, lags substantially behind Smalltalk, which is the senior member among object-oriented languages.
In certain respects, Objective-C is a rather conservative object-oriented language--that is, there are no substantial innovations in object-oriented concepts here that were not already in Smalltalk many years ago. On the other hand, not every powerful feature of Smalltalk is found in Objective-C either. If it can be considered as a hybrid of C and Smalltalk, then the more dominant parent by far is clearly C. Looking at it from the perspective of C, Objective-C has extended the C language by adding one new data type, Object, and one new operation, message expression.
The syntax of Objective-C is, for the most part, quite straightforward. To declare a new class, you use the equal sign (=), and you use the colon (:) to declare its superclass. Other items in the class definition are set off by parentheses, and all data declarations are set off in curly braces. So, for example, the following expression:
= Array:Object {short capacity;}declares Array as a subclass of Object with the instance variable capacity declared as a short integer.
There are two types of methods--class methods and instance methods--just as in Smalltalk, and they are defined using the plus (+) and minus (-) signs, respectively. Instances are usually created, as in Smalltalk, by sending the message new to the parent class.
Objective-C has two main types of message expressions--unary expressions and keyword expressions. There are no binary expressions like those used in Smalltalk. Hence, the expression:
id myarray = [ByteArray new:80];
creates a new instance of the class ByteArray sized at 80 units, and the definition of the method new for the Object class is just:
+ new {return (* alloc)(self, 0);}Here a built-in primitive is called on to do the job of allocating memory for an object. No further work is needed because the object is the simplest possible abstraction class.
On the other hand, the new: (pronounced "new colon") message for the Array class has this high-level Objective-C definition:
+ new:(int)nElements{
self = (* alloc)(self, nElements*[self ndxVarsize]);
capacity = nElements; return self;All messages in Objective-C are set between square brackets, so the expression [self ndxVarSize] is a message that the receiver object will be sent. The ndxVarSize message is an Array class method that is redefined:
+ (int)ndxVarSize {return (int) [self subclassResponsibility];}The subclassResponsibility method simply prints the message "Subclass should override this message" when called. The expression capacity = nElements simply sets the capacity of the array to whatever argument is supplied to new:.
Example 1, this page, shows the entire class library lists of the two versions of Objective-C as they might appear in a Smalltalk system browser, if such a thing existed in Objective-C.
Object Object
Array Array
ByteArray BytArray
IdArray IdArray
IntArray IntArray
Assoc (association) Assoc
Cltn (collection) Cltn
Ordcltn (ordered collection) Ordcltn
Set Set
Bag Bag
Dictionary Dictionary
Stack Stack
AVLDict (sorted dictionary) AsciiFiler
AVLTree BalNode
Paint Sortcltn
Rectangle Ipsequence
Sequence Sequence
String ObjGraph
Unknown Point
Rectangle
String
Unknown
In Objective-C classes are also referred to as factory objects to underscore that a class is an object in its own right whose main function is to serve as a template for the creation of instances and subclasses. As you can see, however, there are no Class and Metaclass classes present. Classes in Objective-C are not instances of a metaclass object and are not created by sending messages to a metaclass, as is true in Smalltalk, Xerox LOOPS, and many object-oriented Lisps. In itself, of course, this is not necessarily a bad thing and is simply another way of saying that Objective-C is a hybrid rather than a pure object-oriented language.
The library classes in Objective-C are arranged in roughly four broad categories: foundation classes; collection classes; other data-type classes such as String; and screen I/O classes. As you can see from the class hierarchy tables, in the new version of Objective-C, the AVL classes have been omitted but several others have been added. The BatNode abstract class and its subclass SortCltn are now used instead. BatNode is genenc code capable of supporting implementations of any binary tree. SortCltn, a class that handles sorted collections, is the replacement for AVLTree.
Another important change is that the Sequence class is now implemented as a subclass of IPSequence. The latter implements sequencing quickly through any kind of collection by running in place over its contents. In order to accommodate the technique used by the IPSequence class, the contents method was added to the collection classes. It returns the pointer to the instance of IdArray that the receiver is using to store the members of its collection.
The AsciiFiler class is a new class that gathers all the file operations and is able to support the transfer of source files between machines of different architectures on the same network.
Another interesting new class added is ObjGraph, which is used to create a graph of a class hierarchy, meaning that of all the classes from which it inherits. The manual provides an example of the use of ObjGraph by implementing a method called broadcast that takes a method name or selector as an argument and sends this method to all the objects that can be reached by the receiver. To accommodate this new way of creating graphs, the asGraph method of the Object class has been rewritten.
Earlier I demonstrated some rudimentary operations with the Array class. Arrays are implemented differently in Objective-C from the way they are in other object-oriented languages. In Smalltalk, the Array class is a descendant of the collection class, though not a direct descendant. In Objective-C,Array is a formal or abstract class that is the direct descendant of Object, the root class. This is obviously for efficiency purposes because C already has an implementation of arrays.
What is new with the Array class is the implementation of indexed instance variables instead of named instance variables. Arrays are fixed in size. Unlike the more sophisticated collection classes I will discuss later on, they cannot be increased in size when the number of elements reaches the maximum that was defined for a given array. The subclasses of Array handle arrays comprising the various C data types. As with most object-oriented languages, the Array class does not provide a facility for defining the dimension of arrays. To create multidimensional arrays, special subclasses of Array must be defined.
As you have seen, Array classes in Objective-C have a fixed capacity. Once an array instance of a certain capacity has been created, its size cannot be changed. This is not true, however, of collection classes, which are designed as "growable" classes that can later have more elements than specified by their initial capacity. The method that allows this in the Cltn class, expand, is written:
-expand {contents = [contents capacity: capacity + = capacity];
return self;
}This is a transparent, high-level, Objective-C method definition that resets the value of the contents variable and uses a simple increment operation to double its capacity.
Objects in Objective-C are designed to reside in a single address space and to be identified exclusively by this address in system memory. This means that systems cannot generally be built with Objective-C when objects need to reside on disk or at other locations on a network. All objects in Objective-C have to reside in the host computer's memory.
Because Objective-C is a hybrid language, it allows "cheating." This means that, unlike a pure object-oriented language such as Smalltalk, it allows access to the protected memory of objects with C code that can access that memory directly. Needless to say, this is a good way to get into some deep trouble, defeating the whole idea of the encapsulation that is one of the main points of an object-oriented system, unless a programmer understands the implications of such actions fully. But the main gambit of a system such as Objective-C is to take that risk for the sake of greater performance.
As with Smalltalk and most other object-oriented languages, the centerpiece of Objective-C for creating data structures is the group of collection classes. This part of the hierarchy has the following member classes:
Cltn
OrdCltn
Stack
Set
Dictionary
Bag
BalNode
SortCltnThe Cltn class is an abstract or formal class whose variables and methods are there to be inherited by its various descendants, which do all the actual work in programs. It is only the subclasses of Cltn that are meant actually to have instances.
The methods for collections are divided into about a dozen categories: instance creation, adding, removing, sequencing, elements perform, conversion, printing, freeing, copying, interrogation, comparison, and private methods. To understand them it is necessary to know a little about how collections work. The collection data structures are not used to store the elements themselves but pointers to the instances of the IdArray class that actually holds the members. The interrogation methods can be used in an application to query collections much as database queries, and searches are performed in conventional programming systems. So, for example, the find method searches for objects by name and returns them if they are present.
The elementsPerform methods are those that can map operations onto each element of a collection in turn. They do this by actually sending a message to each of the objects that are elements of the collection. One complexity is that different methods require different numbers of arguments, and Objective-C does not support functions with an optional number of arguments. The solution is that different elementsPerform methods must be implemented that accept different numbers of arguments. Versions are supplied that support up to three arguments; for more than this, it is no great problem to use these methods for implementing those that can accept more than three arguments.
As the name reveals, ordered collections are those whose elements are kept in order. Often more specialized subclasses of the OrdCltn class are used for handling queues and stacks. In ordered collections no nil entries are permitted, so whenever elements are removed from ordered collections, their contents are automatically compressed to take up the space created by the vacated element. Because of the nature of ordered collections, methods for adding elements to them specify a location at which to add them. These methods include addFirst, addlast, insert:before, and insert:after, which do the operations you would expect them to.
Stack implements collections that can keep entries in last-in, first-out order. In addition to the push: and pop: methods for accessing the contents of a stack, Objective-C stacks provide the at: and removeAt: methods for random access of stack elements. These random-access methods were new with Release 3.3. Stack manipulation methods include those that modify the order of stack elements, such as swap, as well as those such as topElement and lastElement, which provide information without making any modification to the stack.
Sets are collections that are only permitted to have one of each element. No duplicate elements are allowed. One application of sets that is particularly efficient is the creation of symbol tables. The Set class is implemented so that sets may contain any type of object. Several different types of object may even be collected in the same set. This means that, to add new methods to a set, an exhaustive search must be made of all existing elements in it.
The Set class in Objective-C supports a hashing facility so that, with the hash message, a set will place all objects it contains in a hash table for increased efficiency. An important limitation of sets as they are designed here, though, is that they are not designed to be changed dynamically. If the objects in an Objective-C set are modified, then the accessing facilities will no longer work correctly. This naturally limits their usefulness for AI applications.
The Dictionary class is a descendant of Set because dictionaries are implemented as a set of associations. In this case, you want to allow duplicate values, but each of the keys has to be unique. This is done by designing dictionaries to have a close relationship with the Assoc class. Associations store links between keys and values in such a way that these pairs can be stored in dictionaries and accessed by the key. Associations perform comparisons and equality testing by passing on messages to key objects. In addition to the methods inherited from Set and the other ancestor classes, Dictionary implements six new messages of its own: the class method with: for initializing new dictionaries and five methods for indexed acces--atKey:, atKey:put:,values, includesAssociation:, and includesKey:.
As you saw earlier, the SortCltn class replaces the earlier AVLTree class in Objective-C. A sorted collection is one whose member elements always remain sorted. When an object is added, it must be inserted in the appropriate place right away. How the elements are ordered depends on what has been chosen as the value of the cmpSel instance variable. The default is the compare selector. Other options for its value are invertCompare and dictCompare, which are the names of methods.
Compare and invertCompare are implemented in the Object or root class. The dictCompare method is implemented in the String class. Although SortCltn is not a subclass of Cltn, it acts as though it were. Its defined operations are "plug compatible" with it, according to the manual.
Another instance variable that alters the behavior of instances of the SortCltn class is the addDupAction variable. It can take on four different values:ADD, REJECT, MERGE, or REPLACE. These different options select different ways that duplicate elements are handled. If the ADD value is chosen, duplicates are permitted. In order to preserve the sorted ordering, any duplicate elements must always be the immediate successors of the elements they duplicate. With the REJECT option, duplicates are forbidden. As the name suggests, the MERGE option specifies that any duplicates will be merged using the merge method of the member's class.
Objective-C provides the two rudimentary "graphics" classes--Point and Rectangle. As such, this is a far cry from a full object-oriented graphics system, but the construction of these classes is still quite informative. There are two main instance variables for a Point--xLoc and yLoc. Instance methods include all those it inherits from the Object class as well as those for setting and accessing the values of the two coordinates, those for moving the coordinate, and even those for performing simple math operations on the coordinate values.
To make use of instances of the Point class for making actual drawings, object-oriented systems generally have something like the Pen class used in Smalltalk and Actor, which implements the basic turtle graphics functions. In these systems, Pen is a descendant of the BitBlt class, which implements bit-block transfers. These classes do not come with the Objective-C system, so to use the Point and Rectangle classes, the equivalents of BitBlt and Pen would have to be implemented.
Example 2, shows a short demo function in Objective-C, and Example 3, below, shows the actual listing in C generated by the compiler. The C output has been reformatted in a less compressed form for easier reading.
= DemoPoint : Object (Practice) (int xLoc,yloc;)
+ create (return [ [ super new ] initialize];)
- initialize (xLoc - 100; yLoc - 100; return self;)
- print (printf ("This point's coordinates are (%d@%d) \n", xLoc, yLoc;)
=;
#line 2 "demo.c
typedef struct _PRIVATE * id; id_msg(), _msgSuper();
#line 1 "demo.m"
#line 5 "demo.c"
struct _PRIVATE
{
struct _SHARED *isa;
int xLoc;int yLoc;
};
extern id DemoPoint, Object;
struct _SHARED
{
struct _SHARED *isa, *clsSuper;char *clsName;
char *clsTypes; short clsSizInstance;short clsSizDict;
struct _SLT *clsDispTable;
};
extern struct _SHARED _DemoPoint, _DemoPoint;
extern char *Practice[];
#line 1 "demo.m"
#line 3 "demo.m"
/* create-Practice[0] */
static id _1_DemoPoint (self_cmd)id self;char *_cmd;
{
return_msg(_msgSuper(_DemoPoint.clsSuper,Practice[1]
/*new*/,Practice[2] /*initialize*/);
}
#line 5 "demo.m"
/*initialize-Practice[2] */
static id _2_DemoPoint(self,_cmd)id self;char *_cmd;
{
self->xLoc - 100;self->yLoc - 100;return self;
}
#line 7 "demo.m"
/* print-Practice[3] */
static id _3_DemoPoint(self,_cmd)id self;
char *_cmd;
{
printf("This point's coordinates are (%d@%d) \n",
sel >xLoc self->yLoc);
}
#line 16 "demo.c"
extern struct _SHARED _Object, _Object;
struct _SLT
{
char **_cmd;id (*_imp);()
};
static struct_SLT _clsDispatchTbl[1]-
{
&Practice[0], (id (*) ())_1_DemoPoint, /* initialize */
};
static struct_SLT _ndtDispatchTbl[2]-
{
&Practice[2], (id (*) ())_2_DemoPoint, /* initialize */
&Practice[3], (id (*) ())_3_DemoPoint, /* print */
};
static char _bufClsName[]-"_DemoPoint";
struct _SHARED _DemoPoint-
{
&_Object,
&_Object,&_bufClsName[0], 0,sizeof(struct _SHARED), 1,
(struct _SLT *)_clsDispatchTbl
};
struct _SHARED _DemoPoint-
{
&_DemoPoint, &_Object, &_bufVlsName[1],
"#ii",sizeof(struct _PRIVATE), 2,
(struct _SLT*)_nstDispatchTbl
};
#line 8 "demo.m"
An important accompaniment to the Objective-C compiler running on larger machines such as the Sun and VAX is an interpreter that understands both C and Objective-C. As even a superficial acquaintance with Smalltalk demonstrates, a dynamically interpreted environment is an ideal accompaniment to an object-oriented system. The interpreter evaluates and executes both statements typed in interactively and those read in from disk files. Perhaps best of all, not only can Vici be used stand-alone for development but it can also be linked into applications and become an integral part of them.
As with most interpreters, Vici allows you to inspect the value of any variable by typing its name. A trace facility for both C and Objective-C is built-in that provides various trace options, including tracing execution of compiled messages, tracing interpreted messages, and tracing the allocation of space to new pbjects. Although Vici allows statements to be entered interactively, it is not set up with a full-fledged built-in editor for serious programming. Typically, programmers will use an outside editor of their own choice in conjunction with Vici.
One convenient feature of the PC AT version of Objective-C is that you can use the Microsoft CodeView debugger as a source-level debugger for the Objective-C syntax. To be able to work with Objective-C source in CodeView, it is necessary to use the -g option initially when compiling the application. Once done, you can bring up CodeView with Objective-C source displayed and can set breakpoints in the source for interrupting execution, enter expressions for evaluation, and so on.
You cannot inspect the values of objects directly, however. To inspect them, you must obtain access on the lower level by first using CodeView to find the addresses of objects whose values you seek. You then have to know the structure of the information stored there as represented in hex. Only by applying CodeView commands to 32-bit pointers in hex can you inspect the values. So, as you can see, there are substantial limitations to how much debugging can occur at the source level.
One question that inevitably comes up with a system such as Objective-C is just how well it stacks up against a more traditional object-oriented system such as Smalltalk. Certainly, if you are using it on a PC- or AT-type machine without the benefit of an interpreter such as Vici, there are some decided limitations to your ability to gain knowledge of the system through exploring it interactively.
In a Smalltalk system, besides the convenience of the browsers and other window-oriented facilities, you can use various built-in methods to help you explore the system and to provide you with information interactively that you need for writing your program. With Objective-C on a PC or AT, you have to rely on written documentation. Fortunately, this documentation is extremely well written, and the difference is essentially one of convenience. As with any programming language, it is possible to write various utilities, such as cross-reference programs and others, that can assist you.
Another issue is the size of the foundation class library that comes with the system. Here, Objective-C lies about midway between C + + and more fully developed environments such as Smalltalk and Actor. One thing that compensates somewhat for this is the ability to use existing C code and libraries to build up a custom class library relatively quickly. This possibility also exists m some cases for other languages too, however.
Keep in mind, too, that the runtime image of Objective-C may not always be compatible with all C programming systems and libraries available for Microsoft C. Unfortunately, this is the case with the Microsoft Windows programming environment. There is apparently a conflict between its run-time requirements and those of Objective-C that makes it impossible to use the two together.
A major difference between Objective-C and other object-oriented languages such as Smalltalk and Actor is the absence of the block construct. In these languages, a block of code is implemented as an unnamed object that allows evaluation to be deferred. Block objects are extremely powerful constructs that allow an additional degree of modularity in defining methods. Research is being conducted on adding the block construct to Objective-C, but it has not as yet been announced as a feature for a future release.
The field of object-oriented programming is besieged with metaphors, and these metaphors can be a mixed blessing. There is no doubt that they perform a useful function in introducing the basic notions of this programming paradigm. But they also can be the source of misconceptions. So, it is important at some point to give them a hard shake to see what fruit they really bear.
In what sense is it really appropriate to compare classes with factories? It is only the products of a class, its instances, that really do anything. Like a factory, the class is designed to produce a specific product. But does a factory ever resemble its products? Is a class designed purposely to produce massive numbers of instances efficiently? This tells us right away that a class is more like a mold or template used in a factory rather than the factory itself. However, they are most unique in that they are molds that can also be used to produce other molds as well. In this sense they are more flexible than either factories or molds.
What about the Software-IC metaphor? The idea here is that, just as functional hardware modules can be reused in many different boards, so can classes in a software design. But in just what sense are classes reusable? Aren't library functions also reused in many different applications that their original authors never foresaw? It is important to recognize that classes are reusable in ways that neither library functions or hardware lCs are. You cannot generally use off-the-shelf chips to construct new chips. But you can clearly build new library functions and classes out of existing ones. The real difference between classes and library functions is that classes are much larger and more complex units and are used in very different ways. A class usually has many library functions and data objects contained in it that will belong together in any application in which they are used. Library functions and hardware ICs both fall far short of this.
One of the best features of the Objective-C language is its syntax. It is far more readable than C ++ or C. Many people have criticized C for its poor readability. Here, I think, is one area in which Objective-C represents a clear-cut advance over its mother tongue.
Another is that you get many of the advantages of C but on a much higher level. With so many data structures and functions provided as standard in the language, Objective-C in principle allows some fairly large, high level applications to be written without the danger that much of them will depend on machine-specific library routines or custom code that becomes obscure with the passage of time.
Finally, Objective-C implements dynamic binding in a C compiler environment, which means that objects are created at run time rather than at compile time. This is clearly one of the features that all Al languages have in common. It is difficult to imagine writing programs that can learn or respond to new situations when everything has been defined statically at the time the program was compiled. This, aside from the more readable syntax, is what really differentiates Objective-C from ++.
DDS