Oberon/F (1994)

Dick Pountain - 10/10/94 11:06 - Oberon/F

OBERON/F

Oberon/F is the latest recruit to that important new

software category, the object-oriented component

framework. XVT and NextStep's Interface Builder are

probably the most famous current exemplars, but Taligent's

TalAE will soon be contesting that title. Oberon/F

provides a thin object-services layer which runs on top of

the host operating system and enables you to write

cross-platform portable and extensible applications as if

the host OS supported full object-orientation. Currently

Oberon/F runs on Win32s for Windows 3.1, NT and 95 and on

Macintosh System 7, but versions are planned for OS/2 and

Unix/Motif.

Why component frameworks? Mostly because the class library

based approach to OOP which promised to deliver software

reuse has in practice have often disappointed (see

"Componentware", May 1994 Byte). Component frameworks aim

both to reduce the huge learning-curve of class libraries

and to enable software reuse by supplying big,

already-useful chunks. If using a class library is like

buying a load of bricks, then using a framework is like

erecting a pre-fabricated house, with the walls already

assembled.

Oberon/F is wholly document-centric framework in which

everything is a document that you can edit within the

development system, which also serves as the runtime

system. Every Oberon/F document contains one or more

'views', software components that enable viewing and

editing of a particular data type eg. a text, a picture or

a spreadsheet. Each view is implemented by a separate

module, which gets dynamically linked and loaded on demand

like a DLL, but unlike Windows DLLs (or VBXs) you can

extend Oberon modules. If the appropriate module is

present, any Oberon/F document editor can edit any type of

view, so the concept of applications owning files has

completely dissolved.

Oberon/F incorporates a highly-efficient but proprietory

compound document model that allows you to embed views

into one each other in arbitrarily complex ways. In future

releases this system will be progressively integrated with

the COM and DSOM models used in OLE 2 and OpenDoc.

THE DEVELOPMENT ENVIRONMENT

Where NextStep and Taligent are based on Objective-C and

C++ respectively, Oberon/F is based on the Oberon 2

language, Niklaus Wirth's successor to Pascal and Modula

2, and includes a compiler and debugger for the language.

(The Oberon/F system is being developed by Oberon

Microsystems inc. of Zurich, Switzerland, a commercial

firm founded by ETH alumni with Wirth as a director.)

Oberon is a strongly-typed, compiled language which

supports both modular and object-oriented programming, and

also Eiffel-like precondition and postcondition testing

using ASSERT statements. Unusually for a compiled language

it features an automatic garbage collector to preserve

memory integrity. (Taligent too has recognized that

extensible programming and 'malloc' don't mix, and is

adding garbage collection to its C++ compiler.) The

driving force throughout the Oberon project has been the

pursuit of simplicity, well illustrated by the fact that

my beta-0.9 Oberon/F system arrived on a single 1.4 Mbyte

floppy disk and occupies barely 4 Mbytes of hard disk

space.

The programming, editing and debugging environment is

completely integrated, giving it the same sort of feel as

interactive interpreted systems like Smalltalk or Lisp -

this is an illusion as Oberon compiles straight to 32-bit

native 486 or 680x0 code. However the compiler is so fast

at 15,000 odd lines per minute and the modules you write

are typically so small that compilation time is seldom

noticeable.

The compiler, debugger and other software tools are all

based on Oberon/F compound documents; they are active,

editable texts containing hypertext-style embedded

objects. For example in a Show Loaded Modules window you

can highlight any module name in the list and immediately

decompile its interface definition; in a debug window

clicking on diamond-shaped markers lets you follow

pointers and traverse lists; in the editor errors are

flagged by markers embedded in the text which expand into

error messages when clicked.

TEXTS

The first release of Oberon/F provides - in addition to

the Development subsystem - just two component subsystems

called Texts and Forms. An ODBC database subsystem is

planned for the second release. In-place editing of

industry-standard graphics and spreadsheet formats will

not become available until a later release supports OLE 2.

The Text subsystem is a wordprocessor with features

roughly equivalent to Windows Write (eg. it supports

fonts, paragraph attributes and object embedding), but

unlike Write you can extend this editor in any way you

like. As a test example I decided to add the ability to

change a selected passage of text into upper-case (see the

code in Listing 1). A rather minor achievement you might

think, but consider these points:

1) This is not just a macro (as is, say, WordBasic) but a

native 486 code extension to the system.

2) This new ability is available within ANY piece of text

whatsoever in Oberon/F, and will be too in any future

programs that I add.

3) I didn't need to recompile the text editor and indeed

have never even seen its source code, only the published

programming interface.

4) This same code works identically on a Windows PC or a

Macintosh and automatically displays the proper

'look-and-feel' of either platform.

In Oberon/F exported parameterless procedures are called

commands and are executable from anywhere in the system.

The procedure UpCase* (the asterisk indicates it's to be

exported by the module DickText) is a command which

performs its action on the selection of the window which

currently has the focus. You can execute commands by

selecting any instance of their name on the screen and

using the debugger's Execute command, by building

interactive 'tools' containing clickable embedded

'commander' buttons, or more conventionally by installing

them into the regular Windows or Mac menu bar. The menu

system is defined in an Oberon/F document that you can

edit like any other, and you can even install the new

menus on-the-fly without restarting the system.

You can program Oberon/F at three levels of complexity,

and my little example illustrates the simplest, command

programming, which adds new functions to an existing view.

The next higher level is the writing of new views, ie.

visual representations for data types. The third, and

hardest level is the writing of 'container' views which

can contain other embedded views; Oberon/F editors are

normally container views.

FORMS

The Forms component is a simple visual design tool for

data entry forms and dialogs. First you write a code

module that defines a record data type with various

fields, and then you design the corresponding form by

visually dragging control objects around on it as in

Visual Basic. Thereafter the Oberon/F runtime system

creates and maintains the connections between the screen

fields and the underlying data structure without you

having to write any further code, automatically updating

the field variables whenever the user enters data into the

form. The reverse process is not automatic, so when your

program updates a record field it must broadcast an update

message telling all screen views that they need to change

too.

Oberon/F forms are stored as documents like any other and

you can modify their visual appearance without forcing a

recompilation of the application code, a great advantage

compared to conventional code generators. You can embed

forms in texts and vice-versa, recursively to any depth,

to construct a wide variety of user-interface styles. The

beta-release I tested supported the standard Windows and

Mac control types (text fields, captions, scroll bars,

push buttons, radio buttons, and check boxes) but Oberon

Microsystems are working to support OLE 2's .OCX format so

that future Visual Basic custom controls will be usable

from within Oberon/F.

MODELS, VIEWS AND CONTROLLERS

Oberon/F is designed around a heirarchy of abstractions

that isolate modules from the physical hardware (for

cross-platform portability) and from one another (for

extensibility). The physical display, printing and file

systems are hidden in abstract object classes and are

accessed by creating reader and writer objects for them.

The most fundamental data type is a Store which represents

a body of persistent data that knows how to save and

retrieve itself from a non-volatile medium like a hard

disk. The module 'Stores' supplies readers and writers

that can map Oberon data types like characters, integers,

sets and other stores into binary data; stores can contain

other embedded stores and hence can represent compound

documents. Store is an abstract type that is never

instantiated directly; instead Oberon/F supplies three

extensions of Store called Models, Views and Controllers.

This MVC (Model-View-Controller) paradigm was originally

devised by the Smalltalk team at Xerox PARC; it divorces

the presentation of data from its storage and abstracts

from the OS-specific details of windows.

Crudely put, the model is the data itself while a view is

a particular presentation of the data transformed into a

rectangular display area. There may be many views onto the

same model, and if the model is changed this fact must be

broadcast to all views by sending messages. A view might

directly handle interaction with the user's mouse and

keyboard, but in complex applications this task is usually

delegated to a Controller object. Models, Views and

Controllers are themselves extensible, and in Listing 1

you'll see the use of a TextController object 'c' to

measure the current text selection, while the actual

processing takes place directly on a TextModel called

'buf'.

SAFETY FIRST

Though Oberon/F makes great use of inheritance internally

(eg. Stores -> Models -> TextModels) it strictly controls

external inheritance to preserve extensibility, by

imposing the classic separation of interface from

implementation. Many modules deliberately don't export

concrete types used in their interface, to prevent

application programmers from extending them directly.

Instead they export a global variable containing a

'directory object' whose 'New' method allows you merely to

create instances of a hidden concrete type, together with

an abstract interface type which you can inherit to

re-implement extensions of the type. This mechanism

retains most - though not all - of the power of

inheritance, but it's necessary to guarantee the future

extensibility of the program's semantics without running

into the so-called 'fragile base-class' problem (see

"Extensible Software Systems " May 1994 Byte).

In the messy world of PC operating systems the Oberon/F

approach of simplicity and austerity could hardly be more

at odds with industry practice. C++ programmers like to

party, and then use industrial-strength debugging tools

like BoundsChecker and Purify to clean up the mess

afterwards - the Oberon programmer expects to catch 90% of

errors at compile time and most of the remainder by

careful choice of preconditions. But then, twenty years ago

who'd heard of Diet Cola....

PRICE: Commercial version - $350

Educational version - free.

ADDRESS:

Oberon Microsystems Inc.,

Solothurnerstrasse 45

CH-4053 Basel

Switzerland

Fax: +41-(0)61-361-3846

Email: oberon@applelink.apple.com

---------------------------------------------------------------

Listing 1

MODULE DickText;

IMPORT TextModels, TextControllers;

PROCEDURE UpCase*;

VAR beg, end: LONGINT;

ch: CHAR;

c: TextControllers.Controller;

buf: TextModels.Model;

r: TextModels.Reader;

w: TextModels.Writer;

BEGIN

(* determine extent of selected text *)

c := TextControllers.Focus();

IF (c # NIL) & c.HasSelection() THEN

c.GetSelection(beg,end);

(* make a buffer for upper-case text *)

buf := TextModels.dir.New(); (* a directory object *)

w := buf.NewWriter(NIL);

r := c.text.NewReader(NIL);

r.SetPos(beg);

(* process selected text into buffer *)

r.ReadChar(ch);

WHILE (r.Pos() <= end) & ~r.eot DO

IF (ch >= "a") & (ch <= "z")

THEN ch := CAP(ch) END;

w.WriteChar(ch);

r.ReadChar(ch)

END;

(* copy buffer back into document *)

c.text.Delete(beg,end);

c.text.CopyFrom(beg,buf,0,end-beg);

END

END UpCase;

END DickText.