Modula-2
Modula-2 | |
---|---|
Paradigmen: | imperativ, strukturiert, modular |
Erscheinungsjahr: | 1978 |
Designer: | Niklaus Wirth |
Entwickler: | Niklaus Wirth |
Beeinflusst von: | Pascal |
Beeinflusste: | Lua, Oberon, Seed7, Modula-2+, Modula-3 |
Modula-2 ist eine 1978 entstandene Weiterentwicklung der Programmiersprache Pascal und wurde wie diese von Niklaus Wirth entwickelt. Hauptkennzeichen von Modula-2 sind die Sprachmerkmale zur Modularisierung von Programmen. Modula-2 selbst diente später als Vorlage für die Programmiersprache Oberon.
Entstehung
Wirth hatte 1977/78 am Forschungszentrum Palo Alto Research Institute von Xerox die zukunftsweisende Architektur der Alto-Workstations kennengelernt, die bereits über Maus, Grafikbildschirm und Fenstertechnik verfügten. Programmiert wurde der Alto in der Pascal-ähnlichen Programmiersprache Mesa. Nach seiner Rückkehr an die ETH Zürich begann Wirth mit seiner Gruppe die Eigenentwicklung einer solchen Workstation, der später so genannten Lilith, wobei Hardware und Software im Zusammenhang entwickelt wurden.
Standard-Pascal, das als Sprache für den Programmierunterricht entwickelt worden war, eignete sich nicht für die Programmierung eines Betriebssystems für die Lilith, und zwar vor allem aus zwei Gründen:
- es fehlen Vorrichtungen für die nebenläufige Programmierung
- die Aufteilung eines großen Programms in zahlreiche Module mit sauber definierten Schnittstellen nach den Lehren der Softwaretechnik ist nicht möglich.
Die neue Sprache, die den Namen „Modula“ erhielt, enthielt gegenüber Pascal deshalb (neben etlichen Änderungen in der Syntax) zwei neue Konzepte:
- Prozeduren
NEWPROCESS
,TRANSFER
undIOTRANSFER
zur Behandlung nebenläufiger Prozesse (threads) im Sinne eines kooperativen Multitaskings und - das
MODULE
als Einheit für die separate Übersetzung von Programmteilen.
Modula wurde außerhalb der ETHZ erst in der Version Modula-2 bekannt. Die klare Trennung von Definition und Implementierung in getrennten Dateien (in der Regel mit Extension DEF bzw. MOD) war richtungsweisend und wurde von späteren Programmiersprachen zwar kopiert, aber in ihrer Klarheit nicht erreicht. Modula-2 hatte später von Wirth unabhängige Nachfolger wie Modula-2 plus und Modula-3. Seit 1996 gibt es eine internationale Norm ISO/IEC 10514-1 für Modula-2.
Eigenschaften
Da Modula-2 eine Fortentwicklung von Pascal ist, genügt es, auf die wesentlichen Unterschiede zu dieser Sprache einzugehen.
Module
Die prominenteste Neuerung in Modula-2 sind die Module als Vorrichtung für das modulare Programmieren nach den Vorstellungen der Softwaretechnik, zuerst geäußert von David Parnas. Auch das Hauptprogramm heißt deswegen MODULE
statt PROGRAM
wie in Pascal. Alle separat vom Hauptprogramm übersetzten Teile müssen in zwei Dateien aufgespaltet werden: Ein DEFINITION MODULE
enthält nur die Beschreibung der Schnittstelle des Moduls, das heißt: es listet die Konstanten, Typen, Variablen und Prozeduren auf, die für andere Module zur Verfügung gestellt („exportiert“) werden sollen. Ein getrenntes IMPLEMENTATION MODULE
gibt dann die Implementierung an.
Es ist im Sinne der strikten Modularisierung folgerichtig, dass Konzepte wie die Ein-/Ausgabe und mathematische Funktionen, die in Pascal zum normalen Sprachumfang gehörten, in der Sprache Modula-2 nicht enthalten sind. Sie müssen im Bedarfsfall aus dafür vorgesehenen Modulen (in der Regel InOut
für Ein-/Ausgabe und MathLib
für die mathematischen Funktionen) importiert werden.
Datentypen
Die Lilith sollte eine Wortbreite von 16 bit bekommen. Ganze Zahlen hätten somit einen Bereich von -32.768 bis +32.767 gehabt, was Wirth als zu große Einschränkung empfand. Zusätzlich zum Datentyp INTEGER
bekam Modula-2 daher einen Datentyp CARDINAL
für die nicht-negativen Zahlen zwischen 0 und 65.535. Gemischte Ausdrücke, die sowohl INTEGER
- als auch CARDINAL
-Teilausdrücke enthalten, waren verboten. Deshalb gibt es allgemeine Möglichkeiten, Typen zu verwandeln:
- Typkonversionsfunktionen
VAL(
Typ,Ausdruck)
rechnen einen Ausdruck so um, dass er zu dem neuen Typ gehört, während - Typtransferfunktionen („type casts“, bei Wirth: „type cheats“) der Form Typ
(
Ausdruck)
ein Bitmuster unverändert lassen und lediglich für den Compiler den Datentyp verändern.
Beispielsweise ergibt VAL(CARDINAL,-1)
eine Fehlermeldung, während CARDINAL(-1) = 65.535
gilt.
Eine Innovation gegenüber Pascal stellt auch der Datentyp PROCEDURE
dar, mit dem eine Schwäche von Pascal behoben werden sollte: In Pascal war es möglich, einer Prozedur eine Funktion als Argument zu übergeben, gekennzeichnet durch das Schlüsselwort FUNCTION
. Dabei konnte jedoch nicht überprüft werden, ob die später aktuell übergebene Funktion in Anzahl und Typ ihrer Parameter überhaupt passend war. Deklariert man jedoch in Modula-2 beispielshalber
TYPE myFunction = PROCEDURE (INTEGER): REAL;
so kann beim Aufruf einer Prozedur (das Schlüsselwort FUNCTION
gibt es in Modula-2 nicht)
PROCEDURE myProcedure (f: myFunction; n: INTEGER): REAL;
der Compiler bei jedem Aufruf von myProcedure
feststellen, ob die aktuell für f
übergebene Funktion den richtigen Typ hat. Da Prozeduren damit ganz normale Datentypen sind, ist es auch möglich, sie in anderen Datenstrukturen wie etwa ARRAY
s und RECORD
s einzubauen.
Kontrollstrukturen
Zur Vermeidung zahlreicher BEGIN
− END
-Klammern wird in Modula-2 die IF
- und WHILE
-Anweisung jeweils mit einem END
abgeschlossen. Das von Pascal vertraute GOTO
gibt es nicht, dafür aber ein LOOP
– EXIT
-Konstrukt.
Das Pseudomodul SYSTEM
Als Sprache für die Betriebssystemprogrammierung musste Modula-2 über Vorrichtungen verfügen, auf Details der zugrundeliegenden Maschine zuzugreifen. Dafür gab es ein eigenes Modul SYSTEM
, aus dem sich die Datentypen WORD
für ein unspezifisches Speicherwort und ADDRESS
für eine Speicheradresse importieren ließen, ebenso wie eine Funktion ADR
zur Ermittlung der Speicheradresse eines Konstrukts und TSIZE
zur Ermittlung der Speichergröße für einen bestimmten Datentyp. Hinzu kommen die bereits erwähnten Funktionen für die nebenläufige Programmierung.
SYSTEM
heißt Pseudomodul, weil es dazu weder Definitions- noch Implementierungsteil gibt, sondern alle Kenntnis über dieses Modul direkt in den Compiler eingebaut ist.
Entwicklung
Es gibt zwei Dialekte von Modula-2. Einerseits PIM, die von Niklaus Wirth entwickelten und im Standardwerk "Programmieren in Modula-2" definierten Varianten. Entsprechend den Auflagen des Buches gibt es die zweite, dritte und vierte Variante von PIM. Mit jeder Auflage wurde die Sprache leicht verändert. Der zweite Dialekt ist ISO, die von einem internationalen Komitee (unter dem Dach der International Organization for Standardization) erarbeitete Variante.
- PIM2 (1983): Expliziter EXPORT in Definitionsmodulen.
- PIM3 (1985): Kein expliziter Export in Definitionsmodulen mehr nötig.
- PIM4 (1989): Konkretisierung des Verhaltens des MOD-Operators, wenn die Operanden negativ sind.
- ISO (1996): Der Anspruch bei der Entwicklung von ISO Modula-2 war, die Mehrdeutigkeiten von PIM Modula-2 aufzulösen. Außerdem wurden der Sprache die Datentypen COMPLEX und LONGCOMPLEX, Ausnahmen (Exceptions), die Modultermination (FINALLY-Klausel) und eine umfangreiche Standardbibliothek für Ein- und Ausgabe hinzugefügt – neben einer Reihe von kleineren Änderungen.[1]
Implementierungen
Modula-2 erreichte in den späten 1980er Jahren eine verhältnismäßig große Popularität, insbesondere in der Version von Jensen und Partners International (JPI), die einen 10-Fenster-Editor in ihrer Entwicklungsumgebung für MS-DOS und einen sehr schnellen Compiler mit gut optimiertem Objektcode auf den Markt brachten. Spätere Versionen davon hießen TopSpeed Modula-2; in die Entwicklungsumgebung wurden auch C und C++ aufgenommen.
Aktuelle Modula-2-Compiler:
- Megamax Modula-2 – ATARI ST(e) (M68K)
- Hänisch Modula-2 – ATARI ST/TT (M68K)
- Aglet Modula-2 – AmigaOS (PPC)
- GNU Modula-2 – GNU compiler collection (GCC)
- Objective Modula-2 – Cocoa und GNUstep support
- MOCKA Modula-2 Compiler System – BSD und Linux für x86
- XDS Modula-2 – Windows und Linux
Kritik
Wirth selbst[2] listet im Zusammenhang mit der Entwicklung von Oberon folgende Probleme von Modula-2 auf:
Qualifizierende Importe
Die empfohlene Methode zur Verwendung von Bestandteilen fremder Module ist
IMPORT M;
Dadurch werden alle von M
exportierten Bezeichner durch sogenannte qualifizierte Bezeichner, etwa M.A
, M.B
verfügbar. Alternativ dazu kennt Modula-2 den qualifizierenden Import
FROM M IMPORT A,B;
Bei diesem Import sind die Bezeichner dann einfach in der Form A
bzw. B
verfügbar; die Herkunft aus dem Modul M
ist an der Verwendungsstelle dann nicht mehr direkt sichtbar. Dadurch können Programmierer Irrtümer begehen. In Oberon gibt es den qualifizierenden Import deshalb nicht mehr.
Export von Aufzählungstypen
Enthält ein Modul den exportierten Typ
TYPE Ampel = (rot, gelb, gruen);
so bezieht sich nach der Logik der Sprache ein Import dieses Typs nur auf den Typnamen (Ampel
), in Wirklichkeit werden aber die Namen rot
, gelb
und gruen
mitimportiert, was im Falle von Bezeichnerkonflikten an der Verwendungsstelle den Programmierer verwirren kann. In Oberon gibt es Aufzählungstypen deshalb gar nicht mehr, weil man sonst auch ihren Export nicht verhindern könnte.
Datentyp CARDINAL
Vorzeichenlose Arithmetik funktioniert ganz anders als vorzeichenbehaftete Arithmetik; deshalb sind Ausdrücke, in denen vorzeichenlose und vorzeichenbehaftete Werte gleichzeitig vorkommen, problematisch. Modula-2 hat deshalb solche Mischungen grundsätzlich verboten, was bei den Programmierern auf Protest stieß, weil die Programme durch die Verwendung von Typkonversionsfunktionen unnötig verkompliziert wurden. Darüber hinaus entstehen Unsymmetrien in der Behandlung bestimmter Programmstrukturen und bei der mathematischen Modulo-Funktion (MOD
).
Typtransferfunktionen und andere low level-Vorrichtungen
Mit den Typtransferfunktionen (type casts) wird es möglich, Eigenschaften der zugrundeliegenden Maschine zu entdecken, die dem Programmierer einer höheren Programmiersprache eigentlich verborgen bleiben sollten, wie etwa die Endianness, das heißt die Frage, in welcher Ordnung die einzelnen Bits eines Maschinenworts abgespeichert sind. Je nachdem ist nämlich
BITSET(65520) = {4 .. 15}
oder
BITSET(65520) = {0 .. 11}
für eine 16-Bit-Architektur.
Literatur
- Niklaus Wirth: Programming in Modula-2. 4. Auflage. Springer, Berlin u. a. 1988, ISBN 0-387-50150-9.