Most user interfaces (UI) are a spaghetti of event handlers, timers, and signals. This is because they need to deal with user input coming in at arbitrary times, they need to deal with refreshing the screen, and they have to make sure that long running operations don't cause the application's windows to freeze up. All of these constraints make developing user interfaces in traditional languages and libraries very difficult.
The SRC Modula-3 implementation provides a UI library known as Trestle. The notable thing about Trestle is that it is highly concurrent. It was written to make extensive use of threads and to be used in a multithreaded environment.
This simplifies the development of user interfaces considerably, since you don't have deal with the event loop any more. An event loop is essentially “poor man's multithreading”. Since the language and libraries support first-class threads, these can be used instead. If the action associated with a button may take a long time, the action can merely fork off a thread to handle the bulk of the action. This thread can make arbitrary calls into Trestle to update the screen with new results. Trestle protects itself through judicious use of locks.
Trestle provides two sorts of objects: graphics objects such as paths and regions, and a base set of user-interface objects. These user-interface objects are known as “VBT”s. These play the same role in Trestle as the X intrinsics play in the world of the X toolkit. They define how screen-space is allocated among different “widgets”. Trestle provides a simple set of buttons and menus in its set of base UI items.
VBTKit is a higher-level toolkit built on top of Trestle. VBTKit provides a much wider array of UI object kinds. It also provides a Motif-like 3-D “look and feel” (on color displays). The same interface can be used on monochrome displays without change, but without the appropriate visual appearance. VBTKit provides the usual complement of scrollbars, buttons, menu items, numeric I/O objects, and the like.
FormsVBT is a User Interface Management System (UIMS) structured as a library and is built on top of VBTKit. FormsVBT provides a simple language for describing the layout of a user interface and an event interpreter for that language. The layout language follows a “boxes and glue” model. Boxes hold some set of UI objects. A VBox arranges those objects in a vertical display, while an HBox arranges them in a horizontal display. Glue is used to force a certain amount of space between items in a visual display. As you might expect, boxes can be nested arbitrarily.
The FormsVBT library allows you to specify callbacks to handle input. The FormsVBT specification that you write specifies the “syntax” of your user interface; the event handlers that you write provide the “semantics”.
FormsEdit is a simple UI creation tool built on top of FormsVBT. It reads and displays graphically, and in source text form, a UI specification written in the FormsVBT language. It also allows for interactive modification of the source.
There are times when you just need to develop some language-specific tools for a project. The problem is that very few language implementations give you any support in doing this. Many times, all you have is a public domain YACC grammar that you have to modify and then build from there. This is where m3 tk comes in. It provides a complete toolkit for parsing M3 source files; and generating and manipulating abstract syntax tree representations of M3 sources. Thus a M3 specific tool can be built with relative ease. In fact, the Network Objects (see below) stub generator was built using it.
Distributed systems are quickly becoming commonplace in the '90s. Most languages provide little or no support for distributed programming. Most distributed applications are still built directly on top of sockets or use libraries that provide a simple stream or RPC interface. These libraries are poorly integrated into the language and introduce a severe impediment between the language and the distributed system.
Network Objects is a facility in the SRC implementation of Modula-3 that allows Modula-3 objects to be exported across address spaces and machines. With Network Objects, a program can't tell if the object it is operating on is one that it created in its own address space or was one that was created and exists in another address space. This provides a very powerful mechanism for developing distributed applications.
To turn an object type into a network object, that object must inherit (either directly or indirectly) from the type NetObj.T. The object cannot contain any data fields. The interface containing the declaration is then run through a tool called a stub compiler. This generates all the coding necessary to handle network interactions. That's all that is required to allow an object to be passed around the network. Pretty simple. Below is an example of a network object. It defines an interface called “File” that defines the operations on a file, and an implementation of that interface.
INTERFACE File; TYPE T = NetObj.T OBJECT METHODS getChar(): CHARACTER; putChar(c: CHARACTER); END; END File; INTERFACE FileServer; IMPORT File; TYPE T = NetObj.T OBJECT METHODS create(name: Text): File.T open(name: Text): File.T END; END FileServer;
The above code defines two types: File.T, which is an object with two methods to get and put a single character; and FileServer.T, an object which manages file objects. A server someplace defines a concrete implementation of these abstract types.
MODULE FileServerImpl; IMPORT File, FileServer; TYPE FileImpl = File.T ...state for a file... OVERRIDES getChar := GetChar; putChar := PutChar; END; TYPE FileServerImpl = FileServer.T OBJECT ...state for a file server... OVERRIDES create := Create; open := 0pen; END; VAR fileServer := NEW(FileServerImpl); BEGIN NetObj.Export("FileServer", END; MODULE FileServerClient; IMPORT File, FileServer; VAR fileServer := NetObj.Import("FileServer file"); file := fileServer.Create("someFile"); BEGIN file.putChar('a'); END FileServerClient;
In the above code, FileServerImpl creates an instance of a file server and puts it into a name server. (The NetObj.Export call does this.) The module FileServerClient (which would be running in a different address space or machine) imports the file server implementation. This gives a valid Modula-3 object back to the client. From that point on, the client invokes methods on it as if it were local. It then creates a File object which it begins adding characters to.
If you have done any development with SunRPC or DCE, you will immediately appreciate how much simpler this is than programming on top of either of these systems. Network Objects is similar in scope to these systems, but is tightly integrated into the programming model instead of being a poorly integrated adjunct.
Two interesting systems have been built on top of network objects. The first is Obliq which is an object-oriented, distributed scripting language. Obliq can call into existing Modula-3 packages. You can also create Obliq objects and hand them to other programs running on other machines. Obliq is similar in scope to Telescript or TCL-DP (TCL with Distributed Programming extensions). The other system is Visual Obliq, which can be thought of as 'distributed Visual Basic for Modula-3'. It includes an interactive, graphical application builder. Callbacks are handled by Obliq scripts. This makes it a very powerful tools for prototyping and building distributed applications. It can also be used as the basis of interesting collaborative applications.