by Arthur Chance, Richard G. Newell & David G. Theriault
Smallworld Magik is an extremely powerful language for the implementation
of large interactive systems. The language is a hybrid of the procedural and
object oriented approaches and program development is carried out in an interactive
It would be pertinent of the reader to ask, why would a small company like
Smallworld devote considerable resources to the creation of such a development
environment? Are not the common languages available on the market today adequate
for the job? Well, the simple answer to the second question is no and it is
the purpose of this paper to try and give an answer to the first.
Traditionally, large interactive systems were developed in a non-interactive
procedural language, such as Fortran or C. In order that an end user could
drive such a system, an interactive command language was provided so that the
user could type in his commands. Many command languages evolved into programming
languages usually by borrowing the programming concepts of Basic. In more modern
user interfaces, this command language may well be hidden behind a system of
screen menus, tablet menus or other input devices. Large modular systems were
glued together using an operating system command language or script. Within
the system, developers and customisers had a number of other languages available
to them to define such things as syntax, menus, data descriptions, graphic
codes, etc, all running as different processes communicating by files.
The structure of such systems were commonly organized at the highest level
around the command syntax and complex commands were structured in a top down
approach from there.
If one examines systems that have been put together in this manner over the
last five to ten years they all suffer a number of difficulties:
- Development is slow. Users’ requests for enhancement have to wait for the
next release, which is usually over a year away.
- They are difficult and expensive to maintain. During the life cycle of
the system, probably 90% of development goes into maintenance.
- Major restructuring in the light of five or ten years of hindsight is unthinkable.
- Customisation is arbitrarily done in one or more of the many languages
used to put the system together, typically Fortran or the command language.
- Integration with other systems is nearly impossible.
Smallworld has implemented Magik in order to avoid all these difficulties.
The way this is achieved is by embodying the following features in the language
and its development environment:
- There is but one language for system, application and customisation development.
- Both object orientation and procedural methodologies are supported.
- Development is in an interactive environment.
- The language is expressive and very readable.
- There is an extensive library of standard object classes, methods and procedures.
- The language is built as a platform suitable for delivering commercial
- Applications can be transferred with a minimum of effort between hardware
It is Smallworld’s belief that the presence of all these features is essential
if commercial systems are to be developed, maintained and customised with a
minimum of programmer effort. It is the lack of a viable language with a sufficient
subset of these facilities that has stimulated Smallworld to produce its own
which embodies all of them.
Magik allows programs to be developed in one seamless environment, meaning
that systems programming, applications development, system integration, and
customisation are all written in one environment in the same language. Thus,
end users who wish to customise the system can be confident in the quality
of the tools provided because they are identical to the development tools used
by the core and application system developers. Further, existing systems, such
as most database management systems, can be fully integrated so that to the
user they appear as part of one homogeneous system.
We have already started to use some of the jargon of object orientation, and
in order to understand Magik, it will be necessary for us to try and explain
object orientation and a number of the other terms used to explain it.
Object orientation is not an easy concept to explain, and we are not likely
to fully succeed here, how-ever, it is suffice to say that experienced computer
industry observers are in no doubt as to the power of the technique and indeed
are convinced that within the next few years object orientation will be the
dominant approach to structuring and building large complex systems. The argument
is subtle, but the benefits of it are profound.
Object orientation refers to a way of structur-ing software systems, thus
people who refer to object oriented databases may be misusing the term, as
it is not clear (at least to this writer) what they mean. Putting it at its
most terse, object orientation structures software around the things being
processed, not around the functions being performed. In order to try and get
over some of the flavour of what object oriented programming is all about let
us try and describe some of the common terminology surrounding it.
An object comprises two things, its own state (manifested as a set of instance
variables) which no other part of the system can access directly and a set
of procedures (called methods) which describe its behaviour. Everything about
an object is encapsulated within it and the only way of getting data out of
it, or changing it, or getting it to do something is by sending messages to
it. An object is a rather sophisticated extension of the concept of variable
in other languages. The fact that data is hidden and that the only way of communicating
with an object is via a rigorously defined system of message passing (see below)
means that extremely robust systems can be created.
Every object belongs to a unique class. Some classes are primitive such as
real numbers, integers and text strings. A class is very similar to a type
in normal procedural languages. All objects of a given class will exhibit the
same behaviour, i.e., have the same set of methods. In an object oriented language,
the programmer himself defines new classes in order that he can later make
instances of them. In order to be more precise here, we should say that Magik
is based on “exemplars” in that a new class is defined by having
a special way of defining the first instance of it, i.e. an example of it.
From then on all further instances are cloned from the exemplar.
A method is a procedure, no more, no less. The reason a different term is
used is because of the way in which it is called. In a normal procedural language
if one sees a reference to a procedure, then it refers unambiguously to a particular
piece of code. However a reference to a method in a message (see below) may
well refer to any one of many pieces of code depending on the class of the
object receiving the message. Further, it will not be possible to ascertain
which method will be executed until run time. What this means is that routines
can be written which do not depend on the type of the data thrown at them and
therefore the same code can be reused over and over again in different contexts.
Such code written without a knowledge of what it is to be used for is sometimes
referred to as a software IC.
Quite frequently, different classes might be similar, in that one might exhibit
all the behaviour of another plus some additional behaviour. In other words,
one class may share a number of another classes’ methods, but also has a different
version of some methods and some new methods of its own. In these cases, one
class can be defined to be a sub-class of another. This is another way of exploiting
code reusability. Note that the behaviour of a sub- class is not a subset of
the behaviour of its super-class, because typically it does things in addition
to the superclass. A subclass is a specialization of its superclass in the
same sense that a duck-billed platypus is a specialization of a monotreme which
is a specialization of a mammal which is a special-ization of a vertebrate
which belongs to the animal kingdom. A duck-bill is unique to the platypus,
whereas egg laying behaviour is common to all monotremes, warm bloodedness
is common to all mammals and all vertebrates have a backbone.
Usually, classes are organized in a strict hier-archy, each subclass in the
hierarchy inheriting its behaviour (methods) from its superclass. In Magik,
the concept of multiple inheritance is also supported (equivalent to a hybrid
in our example from the animal kingdom) so that a class may inherit behaviour
from any number of super classes (thus forming a heterarchy). Some objects
are special in that their only function is to supply behaviour to other classes,
it is not possible to create an object instance from them; these are called “mixins”.
The point of the class heterarchy is to try and maximize code reusability.
Message passing in an object oriented language means exactly the same thing
as a procedure call in a procedural language. “Message expression” is
the term used to describe how a message is sent. A message expression comprises
three parts: an object, a message name, and a list of zero or more parameters,
which are themselves objects.
In Magik a message expression would look like:
p3 << point.new(10,20)
Here a new object “p3” is manufactured by taking the object “point”,
sending it the message “new” with the parameter objects 10 and 20
(note: “<<” is the Magik assignment operator).
x3 << p3.x this makes the object "x3" by sending the message "x" to "p3" (thus x3 becomes 10) p3.y<< 50 this could be regarded as changing p3 by sending the message "y<<" to "p3" with parameter 50.
Magik – a Hybrid Language
We have said in the introduction to this paper that Magik is a hybrid language.
This is because whereas object orientation is the preferred method of organizing
large systems (programming in the large), it can be cumbersome and contorted
for writing certain kinds of routines, especially pure functions. Such routines
are usually not very large and are reasonably self contained. It is therefore
preferable to write such parts of the system using a conventional procedural
approach, as is often the case for small programs (programming in the small).
In Magik, object oriented code can be freely mixed with procedural code, and
indeed the message expression has been made to look rather like a procedure
call where the procedure name can be thought of as a concatenation of the object
class with the message.
Interactive Development Environment
At least as important as the power of a language is the nature of the development
environment provided for programmers. Progress with conventional system programming
languages is considerably slowed by having to re-link every time a new piece
of code needs to be tested. If one provides programmers with an interactive
language with powerful tools to explore and discover existing code in the extensive
library of standard objects, then programmer productivity is improved by a
large factor, regardless of the quality of the language itself. In Magik, the
development environment and code browsing facilities are inspired by Smalltalk.
The inheritance relationships for the objects in the Magik environment can
be complex but inheritance diagrams can be generated automatically. Also, the
implementing class of any particular method can be easily identified. The annotated
source code for most of the environment is available online. There are code
browsers and object inspectors; the system can locate methods and variables
given only part of the name. Debugging tools include tracing of calls and access
to global variables. It is possible to fix and continue after an error.
Magik – a Readable Language
Some popular languages, although very powerful, are extremely difficult to
read. Occasionally language constructions are so cryptic that even the original
author has difficulty deciphering the code that he has written, let alone passing
it to anyone else. The same criticism might also be levelled at very low level
languages such as most assemblers. In order that large systems can be maintained,
it is essential that the language syntax is designed so that the majority of
the program-ming population does not get immediate dyslexia. As far as procedural
programming is concerned, Magik has been designed to fit in with the Algol
style of syntax, so that conditionals, procedure calls, expressions and loops
will all appear familiar and readable. The object oriented facilities such
as method definitions and message expressions have been designed to fit in
with the Algol style.
Library of Object Classes, Methods and Procedures
At the heart of the programming environment is a variety of useful classes
that can be used directly or subclassed. These classes include:
- Different kinds of collections for grouping objects (sets, association
tables, ordered lists, stacks and queues and so on). Some of these collections
support relational algebra.
- Multi-dimensional arrays.
- Stream of objects or text, either to or from an external channel such as
a file or to and from buffers.
- A text editor
- Application framework and menus for putting together interactive applications.
This is in turn built on a library of objects that do graphics.
There are also packages of common mathematical and statistical functions.
Magik – a Delivery Platform
Magik is designed with three strategies specifically aimed at delivery platforms.
The first strategy is simply a defensive programming style. The environment
and application framework are designed to encourage this and error-handling
mechanisms in the programming environment that are intended for debugging can
easily be replaced in a delivery version with appropriate error recovery procedures.
A second strategy is to protect application and system code. Online source
code can be removed from the delivery version if preferred and method tables
can be locked so that methods cannot be changed or removed from the sensitive
classes. Global variables can be made constant. If necessary, the compiler
can be removed from the platform and users restricted to menus instead.
A third strategy is to separate text and pictures intended for human interpretation
from other data wherever possible. This is vital for internationalization so
that words and symbols can be translated into different languages.
Appendix – Snippets of Smallworld Magik code
Simple assignments and expressions:
y << x + 1/x x << (-b + sqrt(b*b-4*a*c))/(2*a)
Multiple assignments. Given two objects a and b, both the following examples
swap a and b, regardless of their classes:
(b,a) << (a,b) # Parallel assignment a << b ^<< a # ^<< Boot and becomes
Calling procedures and sending messages:
largest << max(p1, p2, p3) (red,green,blue) << chair.colour.rgb()
if chair.height < min then report('This chair is too small') elif chair.height > max then report('This chair is too big') else report('This chair is just right') endif
member << for mem over membershiplist.elements() loop # statements to be looped over if mem.age < 18 then continue abc endif # more statements if mem.available then leave with mem endif endloop
quadratic << proc(a,b,c) if a = 0 then if b <> 0 then return c/b endif else discriminant << b*b-4*a*c if discriminant < 0 then return else s << sqrt(discriminant) ta << a+a return (-b+s)/ta, (-b-s)/ta endif endif endproc # leaves x1 with 2 and x2 with 3 (x1,x2) << quadratic(1,-5,6) # leaves x1 with 2 and x2 unset (x1,x2) << quadratic(0,2,4) # leaves both x1 and x2 unset (x1,x2) << quadratic(4,1,3)
Returning objects. The normal way to return objects from a procedure or method is
to use the return statement.
# returns three objects return a,b,c # returns a single list of three objects return vec(a,b,c) # Any statement, simple or complex can also return # objects using the form '>>' # "a" ends up being 42 or 99 a << if p > q then >> 42 else >> 99 endif
Iterators. Iterators generate values used in loops. This example uses the
fibonaccinumbers iterator to supply values for f.
# The fibonacci series is the series 0, 1, 1, 2, 3, 5, 8, 13 . . . for f over fibonacci_numbers(10) loop # statements endloop # The iterator "fibonaccinumbers" could be defined as follows: fibonacci_numbers << iter proc(n) if n < 1 then return endif loopbody(0) if n < 2 then return endif loopbody(1) f0 << 0 f1 << 1 for i over range(1,n) loop (f0,f1) << f1,f0+f1 loopbody(f1) endloop endproc
method rectangle.new(width,length) clone.init(width,length) endmethod method rectangle.area >> width*length endmethod
Iterator methods. Iterator methods are very similar to iterators, except they
are defined as part of the behaviour of a particular class.
"Membership.list.elements()" under loops above is an example of the use of an iterator method.
Protected code. The protection code in a protect statement is guaranteed
to be executed, even if an error occurs:
input << text_fileinput.new(filename) protect # code to process the file protection input.close() endprotect