Oberon/F (1994)
Dick Pountain - 10/10/94 11:06 - 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.