Steve is managing director of MADA and can be contacted at 10062 Miller Ave., Suite 202-B, Cupertino, CA 95014 or [email protected].
New object-oriented applications are announced every day. New object-oriented languages are another matter, however. When was the last time you saw a new commercial-grade, object-oriented language implementation? Beta (pronounced "Bee'-ta") is one such new language.
Development on Beta started in 1976 as a joint venture between universities in Norway, Denmark, and Finland--the same computer-science community that in the early '60s developed Simula, the first object-oriented language. Throughout its formative years, Beta research has been supported by grants from companies such as Apollo, Apple, and Hewlett Packard. Now, after more than 15 years of R&D, Mjolner Informatics has released the first commercial Beta implementation.
Beta Overview
Beta's design is heavily influenced by its object-oriented predecessors, notably Simula. It's strongly typed, like C++, with most type checking taking place statically at compile time. The language design is based on a powerful abstraction mechanism called a "pattern." Example 1 shows the syntax for a Beta pattern.
Attributes can be many things, the most common being object declarations and methods specific to the pattern being defined. The Enter statement lists the values passed to the Do section; Exit lists the values output from the Do section; and the Do section contains imperative (executable) statements that perform actions. All syntactic elements of a pattern are optional.
There are several types of patterns, including classes, procedures, functions, coroutines, processes, and exceptions. There are also three types of pattern derivatives: subpatterns (like subclasses but more powerful), virtual patterns (like C++ virtual procedures), and pattern variables (pattern pointer variables).
You can create any of these derivatives from any of Beta's pattern types, making Beta a very orthogonal language. Using subpatterns, for instance, you can create a hierarchy of procedures, functions, coroutines, processes, or exceptions, where all subpatterns inherit attributes from their superpatterns. You can also define virtual classes, virtual procedures, virtual functions, and the like.
Example 2, a simple class pattern defining a bank-account object, has three attributes: balance, a declaration using a predefined class pattern called "integer," and Deposit and Withdraw, a pair of procedure patterns. Note that:
- Asterisk/parenthesis pairs are used to enclose comments.
- A colon (:) indicates a declaration.
- The @ character indicates the name of an object type.
- Patterns can be nested; the applicable scope rules are similar to Algol.
- Imperative statements always read from left to right.
Reference Attributes and Syntactic Elements
Beta lets you define both static and dynamic reference attributes. Examples 2 and 3 show static object references. You can declare a dynamic reference to pattern Account with A1: ^Account;. You create objects dynamically by invoking their pattern name as an executable statement. For instance, &Account[] --> A1[]; creates a new instance of Account, returns a reference to the instance, and assigns the reference to A1. The difference between &Account and &Account[] is important: The first means "generate an instance of Account and execute it," the second means "generate a new instance of Account without executing it and return a reference to it."
Beta arrays (called "repetitions") are defined using one of the two forms in Example 4(a). Array index range from 1 to <Name>.range. (range is an intrinsic attribute of all array objects). You can dynamically resize arrays using the extend attribute, which adds elements to an array. You can reallocate an array, initializing all the elements to the default value for the array object type. You can also assign array slices (or parts of arrays).
The Beta for statement is in Example 4(b). <Index> is an integer declared locally within the scope of the for statement. It cannot be changed within the loop. The index always starts at 1 and increments by 1. It's possible to overcome the limitations of the for index structure using other patterns.
The if statement, shown in Example 4(c), is an unusual combination of traditional If and Case statements. E1_En are evaluations, I1_In imperatives. The else clause is optional. A Boolean evaluation might be written thus (albeit awkwardly):
(if (x>0) and (y<0) // True then_ // False then_ if)
Beta evaluation (assignment) statements are very flexible. You can use multiple assignment statements such as 3 --> I --> J. You can also combine multiple assignments with pattern executions and enter and exit redirection as in Example 5.
Subpatterns
A subpattern is a pattern that is derived from a superpattern and inherits all its attributes. (Beta does not support multiple inheritance.)
A subpattern is defined by following a pattern's name with the name of its superpattern. For instance, Example 6, defines the basic data structures for a transportation system that has two types of reservations. Each instance of FlightReservation and TrainReservation has its own explicit attributes, plus Date and Customer attributes inherited from the superpattern Reservation.
Certain rules define what you can do with subpatterns. First, you can only assign patterns to a pattern variable of the proper pattern or superpattern, allowing restricted dynamic run-time binding. Second, attribute references are restricted to those attributes belonging explicitly to the pattern being referenced.
It's easy to create a generic pattern that can be used with all objects in a subpattern hierarchy. Suppose you want to create an array of reservations in the system, regardless of their type. Example 7 shows how. This group of patterns works with Flight, Train, and Reservation objects.
Controlling Subpattern Imperative Sequencing
A subpattern automatically inherits the attributes of its superpattern. When a subpattern is executed, its superpattern's imperative statements are automatically executed as well. The superpatterns are normally activated first in the execution sequence, starting with the superpattern directly above a subpattern in the hierarchy.
When P3 is invoked in the pattern definitions in Example 8(a), the execution through the pattern hierarchy is from top to bottom; see Example 8(b). Beta overrides the default execution sequence between superpatterns and subpatterns using the inner statement which forces the execution of the imperative section of the subpattern one level down in the subpattern hierarchy. If you redefine P1, P2, and P3 as in Example 8(c), the execution of P3 initiates the sequence in Example 8(d).
Just as the attributes and imperatives of a subpattern hierarchy are combined during execution, the Enter and Exit lists are combined, too. The Enter part of a subpattern is the concatenation of the Enter part of the superpattern followed by the Enter part of the subpattern. The same is true for Exit.
Virtual Patterns
Revisiting the reservation system in Example 6, assume that each attribute has a Display pattern (not shown). Then, add another pattern to display all the attributes for each pattern; see Example 9. If you execute TrainReservation.Display, the complete set of attributes for that pattern are displayed because of the subpattern hierarchy. But if you execute Reservation.Display, only the Reservation attributes are displayed. The pattern Reservation.Display has no easy way of displaying attributes below it in the hierarchy. Clearly you need another solution for dealing with subpattern hierarchies where you may need to reference a pattern that isn't at the bottom, but you'd also like to access attributes below the referenced pattern.
One way of doing this is to define virtual patterns--patterns that can be extended in subpatterns. Although there are different ways to syntactically define virtual patterns, the Example 10 refinement to the reservation system shows the simplest and most direct. With this set of virtual patterns, Reservation.Display, FlightReservation.Display, and TrainReservation.Display display the proper attributes in the proper order.
Just as you can define virtual-procedure patterns to create cumulative sets of actions under one name in a hierarchy, you can do likewise with object patterns. Beta makes no real distinction between the two.
Basic and Container Libraries
Beta contains a wide variety of component and framework libraries for building both UNIX and Macintosh applications. Since Beta includes source code for all libraries, you can study both libraries and sample programs.
Most of Beta's libraries are built on top of simple patterns contained in the basic libraries, including:
- Patterns for all basic data types: integers, Booleans, reals, characters, and repetitions (arrays).
- Streams, for both traditional file streams and internal text strings, and UNIX-like put and get primitives for integer and real data types.
- Exceptions, ForTo, Cycle, and Loop control patterns.
- Stream patterns for repetitions, allowing you to apply integer and text put and get operations to the repetitions.
- A real number math function library, including conversion patterns and basic trig functions.
- systemEnv, a library of abstract superpatterns for concurrency operations, including semaphores, monitors, and coroutines.
- File, directory, and path management patterns for both HFS and UNIX file types.
- A process manager that manages interprocess communication via pipes or sockets.
- A general-purpose interface to external modules written in other languages, based on C and Pascal interface conventions.
- A low-level interface that lets you retrieve internal information about individual objects in the Beta system.
Macintosh Libraries
Although the basic and container libraries simplify writing the internals of a program, they don't provide a framework for creating and managing the user interface. Beta has extensive libraries that provide Macintosh (or X Window) toolbox access and higher-level abstractions for creating and controlling the UI.
Beta's toolbox library is basically just wrappers around toolbox calls. The documentation describes the methods used for handling all the toolbox interface components--value and VAR parameters, data structures, M68000 traps, and Beta's external library capabilities.
Beta's toolbox support is reasonably current, including AppleEvents and the Object Model. If you need wrappers for newer toolbox capabilities or managers, the Beta documentation describes how to build your own interface patterns.
The MacEnv library, which includes more advanced interface components, consists of basicMacEnv, controls, fields, scrollingList, figureItem, and odds and ends. BasicMacEnv, which must be used by all MacEnv programs, contains abstractions for dealing with menus and the menu bar, windows, interfacing to the Finder and Clipboard, and handling the mouse, keyboard, and screen. Other libraries provide supplemental features: the control library, which manages controls (buttons, small text-entry controls, and scroll bars) and items intended for programming dialog boxes; the fields library, which has patterns for displaying pictures in windows and for creating and managing various types of text editors; the scrollingList library, which has facilities for making scrolling lists of text string such as those you might put in a Standard File dialog; and the figureItem library, a small vector graphics library that implements graphics objects that may react to a user's mouse actions.
One of the strengths of the MacEnv library is the simplicity of event handling from the programmer's point of view. All Macintosh events are converted into invocations of special virtual patterns within the appropriate UI object. The patterns have default behaviors (when it makes sense) to respond to all events. To complete or refine the interface, you only have to override those subpatterns that need further definitions. (You don't have to create a giant event loop.)
BasicMacEnv manages menus and windows and all the basic event handling in a program. It defines a menu bar with the three standard menus--Apple, File, and Edit--and the expected commands. Default behavior is defined for obvious operations--opening and closing windows, clipboard operations, and so on. (Unfortunately, the default behaviors skip some basic functionality like printing and undo.) You can override the hit pattern for a command to further refine any menu command.
Beta menus have permanent and dynamic menu items. Permanent items (Open and New on the File menu, and Clipboard commands) are for menu items that are valid irrespective of the active window; there are default virtual patterns for all permanent items. Dynamic items are commands that may vary depending on the state of the active window. You need to explicitly attach and detach (enable and disable) dynamic items to coordinate them with the active window.
BasicMacEnv includes predefined patterns for all standard Macintosh window types, plus a wide range of appropriate attributes--title, position, and so on--with default values. Additionally, there are patterns for opening and closing windows, plus default behaviors for dragging, zooming, resizing, and other standard window behaviors. Listing One (page 116) creates a movable window with zoom and close boxes whenever New or Open is selected from the File menu (note how BasicMacEnv is invoked).
When the program is launched, three standard menus are automatically enabled: Open, New, and Quit. myWindow.open creates a resizable nonmodal window and enables the Close command on the File menu. You close the window by selecting the Close command or clicking the close box.
The fields Library
The fields library contains five patterns designed for displaying and manipulating higher-level data structures: pictureField for displaying pictures; textField, a sophisticated text-editing field with support for styled text and a variety of text-editing functions; abstractScroller, which implements scrolling for any windowItem object; textEditor, a full-fledged text editor canvas with scrolling capabilities; and scroller, a subpattern of abstractScroller which implements scrolling for a windowItem in a canvas.
Listing Two (page 116), a multiwindow text editor, is created by binding a textEditor object to each window opened with either the New or Open command on the File menu. All text editing, Clipboard operations, and menu management are handled automatically by basicMacEnv. In addition, a font menu with some basic size choices is created and added to the menu bar at startup. That menu can be used to modify the text contents of each of the editor windows.
Listing Two shows how easy it is to implement file support, styled text capabilities, and most of the expected default Macintosh behaviors simply by overriding some of Beta's library virtual patterns. However, it isn't a commercial-quality application--there's no industrial-strength file exception handling, no support for undoing editing commands or reverting to previous versions, and no support for intelligent font sizing.
The Mjolner Beta System
In addition to the Beta features described in this article, the Mjolner Beta System includes:
- Native code generation.
- Automatic garbage collection and storage management.
- Separate compilation of program fragments with automatic dependency analysis.
- Interfaces to C and assembly language modules (and Pascal on the Mac).
- An experimental persistent store.
- Process management and concurrency-control patterns.
- Application frameworks for X Windows, Motif, as well as the Macintosh Toolbox.
The Mjolner Beta System is not without flaws--the development environment is static, not dynamic, and it uses the native assembler and linker. You have to wade through compile, assemble, link, and run cycles to develop programs. The application frameworks are a good starting point, but they're missing basic printing, undo, and revert support. Finally, the documentation could be improved and expanded.
Keep in mind that these minor complaints are about the implementation, not the language. Beta springs from a unified, object-oriented theoretical foundation. The result is a very clean implementation of virtually all recognized and desired object-oriented capabilities in a simple, easy-to-understand language available in commercial-grade tools. Beta is truly an evolution of object-oriented technology.
For More Information
Mjolner Beta System
Macintosh, $295.00
UNIX, $2890.00
MADA
408-253-2765 (voice)
408-253-2767 (fax)
10062 Miller Ave.
Suite 202-B
Cupertino, CA 95014
Table 1:Standard container types.
Function Description <I>container</I> Abstract superpattern for all containers, including basic container attributes and exception-definitions collection an abstract superpattern for all position-independent container patterns. <I>multiSet</I> Unstructured collections that allow duplicates. <I>set</I> Unstructured collections where duplicates are not allowed. <I>classificationSet</I> Specialized set that lets you dynamically classify other sets into subsets and supersets, and manage their set operations. <I>hashTable</I> Basic hash table where you can define the hash ranges and hash function, with table searching, and table statistic calculations. <I>extensiblehashTable</I> <I>hashTable</I> where you can rehash the contents of the tab or extend the range of hash values. <I>arrayContainer</I> Element repetition abstraction, including put, get, and sorting operations. <I>sequentialContainer</I> An abstract container for sequentially-ordered collection of elements. <I>stack</I> A basic stack with push, pop, and top patterns. <I>queue</I> An ordinary queue. <I>deque</I> A double-ended queue with patterns for operating on both ends of the queue. <I>prioQueue</I> A sequential container where each container element has a numeric priority. <I>list</I> A more complex pattern that implements a double-linked list; many list operations take a list position as an argument. <I>recList</I> A recursive doubly-linked list.
Example 1:
<Pattern Name>: (# <Attribute 1> ; <Attribute 2> ; . <Attribute N> ; Enter <Input list> Do <Imperatives> Exit <Output list> #);
Example 2:
Account: (# (* a simple bank account class pattern *) balance: @integer; (* bank account balance *) Deposit: (# (* add `amount' to balance *) amount: @integer(* local declaration *) enter amount(* input list *) do balance + amount -> balance (* action *) exit balance(* output list *) #); Withdraw: (#(* subtract `amount' from balance *) amount: @integer enter amount do balance - amount -> balance exit amount #); #);
Example 3:
(# (* a program pattern with no name *) Account: (# (* a pattern declaration within the unnamed pattern *) acct_balance: @ integer; Deposit: (# amount: @ integer enter amount do acct_balance + amount -> acct_balance exit balance #); Withdraw: (# amount: @ integer enter amount do acct_balance - amount -> acct_balance exit amount #); Balance: (# exit acct_balance #); #); A: @ Account; cash_on_hand, balance: @ integer; do 100 -> &A.Deposit; 250 -> &A.Deposit; 75 -> &A.Withdraw -> cash_on_hand; (* $75 on hand *) &A.Balance -> balance; (* $275 balance *) #)
Example 4:
(a) <Name>: [size] @<Type> ;(* static repetition *) <Name>: [size] ^<Type> ;(* dynamic repetition *) (b) (for <Index>: <Range> repeat <Imperative-list> for) (c) (if E0 // E1 then I1 // E2 then I2 . . . //En then In else I if)
Example 5:
(# Power: (# (* compute X^n where n > 0 *) X, Y: @ real; n: @ integer; enter (X, n) do 1 -> Y; (for i: n repeat Y * X -> Y for) exit Y #) Reciproc: (# (* compute (Q, 1/Q) *) Q, R: @ real; enter Q do (if (Q // 0) then 0 -> R else (1 div Q) -> R if ) exit (Q, R) #); A, B: @ real; do (3.14, 2) -> &Power -> &Reciproc -> (A, B); (* A = 3.14 ^ 2, B = 1/A *) #)
Example 6:
Reservation:(# Date: @DateType; Customer: ^CustomerRecord #) FlightReservation: Reservation (# FlightNo: ^Flight; SeatNo: ^Seat #) TrainReservation: Reservation (# TrainNo:^Train; CarriageNo: ^Carriage; SeatNo: ^Seat #)
Example 7:
ReservationRegister: (# Table: [100] ^ Reservation; (* arbitrary size restriction *) top: @integer; Insert: (# (* insert a reservation *) R: ^ Reservation; enter R [] do R [] -> Table [top+1 -> top] [] #); NoOfRes: (# (* get number of reservations in system *) exit top #) GetRes: (# (* get reservation number inx *) inx: @ integer enter inx exit Table [inx] []; #); #)
Example 8:
(a) P1: (# a: @integer do 1 -> a #); P2: P1 (# b: @integer do 2 -> b #); P3: P2 (# c: @integer do 3 -> c #); (b) P3 invoked P2 invoked P1 invoked 1 -> a 2 -> b 3 -> c (c) P1: (# a: @ integer do INNER P1;1 -> a #); P2: P1 (# b: @ integer do 2 -> b; INNER P2#); P3: P2 (# c: @ integer do 3 -> c #); (d) P3 P2 P1 P2 (via INNER P1) 2 -> b P3 (via INNER P2) 3 -> c 1 -> a
Example 9: Building a virtual pattern
Reservation: (# Date: @ DateType; Customer: ^ CustomerRecord Display: (# do Date.Display; Customer.Display; INNER #); #) FlightReservation: Reservation (# FlightNo: ^ Flight; SeatNo: ^ Seat Display: (# do FlightNo.Display; Customer.Display #); #) TrainReservation: Reservation (# TrainNo: ^Train; CarriageNo: ^Carriage; SeatNo: ^ Seat Display: (# do TrainNo.Display; CarriageNo.Display; SeatNo.Display #); #)
Example 10:
Reservation: (# Date: @DateType; Customer ^CustomerRecord Display:< (# do Date.Display; Customer.Display; inner #); #) FlightReservation: Reservation (# FlightNo: ^Flight; SeatNo: ^Seat Display::< (# do FlightNo.Display; Customer.Display #); #) TrainReservation: Reservation (# TrainNo: ^Train; CarriageNo: ^Carriage; SeatNo: ^Seat Display::< (# do TrainNo.Display; CarriageNo.Display; SeatNo.Display #); #)
_THE BETA PROGRAMMING LANGUAGE_ by Steve Mann [LISTING ONE] ORIGIN '~beta/macenv/v3.5/basicmacenv' (* part of the fragment system *) --- program: descriptor ---(* for handling code modules *) MacEnv (* include the basic app framework *) (# myWindow: @window (# type::< windowTypes.zoom; hasClose::< trueObject; open::< (# do (100, 100) -> position; (300, 100) -> size; My window' -> title; #); #); FileMenu::< (# iNew::< (# hit::< (# do myWindow.open #); #); iOpen::< (# hit::< (# do myWindow.open #); #); #); #)[LISTING TWO]
<a name="02c4_001b"> ORIGIN '~beta/macenv/v3.5/basicmacenv' [[ --- INCLUDE '~beta/macenv/v3.5/fields' --- INCLUDE '~beta/macenv/v3.5/macfile' --- INCLUDE '~beta/basiclib/v1.3/file' --- program:descriptor--- MacEnv (# (* === Font Menu Patterns === *) FontMenu: @Menu (# fontSizes: [4] @integer; firstFontOset: @integer; theEditor: ^edWindow; Name::< (# do 'Font' -> theName #); (* create the font menu--names on top, sizes on bottom. the * handler distinguishes between the two using the item number. *) open::< (# do System.AvailableFonts (# do thisFontName[] -> Append #); separator -> Append; '9' -> Append -> firstFontOSet; '12' -> Append; '14' -> Append; '18' -> Append; 9 -> fontSizes [1];12 -> fontSizes [2]; 14 -> fontSizes [3];18 -> fontSizes [4]; #); (* override the standard event handler for this menu * to change the font or size of the current selection *) EventHandler::< (# EvalStatus::< (# Done::< trueObject #); Select::< (# do (if theEditor[] <> None // true then (if theItem.ItemNumber >= firstFontOSet // true then fontSizes [theItem.ItemNumber - firstFontOset + 1] -> theEditor.ChangeSize; else theItem.name -> theEditor.ChangeFont if);if); #); #); #); (* === Editor Window Patterns === *) edWindow: Window (# Type::< WindowTypes.Zoom; HasClose::< TrueObject; myText: ^StyledText;myFile: ^macfile; opened: @boolean; (* change the font type of the current text selection *) ChangeFont: (# aTextStyle: @TextStyle; start,end: @Integer enter aTextStyle.name do myEditor.Contents.Selection -> (start, end); (start, end, aTextStyle.FontID) -> myEditor.Contents.SetOneFont #); (* change the font size of the current text selection *) ChangeSize: (# fontSize, start,end: @Integer enter fontSize do myEditor.Contents.Selection -> (start, end); (start, end, fontSize) -> myEditor.Contents.SetOneSize #); (* create a dynamic menu item for the File/Close command *) DoClose: @theFileMenu.action (# hit::< (# do close #); #); (* create a dynamic menu item for File/Save command *) DoSave: @theFileMenu.action (# hit::< (# do (if opened // true then myEditor.saveMyEd else myEditor.saveAsMyEd if) #); #); (* create a dynamic menu item for File/Save As command *) DoSaveAs: @theFileMenu.action (# hit::< (# do myEditor.saveAsMyEd #); #); (* Window event handler--enable/disable the font menu and dynamic * File commands as current window gets activated / deactivated *) EventHandler::< (# Activate::< (# do this (edWindow)[] -> FontMenu.theEditor[]; FontMenu.enable; DoClose[] -> theFileMenu.CloseItem.Attach; DoSave[] -> theFileMenu.SaveItem.Attach; DoSaveAs[] -> theFileMenu.SaveAsItem.Attach; #); Deactivate::< (# do none -> FontMenu.theEditor[]; ; FontMenu.disable; theFileMenu.CloseItem.Detach; theFileMenu.SaveItem.Detach; theFileMenu.SaveAsItem.Detach; #);#); (* pattern to open an existing file *) openFile: (# do newFile; (if ('','Select a File to Edit: ') -> myFile.GetFile -> opened // true then myFile.RestoreStyledText -> myText[] if) exit opened #); (* pattern to create a new file *) newFile:(# do &StyledText[] -> myText[]; &macfile[] -> myFile[] #); (* pattern to open a new window *) open::< (# do 'Untitled' -> title; (if opened // true then myFile.entry.path.name -> title if); nextPosition -> Position; (350,400) -> Size; (5,10) -> nextPosition.add; myEditor.open #); (* === Text Editor Patterns--scoped inside Window to have === * === access to all of the Window attributes === *) myEditor: @TextEditor (# (* track text changes (to disable Save if no changes). *) docChanged: @boolean; ContentsDesc::< (# Textchanged::< (# do true -> docChanged #); #); (* open (create) a new text editor--bind to window *) open::< (# do this(edWindow).Size->Size; true->BindBottom->BindRight; myText[] -> Contents.contents; myEditor[] -> Target; false -> docChanged #); (* save the current document to disk *) saveMyEd: (# do contents.contents -> myFile.SaveStyledText #); (* save the current doc to disk, but allow a name change *) saveAsMyEd: (# do (if ('Select a Destination File Name: ',title) -> myFile.PutFile // true then saveMyEd; myFile.entry.path.name -> title if) #); (* close and (optionally) save the current text editor *) close::< (# do (if docChanged // true then saveAsMyEd if) #);#); #); (* === File Menu Patterns === *) FileMenu::< (# (* File/Open command *) iOpen::< (# hit::< (# myWindow:^edWindow; do &edWindow[] -> myWindow[]; (if myWindow.openFile // true then myWindow.open if) #);#); (* File/New command *) iNew::< (# hit::< (# myWindow:^edWindow do &edWindow[] -> myWindow[]; myWindow.newFile; myWindow.open #);#);#); (* === Program start - create the font menu, put it on the === * === menu bar, and set the first window tiling position. === *) nextPosition: @Point; do FontMenu.Open; FontMenu.disable; FontMenu[] -> Menubar.Append; (5,40) -> nextPosition #) ---]]
Copyright © 1993, Dr. Dobb's Journal