• Keine Ergebnisse gefunden

Das Datentypsystem von SDL-92 basiert auf dem Kalkül der initialen Algebra [Z.100-92, An-nex C]. Jeder Datentyp definiert eine Sorte. Diese Sorte wird durch eine Menge von Termen be-schrieben. Zwischen den Termen wird mit Hilfe von Axiomen eine Äquivalenzrelation eingeführt (die Termäquivalenz). Die einzelnen Äquivalenzklassen sind dann die Werte des Da-tentyps.

Obwohl dieses Kalkül ausdrucksstark genug ist, um die in Programmiersprachen üblichen Datentypen zu beschreiben, weist es eine Reihe von Mängeln auf, die zur Ersetzung dieses Mo-dells durch ein anderes in SDL-2000 führten:

1. Das Datentypsystem ist für Anwender schwer zu verstehen. Wird ein Anwender mit einem System von Operatoren und Axiomen konfrontiert, erschließt sich ihm oft nicht die Bedeu-tung des Datentyps.

2. Das System ist schwer zu implementieren. Selbst die einfache Frage der Feststellung einer Äquivalenz von Termen ist, im Allgemeinen, äquivalent zum Halteproblem [Sch02]. Wenn-gleich für viele Datentypdefinitionen eine effiziente Implementierung denkbar ist, wurde jedoch für SDL nie ein Werkzeug entwickelt, dass diese effiziente Implementierung findet.

3. Das Datentypsystem basiert auf einer Wertesemantik. Der Objektbegriff lässt sich mit die-sem Kalkül nicht nachbilden. Beispielsweise initialisieren die folgenden Anweisungen zwei Variablen mit einem Strukturwert, wobei die Struktur zwei Felder besitzt:

task a := (. 3, 4 .);

task b := (. 3, 4 .);

In diesem Beispiel ergibt sich aus der Definition der initialen Algebra zwingend, dass die Va-riablen a und b den gleichen Wert haben, da beide mit dem identisch selben Term belegt wur-den. In einer objekt-orienterten Sprache, in der die Variablen a und b Objektreferenzen sind, erwartet man jedoch, dass nach diesen Zuweisungen die Variablen auf verschiedene Objekte verweisen; man versteht den Ausdruck auf der rechten Seite als Konstruktion eines Objekts.

Da Objekte nicht nur durch Zustand und Verhalten, sondern auch durch ihre Identität be-schrieben sind, ist der Begriff der Wertgleichheit allein unzureichend für ein objektorientier-tes Datentypsystem.

Während der Entwicklung von SDL-2000 spielte die Rückwärtskompatibilität zu früheren Sprachversionen eine wesentliche Rolle: Auch wenn beim Einsatz von SDL-2000 Anpassungen alter Spezifikationen an neue Konstrukte nötig sind, so sollte doch in vielen Fällen eine einfa-che, möglichst automatisch durchführbare Anpassung möglich sein6.

Aus diesem Grund wurde ein Datentypsystem, welches ausschließlich auf Objekten basiert (wie etwa das von Java) für SDL verworfen: Zu viele existierende Spezifikationen vertrauen auf die Wertesemantik; diese sollten möglichst ihre Semantik behalten.

Deshalb wurde für SDL zunächst ein zwei-teiliges Datentypsystem entworfen. Mit der Ver-allgemeinerung von Prozessen und Blöcken zu Agenten und der Einführung von Schnittstellen kam dann noch ein dritter Teil hinzu. Ein Datentyp in SDL ist entweder ein

• Wertetyp (value type),

• Objekttyp (object type) oder ein

• Pid-Typ (Pid type).

Wenn ein Wertetyp als Variablentyp oder Parametertyp verwendet wird, erfolgen Zuweisungen an diese Variablen oder Parameter als Wertzuweisungen. Der Wert der Variablen ist also nach der Zuweisung eine Kopie.

6. Der Autor dieser Arbeit hat in Appendix III von SDL-2000 ein Verfahren zur systematischen Konvertierung von SDL-92-Spezifikationen zu SDL-2000 vorgeschlagen.

Bei Variablen oder Parametern von Objekttypen erfolgen Zuweisungen per Referenz. Nach der Zuweisung verweist die Variable auf dasselbe Objekt wie der zugewiesene Ausdruck. Än-derungen dieses Objekts über eine Referenz wirken sich dann auch auf alle anderen Referenzen auf das gleiche Objekt aus.

Eine Abweichung von dieser Referenzsemantik findet sich bei Austausch von Signalen zwi-schen Agenten. Hier wird beim Versenden des Signals eine Kopie aller versendeten Signalpa-rameter erzeugt, sofern sich der Empfänger nicht im gleichen Prozess wie der Sender befindet.

Damit können Agenten in verschiedenen Prozessen nicht die gleichen Objekte referenzieren.

Diese Abweichung erlaubt effiziente Implementierungen von Objekttypen, da bei Zugriff auf ein Objekt stets davon ausgegangen werden kann, dass sich dieses Objekt lokal in dem Prozess befindet.

Mit der Einführung von Objekttypen einher ging die Aufnahme von polymorphen Variablen und von Methoden. Methoden erlauben die Kapselung des Objektzustands: Eine Methode stellt die Programmierschnittstelle eines Objekts nach außen zur Verfügung. Der Objektzustand kann damit zum von außen nicht zugänglichen Implementierungsdetail deklariert und lediglich inner-halb von Methoden manipuliert werden.

Für Wertetypen muss der Typ des Ausdrucks in einer Zuweisung gleich dem Typ der Variab-len sein. Für Objekttypen kann der Typ des Ausdrucks auch eine Spezialisierung des VariabVariab-len- Variablen-typs sein. In diesem Fall unterscheidet sich nach der Zuweisung der statische Typ der Variablen vom dynamischen Typ des referenzierten Objekts.

Unter Umständen ist es erforderlich, bei einer polymorphen Variable auf Eigenschaften der Spezialisierung zuzugreifen. Dies ist in SDL durch zwei Konstrukte möglich, nämlich:

1. Virtuelle Methoden: Bei einer virtuellen Methode wird die Methodendefinition auf Grund des dynamischen Typs der Methodenparameter ausgewählt. Die Methoden werden als

„spät“ gebunden – die zu rufende Methode kann erst im Moment des Rufs bestimmt wer-den.

2. Zuweisungsversuch (assignment attempt): Dieses in C++ als „type cast“ bekannte Kon-strukt erlaubt die Umwandlung einer Referenz auf einen Basistyp in eine Referenz auf eine Spezialisierung. Anstatt dafür ein neues Syntaxkonstrukt einzuführen, wurde die Semantik von Zuweisungen erweitert: Ist der Typ des Ausdrucks in einer Zuweisung Basistyp des Typs der Variablen, so ist diese Zuweisung nach der statischen Semantik richtig. Dynamisch wird überprüft, ob der aktuelle Typ des Ausdrucks eine Spezialisierung des Variablentyps ist. In diesem Fall ist die Zuweisung erfolgreich. Anderenfalls wird der Wert null an die Variable zugewiesen; der Zuweisungsversuch ist dann gescheitert.

Beispiel 8. Diese Konstellationen sollen an einem Beispiel erläutert werden. Gegeben seien drei Typen

object type T1{

literals A;

}

object type T2 inherits T1{

literals B;

}

object type T3 inherits T2{

literals C;

}

Da bei einer Spezialisierung eines Typs die Spezialisierung alle Eigenschaften des Basistyps erbt, enthält der Typ T1 das Literal A, der Typ T2 die Literale A und B und der Typ T3 die Literale A, B und C. Damit werden nun folgende Variablendefinitionen und Zuweisungen möglich:

dcl t1 T1, t2 T2;

task t1 := C; /* Polymorphe Zuweisung von <<type T3>> C */

task t2 := t1; /* Zuweisungsversuch, der dynamische Typ ist T3: t2 wird <<type T3>> C */

task t1 := A; /* Zuweisung von <<type T1>> A*/

task t2 := t1; /* Zuweisungversuch; der dynamische Typ ist T1: t2 wird null */

Neben Objekt- und Werttypen gibt es in SDL Pid-Typen. Eine Pid-Variable enthält eine Refe-renz auf einen Agenten (oder den Wert null, wenn kein Agent referenziert ist, insbesondere als Initialwert einer Pid-Variablen). Pid-Variablen müssen nicht unbedingt mit dem Typ Pid defi-niert werden, der Referenzen auf beliebige Agenten repräsentiert. Statt dessen können auch Schnittstellen als Datentypen verwendet werden – jede Schnittstellendefinition impliziert einen Pid-Typ. In einer Agentendefinition kann definiert werden, dass ein Agent A eine bestimmte Schnittstelle S unterstützt. Pid-Variablen des Typs S können dann Refererenzen auf den Agen-ten A aufnehmen.

3.4.1 Vordefinierte Datentypen

Eine Reihe von Datentypen ist in SDL vordefiniert. Alle dieser Typen sind Wertetypen, darun-ter7

• der Typ Boolean,

• die Zahlentypen (Integer, Real),

• Typen für Zeichen und Zeichenketten (Character, Charstring) und

• Typen für Zeit und Zeitspannen (Time, Duration).

Eine Reihe von Typen sind mit Kontextparametern behaftet, um sie als Containertypen für an-dere Datentypen einsetzen zu können, darunter die Typen

String (zur Repräsentation einer Folge von Werten eines Typs),

Array (zur Indizierung eines Typs mit einem anderen),

Vector (zur Indizierung eines Typs durch Elemente eines Intervalls ganzer Zahlen) und

Powerset (zur Repräsentation von Mengen aus Elementen eines Typs).

3.4.2 Nutzerdefinierte Datentypen

Auf Basis der vordefinierten Datentypen können anwendungsspezifische neue Datentypen de-finiert werden. Dazu gibt es drei Typkonstruktoren:

• Literaltypen, deren mögliche Werte durch Aufzählung aller Elemente angegeben werden,

• Strukturtypen, die durch Bildung des Kreuzprodukts aus bestehenden Typen entstehen und

• Choice-Typen, die durch Vereinigung bestehender Typen entstehen.

Die genauen syntaktischen Regeln für jeden dieser Typkonstruktoren werden in Abschnitt 6.5 vorgestellt.

3.4.3 Auflösung von Operatornamen

In SDL werden in der Regel Bezeichner mit ihrer Definition auf Grund hierarchischer Sichtbar-keitsregeln aufgelöst: Ein Name eines Signals in einem inneren Agenten verdeckt ein gleichna-miges Signal eines äußeren Agenten. Wenn eine Definition verdeckt ist, kann auf sie durch Qualifizierung zugegriffen werden. Außerhalb ihres Sichtbarkeitsbereichs kann eine Definition nicht referenziert werden.

Obige Sichtbarkeitsregeln funktionieren bei Datentypen aus folgenden Gründen nicht:

• Die Namen von Literalen und Operatoren sind innerhalb des Datentyps deklariert, sind also ohne weiteres an der Stelle ihrer Verwendung gar nicht sichtbar.

7. Die vollständige Liste aller Datentypen findet man in Annex D von [Z.100-00].

• Viele Operatoren haben gleiche Namen (beispielsweise der Operator „+“), so dass eine Operatorverwendung oft mehrdeutig ist.

Beispielsweise ist es nicht möglich, für den Ausdruck „2+4“ einen Typ anzugeben, da die Lite-rale vom Typ Integer, Real oder Duration sein können; der Additionsoperator hat dann einen entsprechenden Typ.

Aus diesen Gründen wird in SDL ein Verfahren der kontextabhängigen Auflösung von Opera-tornamen und Literalnamen verwendet (resolution by context).

Dieses Verfahren besteht aus folgenden Elementen:

• Ist ein Datentyp an einer Stelle der Spezifikation sichtbar, so sind auch alle seine Literale und Operationen dort sichtbar.

• Für eine Aktion (Zuweisung, Timer-Start, Signalversendung) werden alle in dieser Aktion auftretenden Operatoren ermittelt, und für jeden dieser Operatoren alle sichtbaren Definitio-nen.

• Für jede dieser Definitionen wird überprüft, ob sie typrichtig ist. Falls alle Definitionen typ-falsch sind, ist der Ausdruck typ-falsch.

• Unter den typrichtigen Operatoren werden diejenigen ausgewählt, die die kleinste Zahl von polymorphen Typverwendungen aufweisen.

• Gibt es mehrere solcher Operatoren, ist der Ausdruck mehrdeutig und damit falsch; gibt es genau eine Interpretation, ist der Ausdruck richtig.

Betrachtet man beispielsweise die Definitionen

dcl a Integer;

task a := a + 1;

so wird für den Bezeichner a zunächst festgestellt, dass es sich um eine Variable vom Typ Integer handelt. Das Literal 1 kann die Typen Integer, Real oder Duration haben. Für den Ope-rator gibt es folgende Interpretationen:

"+" ( Integer, Integer) -> Integer;

"+" ( Real, Real) -> Real;

"+" ( Duration, Duration) -> Duration;

"+" ( Time, Duration) ->Time;

"+" ( Duration, Time) ->Time;

Von diesen Interpretationen erlaubt nur die Version

task a := <<type Integer>>“+“ (a, <<type Integer>> 1);

eine typrichtige Interpretation. Diese ergibt dann auch die Semantik des Ausdrucks.

Wie bereits erwähnt, kann durch die statische Auflösung einer Operation nicht immer ermit-telt werden, welche Operation gerufen wird: Ergibt die kontextabhängige Auflösung eine virtu-elle Methode, so erfolgt die tatsächliche Bindung einer konkreten Methodendefinition erst während der Abarbeitung des Ausdrucks.