Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Artificial Intelligence


JAN88: ARTIFICIAL INTELLIGENCE

Actor Does More Than Windows

This month I'll be looking at the latest version of The Whitewater Group's object-oriented language Actor. The Whitewater Group has been promoting this language simply as an effective tool for rapid development under Microsoft Windows. lt is certainly that. But Actor is also the first object-oriented programming tool specifically designed for writing applications that run under Windows. That fact and The Whitewater Group's intention to port Actor for other windowing systems make it an interesting tool for developing precisely the AI applications that the object-oriented approach is good at while easing the burden of developing for different machines. ln this column I'll describe how the latest version (1.1) of Actor implements some of the Microsoft Windows constructs and will examine it from that perspective.

There are so many similarities between Smalltalk and Actor that you may well ask why anyone would develop a new language when implementations of Smalltalk already exist. There are two reasons. First, Actor was designed from the start to be compatible with commercial personal computer environments. Hence its built-in compatibility with Microsoft Windows today and The Whitewater Group's plans to allow window environment programming with Actor across windowing environments tomorrow. Second, the syntax of Actor has been designed to be familiar to C and Pascal programmers. The Whitewater Group's intent was to incorporate all that was good about Smalltalk in a package tailored to the needs of present-day personal computer programmers.

To give you a feeling for programming in Actor, I have sprinkled some sample programs through this column as Examples 1 through 4 , pages 116, 118, and 125. Three of these are familiar benchmark programs for generating prime and Fibonacci numbers and the Tower of Hanoi and the fourth contains Actor author Chuck Duff's extensions for list processing.

The Actor Desktop

Basically, Actor runs on top of MS-Windows and uses the Windows facilities to implement objects that allow an interactive, windowing environment for development. As with MS-Windows, you can theoretically get along without a mouse, but you wouldn't want to.

The main items used on the Actor desktop are the workspace windows, browsers, and inspectors, which are all modeled on the Smalltalk originals.

When the system first comes up, it shows a workspace window with two rows of command options along the top bar of the window. The commands include File, Edit, Doit!, Browse!, Inspect!, Show Room!, and Templates.

The editing area of a workspace window behaves just like an interpreter does. If you type in an expression and then a carriage return, Actor will attempt to compile and execute it. If there is a body of text already in an editing window, then highlighting a portion of that code and clicking on Do it! will result in that portion of code being compiled and executed. The Browse! and Inspect! options result in a new one of these tools opening like a pop-up window on the Actor desktop.

Inspectors are used for focusing on a particular object. You use them to examine the contents of objects in detail as well as to make modifications to them. The upper-left pane of an inspector window contains a list box that displays all the instance variables of an object. Clicking the mouse on any of the items in the scrollable list of variables results in its value being displayed in the bottom pane of the inspector--its edit window. Both class objects and their instances can be accessed using inspectors.

A browser provides a similar function to that of an inspector, but instead of providing an interactive window on a single object, it does this for the entire system of classes. Its scrollable list box contains a list of all the classes currently in the Actor class hierarchy. The righthand list box contains the methods for the current class. By first selecting a class and then a method in that class, you may access the code in the bottom window and edit it. An Options selection on the browser menu bar allows you to choose whether the classes are listed in hierarchical or alphabetical order.

A Class Act

Actor comes with a large class library of ready-made code that can be used for building applications quickly, once you bridge the learning curve of using the system and knowing what's there. Here is a partial list of the classes used for data structures and the graphics facilities subsumed under them:

Object
       Collection
             Indexed Collection
                  Array
                        Function
                        OrderedCollection
                             SortedCollection
                             TextCollection
             ByteCollection
                  String
                        Symbol
                  Struct
                        DosStruct
                        GraphicsObject
                             Polygon
                             Rect
                                  Ellipse
                                  RndRect
                         Proc

Example 1: Eratosthenes' Sieve benchmark

inherit (Object, #Sieve, nil, nil,nil);!!

now (Sieve);!!

/* Returns the number of prime numbers between 0 and cnt,
inclusive. */
Def sieve (self, cnt | flags, count, c)
(              c := cnt + 1;
               flags := new (Array, c);
               fill (flags, true);
               count :=1;
               do ( over (2, c),
                         {using (i | triple)
                              if flags[i]
                              then triple := i*3;
                                   if triple < cnt
                                   then do ( overBy (triple-1, c,
i+i-1)
                                        { using (j) flags[j] := nil ));
                                   endif;
                                   count := count + 1;
                              endif;
                         }):
               ^count;
}!!

Actor[#Sam] := new (Sieve)!!
/* To run type: sieve (Sam, 100) */!!

Example 2: Fibonacci program

now (Int)!!

/* Recursive way of finding the nth Fibonacci term. Note that
this way of finding the Fibonacci terms is very inefficient
because each message "spawns" two recursive messages. */

Def fib (self)
{ if self < 3
     then ^1
     endif; ^fib (self - 1) + fib (self - 2);
}!!

/* Iterative way of finding the nth Fibonacci term. */

Def fib2 (self | term, term1Before, term2Before)
{if self < 3
     then ^1
     else term := 2; term1Before := 1; term2Before := 1;
          do (new (Interval, 3, self + 1, 1),
               {using (i) term := term1Before + term2Before;
                    term2Before := term1Before;
                    term1Before := term;
               });
          ^term
     endif;
}!!


Actor Syntax

As with languages such as C and Pascal, Actor encloses arguments to a method in parentheses immediately following the method's name or selector. And like Pascal and Smalltalk, variable assignments are made using the colon-equal (:=) symbol. Also as in Smalltalk, the way you create new instances is by sending the new message to a class and assigning this new instance a name. So, for example, you could create an instance of the Turtle class by saying:

Barney := new (Turtle);

In general, sending messages in Actor is like passing arguments but in reverse. The object to which the message is sent is treated as an argument. Once you have created the turtle Barney, you can get him to do your bidding by sending various messages that he can recognize. Like any authorized turtle, Barney knows that the message r means turn right, l turn left, f move forward, and b move backward. He also knows that down means to put his tail to the ground for drawing purposes and up means to pick it up again.

So, if you wanted Barney to perform a turtle walk in the shape of a square, the messages you would send him would be:

down (Barney);
f (Barney, 10);
r (Barney, 90);
f (Barney, 10);
r (Barney, 90);
f (Barney, 10);
r (Barney, 90);
f (Barney, 10);
up (Barney);

In Actor, the keyword Def is used to define methods. So, if you wanted to teach not only Barney but also all authorized turtles the new message walkSquare, you would define the method:

Def walkSquare (self, size)
{down (self);
    f (self,size);
    r (self, 90);
    f (self, size);
    r (self, 90);
    f (self,' size);
    r (self, 90);
    f (self, size);
    up (self);
};

Henceforth, to get Barney or any of his relatives to perform this maneuver, all you would have to do is to say:

walkSquare (Barney, 10);

The more astute turtle watchers have probably noticed that this turtle walk is only one orientation for this type of maneuver. There is a species of turtle, admittedly rare, that instinctively will do its squarewalking counterclockwise. Fortunately, object-oriented systems such as Actor provide a way of mirroring this little sidelight of natural history. What you can do to cover this complexity is to define a new class of turtle called CounterTurtle and provide a walkSquare method for turtles of this species that does the counterclockwise variant of the standard turtle square walk. Coincidentally, this also provides me with the opportunity of illustrating how new classes are defined in Actor.

The way you usually create new classes in Actor is from within a browser, so I'll do it that way first. Very simply, you first select the class Turtle from the class list. Then you go to the Options pull-down menu and select Make Descendant. A popup window then opens that serves as a template for creating the new class. In this case, let's enter CounterTurtle as the name of the class. Now you just click on the Accept button, and the system will create this new class and its name will be added to the class list and become part of the Actor class hierarchy.

The other way to create new classes, which is what the browser is actually doing, is to write the code for it directly. The inherit statement is used for this. So, you could write:

inherit (Turtle, #CounterTurtle)!!

One of the most attractive things about the Actor system is that it provides built-in classes for making the use of the Microsoft Windows user interface easier. Three main classes are concerned with this: Window, Control, and ModalDialog. First let's look at the Window class and its descendants. Here is an outline of this branch of the class hierarchy:

Object
    Window
        PopupWindow
            ToolWindow
                Browser
                Inspector
        TextWindow
            EditWindow
                WorkEdit
                    BrowEdit
                    FileWindow
                    Workspace
            ScanWindow
            WorkWindow

Though relatively large, Window is just a formal or abstract class. This means that it implements the methods that will be used by the subclasses that implement the specialized windows that actually get instantiated and used. In particular, Window implements the routines that communicate with MS-Windows.

The TextWindow class is one of the simplest descendants of Window that you can get to actually do real things. This class allows you to create tiled windows that can print text. It does this with the printString and printChar methods, which call the Textout GDI (Graphics Display interface) function in MS-Windows.

Often you will want text windows that can do more that just show text--that can allow you to go in and edit that text. The subclass of TextWindow called EditWindow provides the code that supports this editing ability. The WorkEdit class takes this one step further by allowing you to create windows that can not only edit but can also enter Actor language statements to be evaluated. The three subclasses of WorkEdit provide the types of windows that are like those most often used-browsers, file browsers, and general-purpose workspace windows. The main difference, though, is that, like their ancestor TextWindow, these are tiled windows.

The windows most often used in Actor come from another branch of the tree that is implemented with the class PopupWindow. The windows you get by instantiating PopupWindow are the familiar layered windows that stack up on top of one another. Unlike the tiled windows, however, they do not permit you to zoom or contract them down to an icon. As is dictated by MS-Windows, pop-up windows have to have a "parent" text window. When this parent window is contracted into an icon, then the pop-up windows associated with it temporarily become invisible.

Controls and Dialog Boxes

As in MS-Windows, in Actor a control is a special type of window that is used for routine input and output in a user interface. Examples of controls include things like buttons, list boxes, and scroll bars. The branch of the class tree concerned with controls looks like this:

Object
    Control
        Button
        ListBox
            ClassList
            Scrollbar

Like Window, the Control class in Actor is just a formal one, and its subclasses are the ones that are actually instantiated in applications. Controls are handled in Actor in an almost identical way to windows. New instances are created by sending the new message, and they are displayed by sending the show message.

I'll describe one other class-the ModalDialog class and its subclasses.

Object
    ModalDialog
        ClassDialog
        DebugDialog
        DirtyCLD
        FileDialog
        InputDialog

Modal dialog boxes resemble popup windows in that they stack on top of other windows and they need a parent window with which they are associated. Like Window and Control, ModalDialog is basically an abstract class that implements code intended for use by its descendants. The FileDialog class in Actor is used to create the dialog boxes that routinely appear in MS-Windows when you load a file by using a pull-down menu. The ClassDialog class is for dialog objects used when a class is being edited or created with a browser.

Using Actor for AI

For AI applications, as well as many other types of application, processing linked lists is essential. How do you go about doing that in Actor? One way might be to work with the OrderedCollection class and add subclasses to it with the necessary methods defined for list processing.

Example 4, a demo provided with the current release of Actor, offers another approach, however. The new class ListNode is defined as a subclass of the Collection class. The methods append, do, isAtom, printon, and rPrinton are defined for this new class. New methods, including isAtom, rPrinton, and cons, are also defined for the root class, Object. If you inspect the code for cons, you will see that there is a routine for sending the message new to the ListNode class and creating a new instance of it. Obviously, this is only the rudimentary beginning of what a functional list-processing class would encompass.

As far as the suitability of Actor for AI applications is concerned, the same limitations that apply to Smalltalk apply here. As compared with LISP and PROLOG, Smalltalk and Actor are relatively low-level languages. They are suitable for developing M applications, but many additional high-level methods and classes have to be written from scratch just to get started.

The structures used by the OrderedCollection class differ significantly from dynamically modifiable linked lists. In the terminology of object-oriented programming, ordered collections are fixed collections that are nevertheless "growable." This means that when you create an OrderedCollection, you must create one with a maximum number of elements. If the elements already stored in the collection have not yet reached the maximum, it is easy to add new elements to the beginning or end of the list. When the maximum is reached, and you need more, you must send the grow message to the collection. What really happens when you do this is that a new array of the needed size is created and the elements of the old array are copied into it.

Debugging

With Version 1.1 a new debugger has been added to Actor. Currently, both a low-level and high-level debugger are provided, but the low-level debugger is not formally supported and may disappear in later releases of the Actor system.

Routine errors in code evaluated by Actor result in a dialog box that contains a stack history up to the point of the error. The dialog box usually also contains a message that diagnoses the type of error. If you wish, when a dialog box is open because of an error, you can click on the Debug button and cause a Debug window to open.

This is a versatile debugging tool that combines some of the features of a browser and some of those of an inspector as well as the ability to change any of the values associated with a method. With it you can also resume processing on the fly immediately after an error has been fixed.

Conclusions

On the whole, I find Actor to be a thorough implementation of a full programming system with an excellent set of demo programs and helpfully written documentation and tutorials. The ideal users of Actor, as I see it, would be programmers who have already had some exposure to Smalltalk and need to prototype something quickly to run in the MS-Windows environment. For purposes such as these, it is hard to beat.

One thing about the implementation of Actor I dislike, though, is the absence of a facility for multiple inheritance. With systems intended for real-world applications, multiple inheritance should be a standard feature because in the real world many things fulfill multiple roles and multiple functions. Multiple inheritance provides a ready way of handling this in an explicit way.

Vendors of object-oriented tools who fail to include multiple inheritance typically say that they do not want to make the system too complex for users or that none of their customers have requested it. I have not found either of these explanations at all convincing. I have had no difficulty in using multiple inheritance in systems that have it and can't imagine trying to build a serious object-oriented application without it.

In response to this, it might be said that you can still create classes of the same definition in a system without multiple inheritance the hard way simply by defining them to be exactly what you want. My feeling about this is that it may be true in theory but it tends to be something that is never done in practice. In the two years or so that I have worked with object-oriented programming systems, I have never done this unless the system provided for multiple inheritance, in which case it becomes a routine practice.

The reason is the same one that makes interactive systems such as Actor significant and not just a mere convenience. The more you make basic things easy to do, the more you tend to launch out into the more difficult areas creatively, trying things that otherwise you might not have tried. If you are a user of an object-oriented programming tool that lacks this feature, I strongly recommend that you pester the vendor to put it at the top of the list for new features. I feel almost certain that you will not regret the results of having exercised your prerogative as a customer.

Version 1.1 of Actor differs from 1.0 in two main ways. First of all, there is more space--an additional 70K--for compiling applications. Second, it is fully compatible with Windows II, though it does not fully support all of Windows II's facilities. A future release of Actor, though, will actually support programming with the new features of Windows II. Needless to say, the implementations of Actor that will use the full features of Windows II and the OS/2 Presentation Manager will ultimately determine the fate of this product. But if the current implementation is any indication, the future versions should be of very high quality indeed.

Vendor

ACTOR The Whitewater Group Technology Innovation Center 906 University Place Evanston, Il. 60201 (312) 491-2370 Reader service No. 28

Example 3: The Tower of Hanoi

/*  ref.  Byte August, 86  p. 146   cbd  8.13.86 */!!

inherit(Object, #TowerOfHanoi, nil, nil, nil);!!

now(TowerOfHanoi);!!

Def moveTower(self, height, from, to, use)
{  if height > 0
   then
     moveTower(self, height - 1, from, use, to);
     moveTower(self, height - 1, use, to, from);
   endif;
}!!

Def moveTower2(self, height, from, to, use)
{  if height > 0
   then
     moveTower(self:TowerOfHanoi, height - 1, from, use, to);
     moveTower(self:TowerOfHanoi, height - 1, use, to, from);
   endif;
}!!

Actor[#Hanoi] := new(TowerOfHanoi);!!

/* Example solves runs the Tower of Hanoi problem */!!
moveTower(Hanoi, 3, 1, 3, 2);!!

Example 4: List-handling support in Actor

C.B. Duff 7.13.86
(c) Copyright, 1986
The Whitewater Group
Technology Innovation Center
906 University Place
Evanston, Il. 60201 (312) 491-2370      */

now (Nilclass);!!
/* append nil to a node */
Def append (self, aNode)
{ ^aNode }!!

Def cons (self, aNode)
{ ^aNode }!!

Def rPrinton (self, aStrm)
{printon ('[' aStrm);
}!!

inherit (Collection, #ListNode, #(left right), nil,nil);!!

now (ListNode);!!

Def append (self, aNode)
{ ^cons ( left, append (right, aNode));
}!!

Def do (self, aBlock)
{if isAtom (left)
     then eval (aBlock, left);
     else do (left, aBlock);
     endif;
     if right
     then do (right, aBlock);
     endif;
}!!

Def isAtom (self)
{nil }!!

Def printOn (self, aStrm)
{printOn ('[' aStrm);
     printOn (left, aStrm);
     rPrintOn (right, aStrm);
}!!

Def rPrintOn (self, aStrm)
     printOn (' ', aStrm); printOn (left, aStrm);
     rPrintOn (right, aStrm):
     printOn (']', aStrm);
}!!

now (Object); !!

Def isAtom (self)
{ }!!

Def rPrintOn (self, aStrm)
{ printOn ('.', aStrm);
     printOn (' ', aStrm);
     printOn (self, aStrm);
     printOn (']', aStrm);
}!!

Def cons (self, aNode  |  newNode)
if isAtom (aNode)
then newNode :=     new (ListNode);
else newNode :=     copy (aNode);
endif;
newNode.left :=     self;
newNode.right :=    aNode;
^newNode;
}!!


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.