|Column Tag:||Mac OOPS
Prograph Raises OOP To New Height
By Mark Szpakowski, Nova Scotia, Canada
Prograph is a very high-level, pictorial, object-oriented programming language and environment, with integrated Application Builder for easily creating all the components of a Mac interface. Although new as a deliverable implementation, the language, under development since 1982, is rich and expressive, an inheritor of the Lisp, Prolog and Smalltalk traditions. The Prograph 1.2 Editor/Interpreter is available now for $195 from The Gunakara Sun Systems of Halifax, Nova Scotia, Canada.
Prograph is thoroughly pictorial -- text is used only for names and for comments, and serves no syntactic or semantic function. Prographs dataflow-style design diagrams are the source code -- they are immediately executable, and may be animated to display execution progressing through them. This makes debugging easy and intuitive. Prograph 1.2 fully supports the single-inheritance model of Object Oriented Programming (OOP) in a visual manner that preserves OOPs inherent simplicity and naturalness. Like NeXTs NeXT Step environment, Prograph provides a set of inheritable classes with automatic event handling and associated WYSIWYG graphic editors for creating menus, menu items, windows, and window items. In addition, Macintosh Toolbox calls, with names exactly as used in Inside Macintosh, may be made from Prograph.
Prograph is thus an environment both for the hacker who needs to get into the nitty-gritty of the Macintosh and for the user who wants to create Mac-style applications without having to master 5 volumes of Inside Macintosh. Both types will find that, after some exposure to Prograph, moving back to a purely textual language is like moving back from a Mac interface to a line-oriented one. Youve been warned!
Fig. 1 Prographs Opening Screen
Dataflow Pictorial Programming
In this article Ill introduce the look and feel of Prograph programming, describing both the language and the environment. When Prograph starts up, it displays two windows, a Classes window and a Universal window. Well get to the Classes window later. The Universal window is used for defining methods which are not specific to any class (universal methods). A method is roughly equivalent to a procedure or multi-valued function -- it performs some action, taking 0 or more inputs and returning 0 or more outputs. To create a method, click in unoccupied space (click-space) in the Universal window: a new, unnamed method icon appears. This is a general principle in Prograph: clicking-space in a window creates an object appropriate to that window.
Fig. 2 An Unnamed Method Icon
An insertion point below the icon permits immediate typing of a name for the method. Command-clicking on the icon opens another insertion point, for typing a multi-line comment. This comment may be moved, follows its method icon when that is dragged, may be hidden or displayed at will (by command-clicking), and will appear in Prographs Info windows when that method is selected there. Comments may also be hidden or shown for a window as a whole. Comments may be attached to most icons in Prograph, making it largely self-documenting.
Fig. 3 Method with name and comment
Double-clicking on the method icon will open a window onto its structure. The stacked shape of the icon is meant to suggest that a method consists of a series of cases. Execution in a method begins with the first case, and will either proceed through the first case or go on to the next, depending on whether controls attached to operations within the case have or have not fired. The window banner gives the method name, the current case number, and the total number of cases in that method (as in show screen size 1:1 below). The icons between the name and the zoom box are used for navigating between cases.
Fig. 4 Case Window with Un-named Oper
Each case has an input bar and an output bar, on which inputs and outputs accumulate. Data in general flows from top to bottom, from the input bar, through operations, to the output bar. An operation is created by clicking-space in a Case window, resulting in an un-named operation icon appearing, with an insertion point within it for typing its name. This name may be typed in, or it may be transferred into the selected icon from any of several scrolling lists of names accessed via the Info menu. Typos may thus be completely eliminated. Lists are available for Prograph primitives; Mac Toolbox calls, globals, constants, etc; and classes, attributes and methods, whether these are system-supplied or created by the user.
Fig. 5 User-defined operation, primitive, constant, and comments
Hitting return after a name has been supplied causes Prograph to supply the icon with its default arity (inputs and outputs), as represented by terminals just above the operation icon, and roots (sources of data) just below the oper. In Fig. 5, show is a primitive, visually distinguished from a user-defined operation screen size by the line on the bottom end of the icon. A datalink is created by selecting a root, holding down the option key, and then clicking on a terminal (e.g., the two datalinks between screen size and show). The screen size operation will return two numbers (for the screen right and bottom coordinates). These will flow into the show primitive, which concatenates its inputs and displays them in a dialog window. The constant , will appear between the two coordinates. Thus the result of executing this should be that, for example, the string 640, 480 (depending on the monitor) will be displayed.
Double-clicking on the show icon will open an info dialog, giving the documentation on that primitive: a brief description of what it does, what inputs and outputs it expects and what their types are. Double-clicking on a user-defined operation icon, such as screen size, will open the first case window of the method it calls if it exists, or, if it doesnt yet exist, will open a new case window so that definition of that method can begin.
Note that weve attached comments to the two roots of screen size. When we double-click on screen size to create its first case window, those comments will also appear on the output bar of the screen size method.
Fig. 6 Mac Global and 3 Mac Get Fields
The screen size method begins by taking the bit map stored in the screenBits Mac Global, extracts its bounding rectangle by way of the bounds Mac Get Field, and then gets the right and bottom coordinates from that, with the results flowing out the output bar. Note the inherent parallelism: the right and bottom operations could execute in parallel on a parallel machine. Prographs Opers menu has menu items for transforming operations into Macintosh Toolbox calls. Double-clicking on a Mac operation icon opens an Info dialog which provides type information on the operations inputs and outputs.
Executing and Debugging
Fig. 7 Executing a method
To execute the show screen size method, one simply selects its icon in the Universal methods window (or else makes sure its case window is the current front window) and selects Execute Method from the Execution menu. A dialog will appear, displaying 640, 480.
Fig. 8 An Execution Window
Selecting Step/Show On from the Execution menu with screen size selected (or frontmost) puts it into single step mode, with its case window visible during execution. The dotted background indicates that its an execution window, as opposed to an editing window. When execution reaches this method it will pause; hitting return will cause execution to proceed on step at a time. Already executed operations, roots, and terminals are displayed normally. The next operation to executed is highlighted. Operations still pending are shown grayed-out. Double-clicking on a root or terminal opens a window onto the data value at that point. If the value is changed, execution will roll back to accommodate the change, and then continue forward again. Double-clicking on the dotted background opens an editing version of the window, where changes can be made in the design diagram itself. When execution resumes, it will roll back to the point of change, and then continue from that point.
If an error occurs during execution, Prograph beeps twice, and opens an execution window onto the case where the error occurred, with the offending operation highlighted. Selecting Last Error from the Info menu gives information on the nature of the error. A stack window may also be opened, displaying icons of methods as they call other methods. The stack may be popped, or icons double-clicked to open execution windows onto methods on the stack.
Fig. 9 Renowned recursive factorial function with stack window
The factorial function in Fig. 9 illustrates how cases are used. Factorial expects a positive integer as input. Case 1 takes this input and compares it to 1 via the match operation (the 1 with the bar above it). A match either succeeds or fails. The little square attached to the right of the match is a control, with the X within it indicating that the control will activate on failure of the match. A check mark ( ) within a control means it activates on success of its attached operation. Here the control is Next case on failure: i.e., if the input data does not match with 1, go on to the next case. If it does match with 1, the input data is simply passed on to the output (factorial of 1 is 1).
Case 2 of factorial takes the input, decrements it (the -1 operation), recursively takes factorial of that, and multiplies the result by the input (factorial of n is n times factorial of (n - 1)).
As factorial executes, its progress may be observed in the stack window. The numbers within the method icons indicate which case of that method is executing.
Multiplexes and Controls
Here is another example which will illustrate both controls and mutliplexes. Multiplexes are annotations applied to primitives or to user-defined operations which transform them into list-processing, iterative, or looping operations. I.e., the operation applies itself to each of the elements of a list, or loops until some control causes it to terminate. In this example a dialog with three buttons is put up; pressing the first two evokes some response, with the dialog coming up again, while pressing the third, Cancel, button terminates the whole thing.
First thing we do is to create a method, called Marx Bros, consisting of a single operation, called about Marx. This operation has been annotated as a Repeat, via the Repeat menu item in the Controls menu. Repeat will continue executing until a Terminate or Finish control within it fires.
Fig. 10 A Repeat-Annotated Operation
To create the about Marx method, double-click on the left side of the about Marx method call. Its first case window opens up, and the method can be defined as in Fig. 11. The answer primitive is just like its HyperTalk namesake: the first input is a prompt, with up to three other inputs specifying button names. The text of the button clicked flows down to the respond method.
Fig. 11 A Terminate on Failure Control
Selecting the respond oper, and annotating it with Terminate from the Controls menu attaches a Terminate on Failure control to it. This control means stop the current multiplex operation immediately, and return whatever (if anything) was on the output bar on the last successful iteration of this multiplex. There is a related control called Finish which first finishes execution of the current case, returning whatever is on its output bar, before halting the multiplex. Double-clicking on the respond oper now lets us define it.
Case 1 of respond checks if the input matches with Harpo. If not, it goes on to the next case (Next Case on Failure); if so, it feeds the phrase the clown into the show primitive, which displays it. The about Marx method now re-executes. Case 2 does the same for Groucho, using the phrase Cigar!. Case 3 will only be reached if the user pressed neither the Harpo nor the Groucho button (i.e., Cancel must have been pressed). It does nothing at all, except that when execution reaches the output bar, the Failure control attached to its right end will fire. The check-mark on the control means that it fires on success of its attached operation (here, the output bar). The failure will be propagated to the calling operation, the respond operation in the about Marx method. So when Cancel is clicked, respond will fail, and, since it has an attached terminate on failure control, that control will fire, terminating repetition of the about Marx method.
Fig. 12 3 Cases, and a Failure Control
Fig. 13 Fail on failure Control
Fig. 13 illustrates a more economical way of doing the same thing: attaching a Fail on failure control to the Groucho match makes the third case unnecessary. If the button clicked is neither Harpo nor Groucho, the Fail control is triggered, causing respond to fail, causing its Terminate control to fire, ending the loop. This just gives a taste of the power of Prographs annotations (cf. Fig. 14).
Fig. 14 Opers and Controls Menu
Classes, Objects, Attributes, Methods
We now turn to classes. A click-space in the Classes window creates a new class icon, which, as usual, can be named and commented. Selecting a class and then option-click-spacing creates a new subclass, connected to its parent by an inheritance arc. The subclass inherits the attributes (equivalent to class and instance variables in Smalltalk) and methods of its parent. Class hierarchies are thus defined in Prograph the way they usually are on a napkin over coffee, by simply drawing the class hierarchy tree. Fig. 15 shows a small class hierarchy.
A class icon has two sides. Clicking on the left side opens the classs Attributes window, where its attributes can be defined and edited, while clicking on the right side opens its Methods window.
Class attributes, which have one value for the class as a whole, are displayed above the fuzzy line in the Attributes window (cf. the person attributes window), while instance attributes, whose values may vary with each instance of a class, are displayed below the line. Instance attributes are represented as triangular icons, and class attributes as hexagonal icons. The attributes name is below the icon, while its default value, or the class of its value if that value is a class instance, is indicated above the icon. Inherited attributes are displayed with a downward pointing arrow within their icons (cf. the student attributes window).
Fig. 15 Classes and attributes windows
An object (an instance of a class) is created by an Instance annotated operation. This simply produces a default instance of the class whose name it bears. The object will appear on the root of the instance oper, ready to flow into other operations. Making an object is thus a snap in Prograph: click-space in a Case window, annotate with Instance from the Opers menu, give the oper the name of a class, and Execute: bang, an object flashes in and out of existence. It flashes out of existence because if it doesnt flow down a datalink somewhere it will be immediately garbage-collected. However, if you put the method of Fig. 16 into Step/Show mode, execute it, and double-click on the root of the person oper, a Value window will open up, displaying the class and the attributes of the object (in this case, the default person object) on that root.
Fig. 16 Instance generator
Double-clicking an attribute icon opens a value window for entering its value. One may enter a simple datatype (Prograph supports boolean, integer, list, macintosh, none, null, real, string and undefined) or an object (a class instance). Value windows have several panels which permit creating or browsing objects. One is a scrolling list of classes and types; selecting one of these creates the default value or object of that type or class. If a class is selected, the default attribute values of the object created appear as icons in the value panel, just as they do in an attributes window, so they can be double clicked to edit their values, etc. This effectively amounts to a powerful instance browsing facility.
Fig. 17 Get and Set Attribute Operations
Attribute values are accessed at runtime by means of Get and Set annotated opers, as in Fig. 17. The name of the oper is the name of the attribute accessed. Get Attribute icons have a convex left side, while Set Attribute opers are concave on their left side. In general, class operations expect an instance of a class on their leftmost terminal. Here, we expect an instance of class person (or of a subclass of person) to flow out the input bar and into the terminal of the age Get Attribute oper. Get Attribute opers have a single terminal, where the object flows in, and two roots: the object flows out the leftmost root, and the value accessed flows out the right root. Here the age flows through the +1 oper, where it is incremented by 1, and then into the right terminal of the age Set Attribute oper. Set Attribute opers have two terminals: the instance flows through the left one, while the value to which the attribute will be set comes in the right terminal. The modified instance then flows out the single root, where it is available for further operations; here, it just flows out the output bar.
The birthday method is a method of class person. Its window banner (cf. Fig. 17) identifies it in <class-name>/<method-name> <case#>:<total-# of cases> format. Method windows of classes are accessed by double-clicking the right side of a class icon, or via the Windows menu. Method icons in class method windows may also be annotated with Instance, Get and Set to indicate that they are methods which overshadow the normal instance generation and get and set attributes of the same names.
Overshadowing New (instance generation) is useful for performing initializations on objects. Overshadowing Set may be used to do type-checking; e.g. in Fig 18, the Set-annotated age method might coerce age to an integer. Similarly, the Get annotated grade pt avg method in class student might require a password in order to access the students grade point average.
Fig. 18 Overshadowing New, Set & Get
Class-based methods normally take as their leftmost input an instance of a class. At runtime Prograph then looks for a method of that name in the class of the instance. If it doesnt find it there, it looks in its ancestor class, and so on up the inheritance chain. The user may create a method which overshadows an inherited method by giving it the same name as the inherited method. Access to overshadowed methods is still possible by way of the super annotation, which effectively says: start the search for a method of this name not in the current class, but immediately above.
Calls to class-based methods have names which begin with a slash (/). This distinguishes them from universal method calls. It is also possible to explicitly designate a methods class by giving its name in the form <class-name>/<method-name>. This makes it possible to use class methods (as opposed to instance methods): for example, to write a method which gives information about all the instances of a class, whether or not there are any.
Prograph System Classes and the Application Builder
Fig. 19 System and User Classes
Fig 19 shows the System Classes supplied with Prograph; they form the basis of the Application Builder. System classes are distinguished by two solid lines on their bottom ends. System attributes are also visually distinguished by having double-line edges. They may act as triggers: setting them causes certain actions. System classes may be subclassed by the user (cf the icon win and rsrc menu subclasses). Certain system classes may be viewed graphically with an associated WYSIWYG graphic editor which opens either onto the class default or a specific instance. Menus and windows have such graphic editors; windows also have subsidiary editors for the different classes of window items.
Any application created by the user is an instance of class Application. At any given time there is one active application, pointed to in the current class attribute of class Application. Likewise there is one frontmost window, pointed to by the front class attribute. Applications also have lists of menus and windows (menu lib and window lib), and lists of menus and windows which are active (menus and windows).
Fig. 20 Some attributes of class Application
Setting the system attribute active? of a system class object to TRUE makes it active. In the case of a menu, for example, it will appear on the screen. Fig 21 shows a generic close method supplied by class Window.
Fig. 21 Windows close method
We will now produce a small sample application, using the Application Builder: an icon browser which will put up a window with a scrolling list from which icon numbers/names can be selected in order to display those icons. The starting point is to load the System Classes into Prograph (double-clicking the System Classes icon will do). Then, planning ahead a bit for future expansion of the program, we create the subclasses icon win and rsrc menu as shown in Fig 19. Selecting Edit Application from the Execution menu will now open the Application Editor (Fig. 22).
Fig. 22 Application Editor Main Dialog
An application can have a name, an About method, and windows and menus. Giving the application a name will supply that name to the About menu item automatically provided in the Apple menu by Prograph. Filling in the name of an About method, such as /about, will produce a call to a method of that name in class Application (by default, although you can explicitly specify a class name) when the About <Application-Name> menu item is selected.
The Windows and Menus radio buttons control whether one is working with the applications windows or menus. The scrolling list on the left, labelled Classes, displays a list of classes and subclasses of type Window (or Menu). Double-clicking one of these, or clicking on the New Instance button, creates a new default instance of that class, with its name shown in the List of Instances scrolling list. Double clicking one of those instances then opens its corresponding editor. When a menu or window is created, it is by default added to the applications library list. Selecting it and clicking Add to Active List adds the menu or window to the applications active list. One can switch between viewing the library or the active list by clicking the appropriate radio button. After a menu or window instance has been developed, it can be used as the template for its class, defining that classs default instance, by way of the Set Defaults button.
Prographs Menu Editor permits building a menu by filling in its name, and the names of its menu items, associated methods, and command-key equivalents. The menu appears on the right side of the dialog. The method name associated with a menu item is given in <class-name>/<method-name> format, with the default class being the class of the menu instance. In Fig. 23 the Icons menu is an instance of class rsrc menu (cf. Fig 19), so the method /ICONs, corresponding to the Show Icons menu item, will be looked for in that class when Show Icons is selected.
Fig 23 Menu Editor
The window editor is invoked by double-clicking a window instance in the window List of Instances scrolling list. It displays the default window of its class; Fig 24 shows the Icons window, an instance of class icon win (cf. Fig 19). Command-clicking, or dragging on its window banner if it has one, drags the window, thus setting where on the screen it will appear.
Fig 24 Window editor view of Icons window
Command-double-clicking opens a window editor, from which the various characteristics of the window, particularly its title, close method and type, can be set. Icons is a Document window with no Grow Box. Its Close Method is /close, which means it will be looked for in class Icon Win, and, if it doesnt exist there, the /close method it inherits from class Window will be used.
Fig 25 Window editor
Fig 26 Classes of Window Items
Window items are created by click-dragging, which creates an outline of an item. Double-clicking the item then opens a scrolling list (Fig 26) from which the class of the item is selected (cf Fig 19). Once the class has been selected, the specific editor for that class appears (Fig 27). The Icons window has three window items: a Scrolling List which will contain the IDs and names of icon resources, an Icon item in which the icon will be displayed, and a Show button, which will display a selected icon. The scrolling list is assigned a method call /dbl-click to respond to double-click selection within it, and the Show button has a click-response method called /Show(both methods assigned by default to class icon win).
Fig 27 Button Window Item Editor
Once the window and its items has been defined, we can leave the window editor and, in the application editor, set them both to be active. Leaving the application editor, we immediately start the application running, by selecting Run from the Execution editor. The Prograph editing windows and menubar disappear, to be replaced by the Apple menu, a File menu, and the Icons window. The application is running!
Clicking in the close box of the Icons window makes it go away, since it has been assigned a /close method inherited from class Window. To make the window re-appear we select the Show Icons menu item. We have not yet created its corresponding ICONs method, so Prograph pauses execution and asks if we now want to go ahead and create it (Fig 28).
Fig 28 Creating a method on-the-fly
We click OK, and an execution window (Fig 29) opens onto the first case of the method, in the appropriate class (rsrc menu). Single-stepping execution once makes the roots at the input bar available for inspection via double-clicking: the left one is the menu instance, and the right one the menu item.
Fig 29 Newly created ICONs method
We want this method to do the following: if the window exists, make it the front window; otherwise, make a new instance of icon win. Double-clicking space in the window opens an editing version of it, and we can fill in the details as in Fig 30. System classes all inherit an owner attribute from
Fig 30 ICONs menu item method
class System (cf Fig 20). So we get the application which is the menus owner, and then use the find-window-a primitive to search its active window list for a window named Icons. If we find one (i.e., its not NULL) we set it to be the front window. Otherwise we go on to Case 2, where we create an instance of class icon win.
Whenever we create an icon window we want to have its scrolling list display a list of numbers and names of resources of type ICON. To do this well create a method, annotated with Instance, which will be invoked whenever an icon win instance is created. First we add another attribute to that class, by double clicking on the left side of the instance generator (Fig 30): the attributes window of its class opens, and we add an attribute, rsrcType, which will hold the class name (icon rsrc) of the resource displayed in that window. Closing that window and then double-clicking the right side of the instance generator opens its methods window, where we click to create a method icon and annotate it with Instance from the Opers menu. Double-clicking that opens it, and we define it as in Fig. 31.
Fig 31 icon wins New method
First, the window is made active, and the class name of its type of resource is fed into the get list method of class rsrc, which generates a list of resource names/numbers. The scrolling list window item named Icons is then found, and its value list attribute set to that list (value list is a System Class attribute of a scrolling list window item instance which, when set, triggers display of that list).
Class rsrc, and its subclass icon rsrc (cf. Fig 19) may now be created, with each subclass of rsrc inheriting a class attribute type which will hold the four-character type of that resource (for icons, this is ICON). The get list method takes as its right input a name of a class, and feeds that into the type get-attribute; this is how one accesses the value of a class attribute without first creating an instance of that class. This flows into the CountResources Mac Toolbox call, with the resulting count flowing into the rightmost, loop-annotated terminal of info list (the two slashes preceding its name mean use the class of the current method). The leftmost terminal of infolist is also a loop, initialized with the empty list. Each time through the loop, the count of number of resources is decremented; when the count reaches zero, the loop terminates. Meanwhile, GetIndResrource gets the resource type and index (count), accessing the resource and making it available to GetResInfo. The resource ID number is converted to a string and then joined to the name, separated by a space, with the resulting string then attached to the right of the list. When the loop terminates, the list of resource IDs/names flows out of info list and out of get list, and gets displayed as in Fig 31.
Fig. 32 rsrc attributes and methods
Fig. 33 Make list of resources
Whew! Remember, we are still in the midst of execution! We can now close our editing windows, which brings the execution window of the ICONs method to front, and, by hitting Return, we can step through the method. The Icons window will appear, with a list of icons.
If we double-click on an icon ID/name, we can go through a similar process to define the dbl-click method that gets invoked. Again we will be asked if we want to create that method, and we can fill in the details as in Fig 34.
Fig 34 Responding to a double-click
Here the selected item in the list (identified by its index in the list) is extracted, and then the resource ID number taken out of that via the extract rsrc number local and passed on to the rsrc number set attribute operation, which causes the icon to appear in the TheIcon window item.
Fig. 35 Extracting the ID number
A local is somewhat like a macro: a compaction of a number of operations into one. A multiply-selected group of operations is turned into a local via the Opers to Local menu item in the Opers menu; there is also a Local to Method menu item for turning that into a named method if it turns out to be a generally useful one. Locals are called by dataflow rather than by name: the name of a local serves as a descriptive comment. Double-clicking the local opens it (Fig 35). The in primitive searches for a substring (here, a space), returning its index within the string. Decrementing that gives the character position of the last digit of the ID number, which is then extracted with the prefix primitive and turned back to an integer via from-string.
The final step is to write a Show method to respond to clicking the Show button (Fig 36). Here the trick is to make sure the selection list (list of indices of selected items found in the select list attribute of the Icons scrolling list) is non-empty. If it is empty the method terminates, and nothing happens. If its non-empty its first element is extracted and passed to the rightmost terminal of dbl-click, which does the rest of the work.
Fig 36 Method for the Show button
That completes the Icon Browser application. A real simple About method can be added as in Fig 37.
Fig 37 About method for Icon Browser
This completes this first introduction to Prograph's seamless execution/debugging/editing environment and to the language. Further articles will follow, among others detailing how to add primitives to Prograph, using C.
Mark Szpakowski has been working with computers since 1972. He started in on the Macintosh from the beginning. Mark went over to The Gunakara Sun Sytems back in 1987. Prograph is planning on shipping in mid or late June. You can reach TGS Systems at:
The Gunakara Sun Systems
1127 Barrington Street, Suite 19
Halifax, Nova Scotia, Canada B3H 2P8
Fax: (902) 429-9983