\chapter{Polymorphismus, orthogonale Persistenz und Mobilit"at}



\label{tycoon}

In diesem Kapitel wird ein Einblick in das Tycoon-System gegeben. Der Text

soll Lesern, die das System noch nicht kennen, einen "Uberblick "uber die

Leistungsf"ahigkeit und die Besonderheiten von Tycoon und seiner

Programmiersprache {\sc Tl} verschaffen. Au"serdem soll der Stand des Tycoon-Systems

im Bezug auf aktivit"atsorientierte kommunizierende Informationssysteme

dargestellt werden.



Nach einer Zusammenfassung der Entwicklungsziele und der wichtigsten

Eigenschaften 

wird die Systemarchitektur des persistenten Objektsystems vorgestellt.

Alle Schichten des Systems werden dabei kurz angesprochen. Dann werden die

Eigenschaften und Konzepte der Programmiersprache {\sc Tl} besprochen. 

Die Kommunikationskonzepte der einfachen

Kommunikationsverbindungen, entfernten Prozeduraufrufe und mobilen Threads 

spielen in dieser Arbeit eine gro"se Rolle.

Schlie"slich wird der Leser in das Modul- und Bibliothekenkonzept 

von Tycoon eingef"uhrt. Am Ende dieses Kapitels wird anhand eines Beispiels

untersucht, wie sich die Programmiersprache {\sc Tl} f"ur die Programmierung

einer Daten\-bank\-an\-wen\-dung eignet. Dabei soll der Leser durch vier

Programmst"ucke einen ersten Eindruck von

der Syntax und der Semantik der Sprache erhalten.



\section{Das Tycoon-System}



Das Tycoon-Projekt, das an der Universit"at Hamburg im Fachbereich Informatik

seit 1992 l"auft, soll die Produktivit"at des Programmierers 

bei der Entwicklung datenintensiver Anwendungen erh"ohen und

flexibles Arbeiten in offenen, heterogenen Umgebungen erm"oglichen. 

Um dieses Ziel zu erreichen, wurde an zwei Punkten angesetzt: 

zum einen wurde die 

persistente und polymorphe Programmiersprache {\sc Tl} mit einem Typsystem 

h"oherer

Ordnung entwickelt. Mit dieser Sprache lassen sich bestehende Applikationen 

leicht in neue

Programme integrieren, 

indem ihre Dienste einheitlich in {\sc Tl} beschrieben werden. 

Der zweite Ansatzpunkt war die

Entwicklung einer persistenten Systemumgebung, die {\sc Tl} unterst"utzt 

und bestehende Dienste nahtlos integriert. 

Das entstandene System zeichnet sich durch seine Skalierbarkeit und die 

F"ahigkeit zur Zusammenarbeit mit bestehenden und neuen Anwendungen und 

Diensten aus

\cite{MSS95}.



Die Zusammenarbeit mit anderen Systemen wird durch drei Aspekte

realisiert:



\begin{itemize}



\item Die existierenden Dienste werden zum einen einheitlich "uber polymorphe

Tycoon-Bibliotheken integriert. Auf diese Art und Weise wurden bereits mehrere 

generische Dienste integriert. Beispiele daf"ur sind Fenster-Systeme wie 

OpenWindows oder Datenbanksysteme wie Oracle oder Ingres.



\item Zum zweiten sind die eingef"ugten Dienste innerhalb des Systems nicht von

Diensten zu unterscheiden, die in {\sc Tl}

implementiert wurden. Diese Eigenschaft wird durch die

Typsicherheit und Persistenzabstraktion erreicht. 

Persistenzabstraktion bedeutet, da"s die Lebensdauer eines Objektes

f"ur den Programmierer  nicht sichtbar ist und er alle Objekte in dieser

Hinsicht gleich

behandeln kann. Au"serdem wird dadurch der Unterschied zwischen

der programmiersprachlichen Sicht und der Datenbanksicht auf Informationen 

verkleinert \cite{AtMo95}.

Streng typisierte

Dienste sind in der Benutzung sicherer und viele Programmierfehler k"onnen schon

zur "Ubersetzungszeit entdeckt werden.



\item Der dritte Aspekt zur Unterst"utzung der Zusammenarbeit ist die

Eigenschaft von 

Tycoon, da"s es auch als Subsystem in anderen Systemen eingesetzt werden kann.

So k"onnen

zum Beispiel Teile von Tycoon als Dienstgeber f"ur persistente Speicherung eingesetzt

werden. Au"serdem k"onnen die Tycoon-Applikationen von C-, C++- oder

FORTRAN-Programmen aufgerufen werden.



\end{itemize}



Die Offenheit und Integrationsf"ahigkeiten des Tycoon-Systems werden in der 

Abbildung \ref{tyc-integration} deutlich.



\begin{figure}[t]

\centerline{\epsfbox{bilder/tycinteg.eps}}

\caption{Die Integrationsf"ahigkeiten von Tycoon}

\label{tyc-integration}

\end{figure}





\section{Die Systemarchitektur von Tycoon}



Die Systemarchitektur von Tycoon zeichnet sich durch eine strikte Unterteilung in 

Schichten aus. Die Speicherung, Manipulation, Optimierung, 

Modellierung und Darstellung von Daten

wurde in verschiedene Schichten gelegt. F"ur viele Aufgaben, die eine Schicht im

Gesamtsystem hat, existieren

bereits mehrere alternative Implementierungen, die unterschiedliche

Anforderungen 

unterst"utzen. Die Bausteine der Schichten sind in der

Abbildung \ref{tycarch} zu sehen.



\begin{figure}[t]

\centerline{\epsfbox{bilder/tycarch.eps}}

\caption{"Ubersicht der Schichten im Tycoon-System}

\label{tycarch}

\end{figure}



Im folgenden werden die Schichten des Tycoon-Systems einzeln vorgestellt.



\subsection{Das Tycoon Store Protocol (TSP)}

\label{tsp}

Durch das Speicherprotokoll wird eine einheitliche Schnittstelle zu

verschiedenen persistenten Objektspeichern definiert. Diese Schnittstelle ist

vom Datenmodell des Objektspeichers unabh"angig. Dabei kann die 

Implementierung des Objektspeichers

jederzeit gewechselt werden, oder es k"onnen gleichzeitig mehrere 

Objektspeicher benutzt werden. Nach \cite{MMS95c} soll mit diesem Protokoll ein

Standard f"ur die Schnittstelle zu persistenten Objektspeichern definiert werden. 



In einem Objektspeicher werden  nach \cite{Matt93} die folgenden

Dienste ben"otigt, 

um einer datenintensiven Anwendung gerecht zu werden:



\begin{description}



\item[Automatische Freispeicherverwaltung:] Die Klienten des Objektspeichers

d"urfen ihre Pl"atze im Speicher nicht mehr selbst freigeben. Stattdessen soll

eine

Speicherbereinigung im Objektspeicher alle nicht mehr erreichbaren 

Speicherpl"atze wieder freigeben.



\item[Persistenz:] Objekte m"ussen als persistent gekennzeichnet werden 

k"onnen. Alle Objekte, die "uber das gekennzeichnete Objekt erreicht 

werden k"onnen,

sind dann ebenfalls persistent.



\item[Atomarit"at, Fehlererholung:] Der Objektspeicher mu"s Sicherungspunkte

definieren k"onnen. Alle "Anderungen an einzelnen Objekten, die nach diesem 

Sicherungspunkt

geschehen, m"ussen wieder zur"uckgenommen werden k"onnen.



\item[Portabilit"at:] Alle Daten aus einem Objektspeicher m"ussen in einen

anderen Objektspeicher "ubertragen werden k"onnen.



\item[Synchronisation:] Es m"ussen Synchronisationsmechanismen wie das Sperren

bestimmter Objekte und die Freischaltung gesperrter Objekte angeboten werden, um

den Objektspeicher mehreren Benutzern gleichzeitig zug"anglich zu machen.



\end{description}





Die Klienten greifen "uber das Protokoll auf den Speicher zu. Innerhalb

des Speichers k"onnen neue Objekte angelegt, Objekte ver"andert und gel"oscht

werden. Verschiedene Protokollzust"ande sichern die Persistenz zu. Funktionen

zum Commit oder Rollback sind m"oglich.



"Uber das Protokoll werden unterschiedliche Objektspeicher an das Tycoon-System

angebunden. Die selbstentwickelten Speicher {\em Tymem} und {\em Tysin} binden

Speichersysteme f"ur Einzelbenutzer an. {\em Tymem} benutzt zur Speicherung den

Hauptspeicher, {\em Tysin} wurde f"ur Systeme entwickelt, die keine

virtuellen Speicher besitzen. Au"serdem wurden Schnittstellen zu

ObjectStore und NapierStore implementiert. Diese Speichersysteme sind

mehrbenutzerf"ahig. Innerhalb des Protokolls wurde eine lineare Repr"asentation

der Objektgraphen

 (TXR, Tycoon external data representation) definiert, mit der die Objekte von

einem Speicher in einen anderen "ubertragen werden k"onnen.



Durch die folgenden Punkte kann der Objektspeicher den Anforderungen, die an

ihn gestellt werden, angepa"st

werden:



\begin{itemize}



\item Strategien zur Speicherbereinigung



\item Mechanismen zur Behandlung von Fehlern im Objektspeicher



\item Fehlererholung, Protokollierung und Mechanismen zur persistenten

Speicherung



\item Einzelbenutzer- oder Mehrbenutzerzugriff



\item Protokolle zur Transaktionsbeendung



\item Unterst"utzung der Zugriffssicherheit und Authentizit"at



\end{itemize}



Au"serdem gibt das Protokoll R"uckmeldungen des Objektspeichers an die Klienten

weiter. Sie f"ordern die enge Zusammenarbeit von Dienstnehmern und -gebern.





\subsection{Die Tycoon Virtual Machine (TVM)}



Die virtuelle Tycoon-Maschine definiert einen Befehlssatz, der auf einem

funktionalen Ausf"uhrungsmodell mit Funktionen h"oherer Ordnung basiert.

Alle Befehle sind durch Bytes codiert. Die Ausf"uhrung des Bytecodes

geschieht entweder durch Interpretation der Befehle oder durch eine "Ubersetzung

in Maschinenbefehle. Weil die virtuelle Maschine plattformunabh"angig entworfen

und implemeniert ist, kann der Programmcode innerhalb eines heterogenen

Rechnernetzes von einem Rechner auf einen anderen ohne Neu"ubersetzung 

"ubertragen werden. Zusammen mit der austauschbaren Darstellung der Daten im

Objektspeicher k"onnen so persistente {\em Threads} (Bindungen zwischen Daten

und Programm, das sich in der Ausf"uhrung befindet \cite{MaSc94}) zwischen verschiedenen 

Rechnern wechseln.



Die Skalierbarkeit wird durch die Zulassung von einer beliebigen Anzahl 

Threads unterst"utzt.

F"ur spezielle Aufgaben wie

Flie"skommaberechnungen k"onnen neue Befehle eingef"ugt werden.



Die virtuelle Maschine enth"alt einen portablen Mechanismus, um Bibliotheken,

die in anderen Programmiersprachen entwickelt wurden, einzubinden. "Uber diesen

Mechanismus werden viele Basistypen wie Flie"skommazahlen mit unterschiedlichen

Genauigkeiten oder Datums- und Uhrzeittypen angeboten.



Da in den Befehlen der virtuellen Maschine nicht zwischen statisch und dynamisch

eingebundenen Bibliotheken unterschieden wird, ist die Maschine von

Bindungsmechanismen der einzelnen Implementierungen unabh"angig.

Im Laufzeitsystem werden die Namen der externen Funktionen, die Liste der

Parametertypen und die Bibliotheken, in denen sich die Funktionen befinden,

gehalten. Der Interpretierer durchsucht zuerst eine Liste von Bibliotheken und

Symbolen, die statisch gebunden sind. Wenn das Symbol nicht gefunden wird,

versucht er "uber gemeinsame Bibliotheken auf Betriebssystemebene an das

gesuchte Symbol zu kommen.



Durch die virtuelle Maschine wird der Zugriff auf fl"uchtige Daten in

persistenten Systemen erm"oglicht. Alle Konstruktionen und Destruktionen von

Werten au"serhalb des persistenten Objektspeichers werden dort protokolliert. So

kann zum Beispiel ein Fenster einer grafischen Benutzeroberfl"ache nach dem

Beenden und dem Neustart eines Programms automatisch wieder

ge"offnet werden. \cite{MSS95}



\subsection{Die Tycoon Machine Language (TML)}



F"ur die Zwischensprache im Tycoon-System haben sich die folgenden Anforderungen

ergeben: \cite{Matt93}



\begin{itemize}



\item Sie soll als Zielcode f"ur allgemeine Sprachen h"oherer Ordnung dienen und 

daher weitgehend unabh"angig von speziellen Typaspekten dieser Sprachen sein.



\item Sie wird zur Definition der dynamischen Semantik der Hochsprachen

eingesetzt.

 

\item Sie mu"s die Anwendung von Optimierungstechniken gestatten.



\item Der Zwischensprachencode mu"s f"ur reflektive Programmanalysen und

-trans\-for\-ma\-tionen zur Verf"ugung stehen.



\end{itemize}



Bei einem Anwendungsprogramm, das mit dem Tycoon-System entwickelt wurde,

wird viel Programmcode aus anderen Anwendungen wiederverwendet. Die

Entscheidung, welche bereits "ubersetzten Codefragmente in das Programm

eingebunden werden, wird oft bis zur Laufzeit verschoben. Dadurch kann das 

Programm zur "Ubersetzungszeit nicht effektiv

optimiert werden. An diesem Punkt setzt die Zwischensprache ein, die f"ur eine

einheitliche Darstellung aller Programmkomponenten sorgt \cite{GaMa96a}.



Dazu vereinigt die Zwischensprache im Tycoon-System sowohl die statische 

Analyse zur 

"Ubersetzungszeit als auch die Analyse zur Laufzeit mit der Optimierung. Als untypisierte

Sprache, deren Semantik auf dem $\lambda$-Kalk"ul basiert, wird sie "uber die

TML-Schnittstelle zur Implementierung mehrerer polymorph-typisierter

Hochsprachen verwendet (siehe Abbildung \ref{tycarch}).



Ein "Ubersetzer, der in {\sc Tl} implementiert ist, generiert die

Bytecodesequenzen f"ur die virtuelle Maschine. Durch die Darstellung von TML 

im

Fortsetzungs"ubergabestil wird die Optimierung vereinfacht.



Die Optimierer und der "Ubersetzer von TML nach TVM "uberpr"ufen den Syntaxbaum 

des Programms nicht auf Korrektheit. Diese Aufgabe mu"s die Hochsprache 

erledigen.



\subsection{Die Tycoon-Hochsprachen}

Die Skalierung des Systems wird nach \cite{MSS95} durch verschiedene 

Hochsprachen und eine

feste Schnittstelle zur Zwischensprache unterst"utzt. Um eine weitere

Datenbanksprache {\em L} in das System einzubauen, wird eine angepa"ste

"Ubersetzungsfunktion benutzt, die die statische Semantik von {\em L} 

analysiert. Die dynamische Semantik wird dann durch eine strukturgerichtete 

Abbildung auf TML

"uberpr"uft. Den TML-Kern kann man wiederum durch semantische Primitive, 

die 

f"ur {\em L} ben"otigt werden, erweitern. Bisher wurden zwei Hochsprachen

implementiert: {\sc Tl} und TooL. {\sc Tl} wird in Abschnitt \ref{TL}

ausf"uhrlich beschrieben. Bei {TooL} handelt es sich um eine

objektorientierte Sprache. Sie ist stark an Smalltalk angelehnt und benutzt

statische Typisierung.



Die folgenden drei Aspekte, die f"ur persistente Programmiersprachen wichtig 

sind, sind in den Hochsprachen implementiert.



\begin{description}



\item[Dynamische Typen:] 

\label{DynTypes}

Falls die Typen von Objekten, die zum Beispiel "uber ein Netzwerk "ubertragen

werden, zur "Ubersetzungszeit nicht bekannt sind, k"onnen

sie dynamisch ermittelt werden. Solche dynamischen Werte bestehen aus einem 

Tupel mit

dem Wert $v$ und einer Laufzeitrepr"asentation seines Typs $t$. 

Der Typ kann hier

als Untertyp eines allgemeineren Obertyps oder durch Iteration "uber

Wertattribute und Typattribute eines Tupels gewonnen werden. Es ist au"serdem

m"oglich, das Tupel aus einer Liste typisierter Bindungen zu gewinnen.

Ein Einsatzbeispiel f"ur dynamische Typen sind generische Funktionen,

die ihr Verhalten von Typen abh"angig machen. So kann ein Browser f"ur den

Objektspeicher alle Eintr"age, abh"angig von ihren Typen, anzeigen.



\item["Ubersetzungszeit- und Laufzeitreflexion:] 

F"ur die Implementierung der Sprachen {\sc Tl}

und TooL wurde ein "Ubersetzer, der wiederum in der Spache {\sc Tl} entwickelt

wurde,

eingesetzt. Bei anderen Programmiersprachen wird ein "Ubersetzer als

Werkzeug eingesetzt, 

um ein ablauff"ahiges Programm zu erhalten, das den "Ubersetzer anschlie"send 

nicht mehr

ben"otigt. Im Tycoon-System k"onnen die Programme auf die Funktionalit"at des

"Ubersetzers zur"uckgreifen. Zur "Ubersetzungszeit kann so auf Informationen aus

dem Programmcode zur"uckgegriffen werden. Damit kann zum Beispiel generischer

Code zum Durchlauf von rekursiven Datenstrukturen entwickelt werden.



Bei der Laufzeitreflexion werden vom Programm aus Komponenten des "Ubersetzers

wie Zerteiler oder Codegeneratoren benutzt.



\item[Typisierung externer Bindungen:] 

Die Typen der Werte von externen Laufzeitbibliotheken liegen nur im Quellcode der

Bibliotheken vor. Bei der "Ubersetzung der Bibliotheken gehen die Typen

verloren. Wenn solche Bibliotheken an das System gebunden werden, sind sie nicht

sicher typisiert. Um eine sichere Typisierung zu erreichen, m"ussen bei der

Bindung externer Bibliotheksfunktionen die Typen der Funktionsparameter mit

angegeben werden. Somit liegt die Typisierung in den H"anden des Programmierers. 



\end{description}



\subsection{Erweiterbare Grammatiken}



Die Abbildung von der konkreten auf die abstrakte Syntax des Tycoon-Systems 

ist nicht durch die Sprachdefinition vorgegeben. Durch dieses Konzept k"onnen

die Sprachen leicht erweitert werden.



Erweiterbare Grammatiken sind durch eine Bibliothek von "Ubersetzerwerkzeugen in

der Sprache {\sc Tl} implementiert. Durch die Angabe von Produktionen im

Programmtext k"onnen 

so neue Sprachkonstrukte generiert werden, die dem Benutzer zum Beispiel als

Abfragesprache f"ur selbstentwickelte Datenbankanwendungen 

zur Verf"ugung stehen \cite{MSW95}.



\section {Die Programmiersprache {\sc Tl}}

\label{TL}



Die Programmiersprache {\sc Tl} wird zun"achst an Hand ihrer Eigenschaften

beschrieben. Danach wird auf die Bedeutung von Typen und die M"oglichkeiten der

Subtypisierung eingegangen. Zum Schlu"s werden Typausdr"ucke und --operatoren

vorgestellt.



\subsection{Die Eigenschaften von {\sc Tl}}

\label{TL-Eigenschaften}



Das Modell, mit dem die Sprache {\sc Tl} definiert ist, beruht auf den Konzepten 

von Werten, Ausdr"ucken, Signaturen

und Bindungen. Daher hat {\sc Tl} die folgenden Eigenschaften 

\cite{Matt95,Matt93}:



\begin{description}



\item[{\sc Tl} ist algorithmisch vollst"andig:] 

Zus"atzlich zur

Turing-Vollst"andigkeit bietet {\sc Tl} auch ad"aquate Kontroll- und

Datenabstraktionen, um den hohen Anforderungen der Entwicklung von

Datenbanksystemen gerecht zu werden.



\item[{\sc Tl} ist funktional:] 

Die Semantik von {\sc Tl} basiert auf dem $\lambda$-Kalk"ul.

Jeder {\sc Tl}-Term wird zu einem Wert ausgewertet. Die

zentralen Konzepte von {\sc Tl} sind Funktionsabstraktionen und Funktionsanwendungen.

Funktionsabstraktionen bestehen aus einer Liste von Signaturen, die die Parameter

f"ur die Funktion bilden, und 

einem Ausdruck, der die Berechnungsvorschrift f"ur den Funktionswert darstellt,

und werden zu einem Funktionswert ausgewertet.

Eine Funktionsanwendung definiert einen Funktionswert und eine geordnete Liste

von Bindungen, die sich aus der Funktionssignatur ergeben. Der Funktionsterm

wird in

der Umgebung mit den Funktionsargumenten ausgewertet. Funktionen besitzen als Objekte 

{\em erster Klasse} eine Identit"at und k"onnen zum Beispiel in Datenbanken

eingef"ugt werden.



\item[{\sc Tl} ist imperativ:]

{\sc Tl} ist keine rein funktionale Sprache, da Objektidentifikatoren, destruktive

Zuweisungen, Seiteneffekte in Funktionen, Schleifen und Ausnahmen erlaubt sind.

Dadurch wird die zustandsbasierte transaktionale Datenbankprogrammierung

unterst"utzt.

 

\item[{\sc Tl} ist strikt evaluativ:] 

Wenn Terme als Funktionsargumente "ubergeben werden, so werden diese Terme vor

der Applikation ausgewertet. {\sc Tl} benutzt als Parameter\-"uber\-ga\-be die

Call-By-Value-Strategie. Ausnahmen von dieser Regel bilden nur die bedingten

Verzweigungen {\k if}, {\k orif}, {\k andif} und {\k case}. Sie werden 

nach der Strategie der

faulen Auswertung (engl.: lazy evaluation) evaluiert.



\item[{\sc Tl} ist deterministisch:] 

Unterterme von zusammengesetzten Termen werden in einer festen Reihenfolge

ausgewertet. Es gibt auch keinen expliziten nil-Wert, der zu unerwartetem

Programmverhalten f"uhren kann. Mit der strikten und deterministischem 

Auswertung von Termen wird die

Spezifikation und Verifikation der Programme vereinfacht.



\item[{\sc Tl} besitzt eine Referenzsemantik:]

Mit Hilfe von statischen {\sl let}-Bindungen, dynamischen Funktionsargumenten 

und

destruktiven Zuweisungen von strukturierten Werten k"onnen mehrere Zugriffspfade

auf den gleichen Wert eingerichtet werden. Mit dieser Referenzsemantik wird

au"serdem die

Manipulation von rekursiven Datenstrukturen vereinfacht.



\item[{\sc Tl} hat statische Sichtbarkeitsregeln:]

Durch die statischen und blockstrukturierten Sichtbarkeitsregeln kann die

Deklaration eines Bezeichners mit einer textuellen Analyse des Quellprogramms

gefunden werden. Mit Hilfe von Funktionsabstraktionen k"onnen Werte gekapselt

werden.



\item[{\sc Tl} ist nebenl"aufig:]

Mit dem Thread-Konzept, das in Abschnitt \ref{threads} vorgestellt wird, 

k"onnen mehrere Programme gleichzeitig denselben 

Objektspeicher benutzen. Threads werden durch Synchronisationsmechanismen

unterst"utzt. Sie sind Objekte erster Klasse.



\item[{\sc Tl} ist persistent:]

Die Lebensdauer eines {\sc Tl}-Threads ist nicht an die Lebensdauer des

Prozesses vom Betriebssystem gekoppelt. Die Threads sind persistent und mobil.

Alle Werte sind automatisch persistent. Die

Auswertung eines {\sc Tl}-Terms kann sich auf 

Zustandsvariablen in einem pr"a-populierten Objektspeicher beziehen und

Seiteneffekte im Objektspeicher erzeugen. {\sc Tl}-Programmierer k"onnen von

der 

Speicherverwaltung vollst"andig abstrahieren. Speicherbereinigung,

Daten"ubertragung auf Plattenspeicher und Zugriffskontrolle bei mehreren

Benutzern werden vom Objektspeicher automatisch erledigt. Au"serdem sind

Commit- 

und Rollback-Aufrufe  m"oglich. 

Die Persistenz der Objekte ist nicht von ihrem Typ abh"angig, so da"s man

von orthogonaler Persistenz \cite{AtBu87} sprechen kann.



\item[{\sc Tl} ist strikt typisiert:]

{\sc Tl} definiert Konsistenzbedingungen an Terme, die durch Typ\-regeln

formalisiert 

sind. Die Einhaltung dieser Konsistenzbedingungen wird f"ur beliebige Terme

durch eine statische Programmanalyse erzwungen. Durch explizite Sprachkonstrukte wie

dynamische Typen oder Module kann dieser Konsistenztest jedoch kontolliert auf

wohldefinierte Zeitpunkte zur Programmlaufzeit verschoben werden.



\item[{\sc Tl} ist explizit typisiert:]

In {\sc Tl} k"onnen Wert- und Typbezeichner jederzeit mit Typinformationen

versehen 

werden. Bezeichner, die dynamischen oder rekursiven Bindungen unterliegen

(zum Beispiel Funktionsparameter) m"ussen mit diesen Typinformationen versehen

werden, bei anderen Bezeichnern werden sie automatisch vom "Ubersetzer

inferiert. 



\item[{\sc Tl} ist typvollst"andig:] 

Die Wertkonstruktoren von {\sc Tl} k"onnen einheitlich auf Werte aller

Typen angewandt 

werden. Die Komponenten von Tupeln, Records oder Vektoren k"onnen

"uber ihren Namen oder 

ihre Stelle selektiert werden.



\item[{\sc Tl} ist polymorph:]

Konzepte zur existentiellen und universellen Typquantifizierung und eine 

induktiv definierte Subtypbeziehung

auf Typen in {\sc Tl} erm"oglichen parametrischen Polymorphismus, Subtyppolymorphismus

und begrenzten parametrischen Polymorphismus (engl.: bounded parametric polymorphism).

Benutzerdefinierte Typoperatoren

h"oherer Ordnung erm"oglichen neuartige Formen der polymorphen Programmierung.



\item[{\sc Tl} ist interaktiv:]

Die Implementierung von {\sc Tl}-Programmen erlaubt einen flie"senden "Ubergang zwischen

stapelorientierter "Ubersetzung, Bindung und Ausf"uhrung von Modulen und

Bibliotheken und der direkten Auswertung interaktiv eingegebener {\sc

  Tl}-Terme. 

So kann man {\sc Tl} sowohl zur Datenbank\-programmierung als auch f"ur 

ad-hoc-Daten\-bank\-anfragen verwenden.

 

\item[{\sc Tl} ist modular:]

Anwendungsprogramme sind in separat entwickelte Module und Bibliotheken

gegliedert, deren Strukturen und Importabh"angigkeiten explizit in {\sc Tl}

dargestellt werden.



\end{description}



\subsection{Die Bedeutung von Typen und Subtypen in {\sc Tl}}



{\sc Tl} ist nach Abschnitt \ref{TL-Eigenschaften} eine explizit typisierte Sprache.

Die Zusammenh"ange zwischen Typkompatibilit"at und Subtypbeziehungen geben eine Antwort

auf die Frage, wann ein Ausdruck $a$ den Typ $A$ besitzt.



Dabei ist es zun"achst wichtig zu wissen, da"s eine Typsignatur f"ur einen Wert

nur eine partielle Spezifikation darstellt. Der  Wert hat mindestens den Typ $A$, er

kann aber auch pr"aziser sein. Die Ordnung von Typen wird durch die

Subtypbeziehung beschrieben. Dabei ist ein Typ $B$ pr"aziser als ein Typ $A$,

wenn $B$ ein Subtyp von $A$ ist. 



Das Konzept der Subtypisierung leistet einen wesentlichen Beitrag zur

Erweiterbarkeit von Softwaresystemen. Bestehende Komponenten k"onnen f"ur

Objekte mit pr"aziseren Typen unver"andert "ubernommen werden. Au"serdem

wird durch Subtypbeziehungen die Entwicklung generischer Algorithmen, die von

irrelevanten Typdetails abstrahieren, vereinfacht. Die Subsumptionsregel sagt

aus, da"s jeder Wert eines Typs auch ein Wert des Supertyps ist. Der 

allgemeinste Typ ist {\k Ok}.



Bei Tupeltypen besteht folgende Subtypbeziehung: Ein Tupeltyp $A$ ist Subtyp

eines Tupeltyps $B$ wenn er mindestens die gleichen Komponenten an der gleichen 

Stelle besitzt. Es k"onnen weitere Komponenten hinzukommen. Au"serdem m"ussen

die Komponententypen von $A$ Subtypen der Komponententypen von $B$ sein. Wenn

weiterhin Variablennamen f"ur die Komponenten benutzt werden, so m"ussen die

Variablen des Supertyps die gleichen Namen wie im Subtyp haben. Die

Subtypisierung beruht also auf struktureller Kompatibilt"at.



Bei Funktionstypen f"uhrt die Subtypisierung zur Kontravarianzregel: ein

Funktionstyp $A$ mit Parametertypen $S$ und einem Ergebnistyp $E$ ist Subtyp

eines Funktionstyps $A'$, wenn seine Parametertypen $S'$ Supertypen von $S$

sind und sein Ergebnistyp $E'$ ein Subtyp von $E$ ist. Damit k"onnen Funktionen

typsicher spezialisiert werden.



\subsection{Typen von Typen}



In {\sc Tl} k"onnen die Typen wie Wertausdr"ucke und Wertvariablen verwendet werden.

Dadurch wird eine gr"o"sere sprachliche M"achtigkeit und Flexibilit"at

erreicht. Mit diesem Ansatz werden Sprachkonzepte wie generische Datentypen,

abstrakte Datentypen und polymorphe Funktionen erfa"st.



Wenn Typvariablen in Signaturen statisch gebunden werden, erh"alt man eine

existentielle Quantifizierung von Typausdr"ucken. Mit dieser

Konstruktion k"onnen 

abstrakte Datentypen leicht in eigenen Modulen implementiert werden, die erst 

bei der Benutzung

durch den Aufruf mit einem konkreten Typ versehen werden \cite{MaSc93a}.



Die universelle Quantifizierung von Typausdr"ucken

f"uhrt zu polymorphen Funktionen, Typoperatoren oder

dynamischen Typen, 

je nach Kontext. Sie kann zum Beispiel dazu eingesetzt werden, allgemeine

Sortierfunktionen zu implementieren, die Werte aller Typen sortieren kann. Sie

erh"alt beim Aufruf den Typ der zu sortierenden Werte und eine

Vergleichsfunktion.



In polymorphen Funktionen werden Typvariablen als Parameter benutzt, um den

Ergebnistyp abh"angig davon zu berechnen. Wenn die Typvariable durch die Angabe

eines speziellen Supertyps (spezieller als {\k Ok}) eingeschr"ankt

wird, spricht man 

vom Prinzip des begrenzten parametrischen Polymorphismus.



Typoperatoren sind Funktionen, die Typen auf Typen abbilden. Sie erlauben eine

Anpassung der Sprache an verschiedene Konzepte zur Datenstrukturierung und

-modellierung. Ein Beispiel f"ur den Einsatz von Typoperatoren ist die

Implementierung von Nullwerten.



Zu dynamischen Typen siehe Abschnitt \ref{DynTypes}.



\section{Kommunikation zwischen Tycoon-Systemen}



Die Kommunikationsbibliothek im Tycoon-System ist in Schichten implementiert.

Dabei bestehen die unteren Schichten aus einfachen Kan"alen f"ur

Byte-Str"ome, auf denen typ\-un\-sich\-ere Kan"ale f"ur {\sc Tl}-Objekte aufbauen.

Diese werden wiederum zu typsicheren entfernten Prozeduraufrufen (Remote 

Procedure Calls, RPC) erweitert. Diese

Prozeduraufrufe beruhen auf einfacher Adressierung, wobei in der n"achsten

Schicht mobilit"atsorientierte RPCs realisiert sind. Damit wird die Adressierung

der Dienste f"ur den Benutzer transparent. Aus der Orthogonalit"at der Typen im

Tycoon-System k"onnen mit den mobilit"atsorientierten RPCs auch ganze Programme

in Ausf"uhrung (Threads) "ubertragen werden.



\subsection{Typunsichere Kommunikation}



Die Grundlage f"ur Kommunikationsdienste im Tycoon-System bildet ein

typunsicherer Dienst, mit dem beliebige Tycoon-Objekte aus dem Objektspeicher 

im Internet "uber\-tra\-gen

werden k"onnen. F"ur diesen Dienst, bei dem der Sender ein Objekt an eine

Netzwerkadresse abschickt und der Empf"anger bereits auf die Daten warten mu"s,

wird die linearisierte Objektdarstellung TXR aus Abschnitt \ref{tsp} benutzt.

Es 

ist auch eine bidirektionale "Ubertragung m"oglich, bei der der Sender solange

blockiert, bis die Antwort vom Empf"anger wieder eintrifft. Dieser Dienst ist

deshalb typunsicher, weil er sich eng an die Socket-Programmierung in C

anlehnt und keine Typinformationen aus {\sc Tl} benutzen kann.



\subsection{Entfernte Prozeduraufrufe}

\label{tycoon-komm}

Um zu typsicheren Diensten zu gelangen, m"ussen die Operationen von oben in die

Programmiersprache {\sc Tl} eingebettet werden. Um eine {\sc Tl}-Funktion als

entfernten Dienst zur Verf"ugung zu stellen, mu"s sie in ein entsprechendes

Tupel eingebunden werden. Auch auf Seite der Klienten mu"s die Funktion f"ur

die 

Benutzung eines entfernten Dienstes nicht neu "ubersetzt werden. Die

Kommunikationsunterprogramme werden dabei durch Manipulation der Laufzeitwerte

zusammengesetzt. 



Ein Dispatcher verwaltet Funktionen, die als entfernte Dienste zur

Verf"ugung stehen sollen. Wenn man einen Dienst eintr"agt, wird sein

dynamischer Typ mit angegeben. Auf der Seite des Klienten mu"s f"ur einen

entfernten Aufruf die Funktion mit ihrem Typ an einen Namen gebunden werden. 

Bei der

Bindung wird --- f"ur den Benutzer transparent --- im Dispatcher nachgeschaut, ob ein

entsprechender Funktionstyp vorhanden ist. Wenn die Anfrage erfolgreich ist,

wird der untypisierte Klient nachtr"aglich typisiert. Wenn die Anfrage keine

"Ubereinstimmung ergibt, wird ein Fehler erzeugt.



Der n"achste Schritt zu einer komfortablen Client/Server-Umgebung ist ein

hierarchisches Adressierungsschema. Mit ihm k"onnen die angebotenen Dienste

auch 

ihre Netzwerkadresse, die f"ur die typsichere Kommunikation zwingend notwendig

ist, wechseln. Eine "ubergeordnete Adressierungschicht teilt jetzt die Adressen

dynamisch zu. In dieser Schicht m"ussen zwei Aufgaben getrennt voneinander

behandelt werden: die Dienstauswahl und der entfernte Aufruf.



Der entfernte Aufruf wird mittels zweier Hilfsprogramme durchgef"uhrt: Der

Mapping Daemon (Mad) verwaltet die Kommunikationskan"ale f"ur die

verschiedenen Dienste auf einem Host-Rechner. Wenn der Mad keinen

passenden Dienst findet, wird die Anfrage an die n"achsth"ohere Instanz im

Netzwerk weitergegeben, den Tycoon LAN Daemon (TyLANd). Er durchsucht alle

anderen Mads im Netzwerk und gibt das Ergebnis der Suche an den anfragenden Mad

zur"uck, der den anfragenden Klienten informiert. Die Suche nach migrierten

Diensten funktioniert auch in einem entfernten LAN (Local Area Network), das

"uber ein WAN (Wide Area Network) mit dem

Klienten verbunden ist, weil nur der Ausgangspunkt f"ur

die Suche ausschlaggebend ist. Um alle denkbaren F"alle abzudecken, kann

jedem Dienst eine eigene Suchfunktion zugeordnet werden. Diese Funktion wird im

Dispatcher eingetragen.



Die Dienstauswahl, die dem entfernten Aufruf vorausgeht, beruht prinzipiell auf

dem gleichen Mechanismus wie die Bindung typsicherer Dienste. Zus"atzlich wird

der {\sl bind}-Funktion ein Parameter f"ur die Suchtiefe mitgegeben. Damit kann

bestimmt werden, ob die Suche auf den Host, das LAN oder gr"o"sere Regionen

des 

Internet beschr"ankt werden soll. Au"serdem kann die Suche durch die Angabe von

Attributen und Pr"adikaten f"ur die Attribute geleitet werden.



\subsection{Mobile Threads}

\label{threads}

Unter Multi-Threading versteht man die M"oglichkeit, in einem Speicherbereich

mehrere nebenl"aufige Programmausf"uhrungen zu halten. Sie unterscheiden sich

von Betriebs\-sys\-tem\-prozessen durch den gemeinsamen Speicherbereich und einen

geringeren Ressourcenverbrauch. Sie werden daher auch Leichtgewichtprozesse

genannt.



Im Tycoon-System sind Threads orthogonal in das sprachliche Gef"uge von {\sc Tl}

integriert. Dadurch k"onnen sie ohne syntaktische Besonderheiten wie alle

anderen Werte behandelt werden. Man kann sie an Funktionen "ubergeben und sie 

von ihnen

zur"uckbekommen, sie k"onnen auf Identit"at verglichen werden, in komplexe

Datentypen wie Tupel oder Arrays eingef"ugt werden oder Konstanten und

Variablen zugewiesen werden.



Threads werden im Programmablauf mit einer vorgegeben Funktion gestartet,

k"onnen gel"oscht werden, sich auch selbst l"oschen oder mit anderen Threads

synchronisieren. Bei der Synchronisation geben die Threads Werte an den

wartenden Thread zur"uck. Wenn ein Thread auf einen anderen Thread wartet, 

wird seine

Ausf"uhrung bis zum Eintreffen des Ergebnisses blockiert.



Mit diesem Konzept k"onnen zum Beispiel leicht Programme erstellt werden,

die im Hintergrund verharren und bei bestimmten Anl"assen aktiviert werden.



Durch die Einbettung in ein persistentes Objektsystem braucht sich der

Programmierer oder Benutzer "uber die Dauerhaftigkeit seiner Anwendungen keine

Gedanken zu machen. Wenn der Objektspeicher gesichert wird, werden auch alle

Threads in ihm gesichert. 

Zusammen mit der Kommunikation von Tycoon-Systemen, die im Abschnitt

\ref{tycoon-komm} n"aher beschrieben ist, kommt man zu mobilen Threads, die

sich f"ur den Benutzer nicht von station"aren Threads unterscheiden.

Weil sich Threads in Tycoon reflexiv auf sich selbst beziehen k"onnen,

k"onnen sie sich, wie im

Agentenparadigma \cite{whi194} beschrieben, auch selbst verschicken. Mit der erweiterbaren

Grammatik wurden daf"ur Befehle wie {\k migrate to\/} \dots {\k do\/}

realisiert.



Intern werden die Threads durch Objekte im Objektspeicher dargestellt. Dadurch

sind sie von einer bestimmten Hardware-Umgebung abgekoppelt. Als untypisierte

TSP-Objekte gleichen sie allen anderen {\sc Tl}-Werten und k"onnen damit

unterschiedslos linearisiert, in Netzwerken "ubertragen und als RPC-Argumente

akzeptiert werden (siehe Abschnitt \ref{tycoon-komm}). Damit erh"alt man mobile

Threads. Bei der Entwicklung von Threads wurden  m"oglichst wenige 

Ob\-jekt\-re\-ferenzen 

benutzt. Damit wird die Mobilit"at unterst"utzt, weil weniger 

Objekte "ubertragen werden m"ussen. \cite{mathi96}



\section {Module und Bibliotheken in Tycoon}



Um die Skalierbarkeit von Programmen zu unterst"utzen, sind viele Dienste in

Bibliotheken ausgelagert, die bei Bedarf wieder importiert werden k"onnen

\cite{MMM93}. In den Bibliotheken werden Module und Schnittstellen

zusammengefa"st,

die die geforderte Funktionalit"at liefern. Sie m"ussen in der Reihenfolge

angegeben werden, in der sie sich aufeinander beziehen. Wenn ein Modul $b$ auf

einem Modul $a$ aufbaut, so mu"s $a$ in der Bibliothek vor $b$

importiert werden. 

Bibliotheken k"onnen andere Bibliotheken, Schnittstellen und Module enthalten

\cite{MMS94}. Alle in einer Bibliothek deklarierten Komponenten werden

exportiert und k"onnen vom Benutzer interaktiv aufgerufen werden.



Um ein Programm modular zu zerlegen, mu"s zun"achst eine

Hauptbibliothek erstellt 

werden. Sie wird anschlie"send "ubersetzt. Dadurch sind die Abh"angigkeiten

zwischen den Modulen und Schnittstellen sichtbar und f"ur die weitere

"Ubersetzung nutzbar. Dann kann das Hauptmodul "ubersetzt werden. Dabei werden

alle Abh"angigkeiten aufgel"ost und die Module, die nach der

letzten "Ubersetzung ge"andert wurden, neu "ubersetzt. Mit {\sl import\/}-Befehlen von der

interaktiven Benutzereingabe werden die exportierten Funktionen und Werte in 

die interaktive Benutzerumgebung

geladen und k"onnen vom Benutzer ausgef"uhrt werden.



In Schnittstellen werden die Signaturen der Werte und Typen eines Moduls, die

nach au"sen sichtbar gemacht werden sollen, definiert. Im Modul befinden sich

dann die Implementierungen zu diesen Signaturen. Man kann Module

oder Schnittstellen, die bei der Definition benutzt werden, aus anderen

Modulen oder Schnittstellen importieren.



\section{Ein  praktisches Beispiel}



Atkinson und Buneman arbeiten in ihrem Artikel "`Types and Persistence

in Database Programming Languages"' \cite{AtBu87} notwendige

Eigenschaften von Datenbankprogrammiersprachen heraus. Sie diskutieren die

Prinzipien {\em Persistenz}, {\em Typvollst"andigkeit} und {\em

Ausdruckst"arke} am Beispiel verschiedener Programmiersprachen. In

jeder Sprache 

sollen verschiedene Programme entwickelt werden, die eine kleine

Datenbankanwendung darstellen. In diesen Programmen geht es um die Datenbank 

einer

Produktionsfirma, die alle von der Firma verwendeten Bauteile verwalten soll. Es gibt

Grundbauteile, die von anderen Herstellern eingekauft werden, und

zusammengesetzte Bauteile, die aus den Grundbauteilen oder anderen

zusammengesetzten Bauteilen produziert werden. F"ur jedes Bauteil wird der Name und 

das Gewicht

gespeichert, bei eingekauften Bauteilen der Einkaufspreis und der Lieferant, bei

zusammengesetzten Bauteilen die Baukosten und alle ben"otigten Bauteile.



Die folgenden vier Aufgaben wurden mit allen Sprachen angegangen:



\begin{enumerate}



\item Beschreibung der Datenbank.



In diesem Teil soll untersucht werden, welche zus"atzlichen

Informationen wie zum Beispiel Bedingungen an die Daten in das Schema

der Datenbank gesteckt werden k"onnen.



\item Ausgabe der Namen, Preise und Gewichte aller eingekauften Bauteile, die mehr

als 100 DM kosten.



Mit dieser Aufgabe wird gepr"uft, ob sich einfache Anfragen an eine

Datenbank auch einfach ausdr"ucken lassen.



\item Ausgabe des Gesamtgewichts und der Gesamtkosten eines zusammengesetzten

Bauteils.



Diese anspruchsvolle Anfrage erfordert Rekursionen, die in

einfachen Abfragesprachen wie SQL nicht m"oglich sind.



\item Aufnahme eines neuen Produktionsprozesses in die Datenbank, d.h.

  Aufnahme eines neuen zusammengesetzten Bauteils, da"s aus anderen Bauteilen

  hergestellt wird.



Mit dieser Anfrage wird die Implementierung von Integrit"atsbedingungen gemessen.



\end{enumerate}



Auf den Vergleich der Sprachen gehe ich hier nicht weiter ein, es soll lediglich

anhand von Programmbeispielen gezeigt werden, wie diese Aufgabe mit dem

Tycoon-System gel"ost werden kann. Dabei wird der Programmcode

ausf"uhrlich beschrieben, damit der Leser einen Eindruck von der

Sprache {\sc Tl} bekommt.



\begin{figure}[tb]

\begin{sl}



\begin{tabbing}

\hspace*{1cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=

\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\kill



(* Task 1: Describe the database *)\\

\\

\>{\k Let Rec} Part $<:${\k Ok} =\\

\>\>{\k Tuple}\\

\>\>\>name :String\\

\>\>\>{\k case} basePart {\k with}\\

\>\>\>\>cost :Int\\

\>\>\>\>mass :Real\\

\>\>\>\>supplier :Supplier\\

\>\>\>{\k case} compositePart {\k with}\\

\>\>\>\>assemblyCost :Int\\

\>\>\>\>massIncrement :Real\\

\>\>\>\>madeFrom :iter.T(Component)\\

\>\>{\k end}\\

\\

\>{\k and} Component $<:${\k Ok} = {\k Tuple} quantity :Int subpart :Part 

{\k end}\\

\\

\>{\k and} Supplier $<:${\k Ok} = {\k Tuple} name :String {\k end};\\

\>\\

\>{\k let var} parts = iter.new(:Part);\\

\\

\>{\k let var} supplier = iter.new(:Supplier);\\

\\

\>{\k let var} components = iter.new(:Component);\\

\end{tabbing}

\end{sl}



\caption{{\sc Tl}-Code zur Beschreibung der Datenbank}

\label{tl-task1}

\end{figure}





Mit dem Programmfragment aus Abbildung \ref{tl-task1} wird ein 

Datenbankschema definiert und die Datenbank

aus {\sl parts, supplier\/} und {\sl components\/} erstellt. Alle W"orter im

Programmtext, die mit Gro"sbuchstaben beginnen, beziehen sich auf

Typen. So werden in den ersten drei Abschnitten Datentypen f"ur Bauteile,

Komponenten und Hersteller definiert. Diese Typen sind mit {\k Rec\/}

und {\k and\/} gekennzeichnet, so da"s sie parallel gebunden werden. 

Alle Datentypen bestehen aus einem Tupeltyp, der verschiedene

Attribute enth"alt. Der erste Typ ist ein varianter Tupel, der zum

gemeinsamen Attribut {\sl name\/} vom Basistyp String (der Typ zum Attribut

wird syntaktisch mit dem Doppelpunkt eingeleitet) die Varianten Grundbauteil oder 

zusammengesetztes Bauteil enth"alt. Ein Grundbauteil hat die

Attribute {\sl cost, mass\/} und {\sl supplier\/}. Ein zusammengesetztes Bauteil 

enth"alt die

Attribute {\sl assemblyCost, massIncrement\/} und {\sl madeFrom\/} als Iteration 

von Komponenten, aus denen es zusammengesetzt wird.



Die Komponenten und Hersteller werden durch einfache Tupeltypen beschrieben.

Ein Tupel f"ur Komponenten enth"alt Attribute

f"ur die Menge und das Bauteil, das eingebaut wird.

Der Hersteller wird bisher nur durch seinen Namen beschrieben.



Wegen der Rekursion der Datentypen m"ussen sie als Subtypen des allgemeinsten 

Datentyps {\k Ok\/} gekennzeichnet werden. Dies  geschieht durch den Zusatz

$<:${\k Ok\/} \cite{Matt93}.



Die Datenbank selbst besteht aus Iterationen von Variablen der eben

definierten Typen. Dazu mu"s zuvor das Modul {\sl iter\/} aus der

Standardbibliothek in die interaktive Benutzerumgebung importiert werden.

Die Funktionen k"onnen dann mit der Punktnotation {\sl

iter.new(\dots)\/} angesprochen werden. Mit dem Schl"usselwort {\k

var\/} werden sie als ver"anderbar gekennzeichnet. Das Semikolon am

Ende schlie"st die interaktive Eingabe ab und "ubergibt den

Programmtext an den "Ubersetzer.



\begin{figure}[tb]

\begin{sl}

\begin{tabbing}

\hspace*{1cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=

\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}

\=\hspace{0.5cm}\=\kill



(* Task 2: Print all the names, cost, an mass of all imported parts,\\

\>\>\>\>\> that cost more than 100 Dollars *)\\

\\

\>{\k let} printBaseNameCostMass (part :Part) :{\k Ok} =\\

\>\>print.string (''Part '' $<>$ part!basePart.name $<>$ ''  ''\\

\>\>\>$<>$ ''costs '' $<>$ fmt.int(part!basePart.cost) $<>$ ''  ''\\

\>\>\>$<>$ ''and its mass is '' $<>$ fmt.real(part!basePart.mass)

$<>$ ''$\setminus$ n'')\\

\\

\>{\k let} expensiveBaseParts = iter.select(parts {\k fun}(p :Part) :Bool\\

\>\>p?basePart {\k andif} p!basePart.cost $>$ 100)\\

\\

\>iter.forEach(expensiveBaseParts {\k fun}(p :Part)\\

\>\>printBaseNameCostMass(p));\\

\end{tabbing}

\end{sl}



\caption{Programmcode zur zweiten Aufgabe mit {\sc Tl}}

\label{tl-2task}

\end{figure}



Im Programm aus Abbildung \ref{tl-2task} steht der Programmcode f"ur die 

zweite Aufgabe.

Diese Aufgabe ist mit der m"achtigen Iterations-Bibliothek des Tycoon-Systems

leicht zu l"osen. Da alle Bauteile der Datenbank in einer Iteration vorliegen,

kann man mit {\sl iter.select(\dots)\/} leicht eine neue Iteration erstellen,

in der sich nur ausgew"ahlte Elemente der Ausgangsiteration befinden. In diesem

Fall werden sie mit einer booleschen Funktion, die {\k true} zur"uckgibt, falls

das Bauteil ein Grundbauteil ist und sein Preis mehr als 100 DM betr"agt,

bestimmt. Auf die Elemente des Tupels wird mit dem Variantentest {\sl

parts?basePart\/} und der Variantenprojektion {\sl parts!basePart\/}

zugegriffen. Wenn alle gesuchten Elemente in der neue Iteration enthalten sind,

m"ussen sie ausgegeben werden.

Dazu wird die Funktion {\sl iter.forEach(\dots)\/} verwendet, die f"ur jedes

Element der Iteration die Ausgabefunktion aufruft.



Die Ausgabefunktion {\sl printBaseNameCostMass(\dots)\/} kapselt die Ausgabe der

Attribute 

des Grundbauteils. Hier wird die Funktion {\sl print.string(\dots)\/} an einen neuen

Namen 

mit einem Parameter gebunden. Der Funktion {\sl print.string(\dots)\/} wird

wiederum 

eine Zeichenkette "ubergeben, die sich aus mehreren Strings, die mit $<>$

verkettet werden, zusammensetzt. Variablen, die nicht den Typ

{\sl String\/} haben, m"ussen mit Formatierungsfunktionen in diesen Typ

"uberf"uhrt werden. 



\begin{figure}

\begin{sl}

\begin{tabbing}

\hspace*{1cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=

\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}

\=\hspace{0.5cm}\=\kill



(* Task 3: Print the total mass and total cost of a composite part *)\\

\\

\>{\k Let} MassCost = {\k Tuple} mass :Real cost :Int {\k end};\\

\\

\>{\k let rec} addMassCost (quantity :Int part :Part) :MassCost =\\

\>\>{\k begin}\\

\>\>\>{\k case of} part\\

\>\>\>{\k when} basePart {\k with} bp {\k then}\\

\>\>\>\>{\k let} mass = real.mul(bp.mass real.val(quantity))\\

\>\>\>\>{\k let} cost = bp.cost $*$ quantity\\

\>\>\>\>{\k tuple} mass cost {\k end}\\

\>\>\>{\k when} compositePart {\k with} cp {\k then}\\

\>\>\>\>{\k let var} mass = 0.0\\

\>\>\>\>{\k let var} cost = 0\\

\>\>\>\>iter.forEach(cp.madeFrom\\

\>\>\>\>\>{\k fun}(sPart: Component)\\

\>\>\>\>\>\>{\k begin}\\

\>\>\>\>\>\>\>{\k let} mc = addMassCost(sPart.quantity sPart.subpart)\\

\>\>\>\>\>\>\>mass := real.add(mass real.mul(real.val(quantity) mc.mass))\\

\>\>\>\>\>\>\>cost := cost $+$ {mc.cost $*$ quantity}\\

\>\>\>\>\>\>{\k end})\\

\>\>\>\>mass := real.add(mass real.mul(real.val(quantity) cp.massIncrement))\\

\>\>\>\>cost := cost $+$ {cp.assemblyCost $*$ quantity}\\

\>\>\>\>{\k tuple} mass cost {\k end}\\

\>\>\>{\k end}\\

\>\>{\k end};\\

\\

\>{\k let} notFound = {\k exception} ''Element not found$\setminus$n'' {\k

end};\\ 

\\

\>{\k let} findPart(name :String parts :iter.T(Part)) :Part =\\

\>\>{\k try}\\

\>\>\>iter.head(iter.select(parts {\k fun}(p :Part) string.equal(name p.name)))\\

\>\>{\k else}\\

\>\>\>{\k raise} notFound {\k end}\\

\>\>{\k end};\\

\end{tabbing}

\end{sl}



\caption{Das {\sc Tl}-Programm zur Untersuchung eines zusammengesetzten Bauteils}

\label{tl-3task}

\end{figure}



In Abbildung \ref{tl-3task} steht der Programmtext, um die Kosten und das

Gewicht eines zusammengesetzen Bauteils zu berechnen. Kernfunktion ist die

rekursive Funktion

{\sl addMassCost(\dots)\/}, die mit der H"aufigkeit des Bauteils und dem Bauteil

selbst aufgerufen

wird und ein Tupel mit Preis und Gewicht zur"uckgibt. Falls es sich bei dem

"ubergebenen Bauteil um ein Grundbauteil handelt, ist die Rekursion beendet. 

Der Preis und das Gewicht wird mit der Anzahl verrechnet und als Tupel

zur"uckgegeben. F"ur ein zusammengesetztes Bauteil werden lokale Z"ahler f"ur das

Gewicht und den Preis angelegt und die Iteration von Bauteilen durchlaufen. Mit

der Funktion {\sl iter.forEach(\dots)\/} wird f"ur jedes Element der Iteration

eine namenlose Funktion aufgerufen. In dieser Funktion wird der

Rekursionsschritt ausgef"uhrt und die Z"ahler f"ur Preis und Gewicht werden

aktualisiert. Am Ende werden die Baukosten und die Gewichtsver"anderung, die durch

die Montage entsteht, dazu addiert. Das Tupel mit dem Preis und dem Gewicht an

die aufrufende Umgebung zur"uckgegeben.



Damit der Benutzer das Bauteil nicht nur "uber die Stelle in der Iteration

ansprechen kann, wird es vom Benutzer mit der Funktion {\sl findPart(\dots)\/} 

anhand seines Namens

gesucht. Zuerst wird eine globale Ausnahme definiert, die aktiviert wird, falls

das Bauteil nicht in der Datenbank gefunden wird. Die Ausnahme ist global, weil die

Funktion {\sl findPart(\dots)\/} auch im Programm zur vierten Aufgabe verwendet wird.

Innerhalb von {\sl findPart(\dots)\/} wird mit der Funktion {\sl iter.select(\dots)\/}

eine lokale Iteration erzeugt, die nur die Elemente aus {\sl parts\/} enth"alt,

f"ur die die Funktion {\sl string.equal(\dots)\/} den Wert {\sl true\/}

zur"uckgibt. Aus dieser Iteration wird das erste Element an die aufrufende

Umgebung zur"uckgegeben. Falls die Funktion {\sl iter.head(\dots)\/} auf eine

leere Iteration zugreift, wird eine Ausnahme ausgel"ost, die mit dem {\k

try}-Konstrukt abgefangen wird. Wenn der Versuch ein Element auszuw"ahlen

fehlschl"agt, wird die vom Programmierer bestimmte Ausnahme {\sl notFound\/}

ausgel"ost, die den Benutzer dar"uber informiert, da"s das gesuchte Element

nicht in der Datenbank enthalten ist.



Durch Rekursion und komfortable Iterationsfunktionen, die alle Bauteile f"ur ein

zusammengesetztes Bauteil erfassen, l"a"st sich auch die dritte Aufgabe leicht

bearbeiten. Weil aber in {\sc Tl} keine "Uberladung zugelassen ist, m"ussen die

mathematischen Operatoren f"ur Real-Werte umst"andlich ausformuliert werden.

Jedoch k"onnen diese Formulierungen mit einer eigenen erweiterten Grammatik 

abgek"urzt werden.



\begin{figure}

\begin{sl}



\begin{tabbing}

\hspace*{1cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=

\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}\=\hspace{0.5cm}

\=\hspace{0.5cm}\=\kill



(* Task 4: Record a new manufacturing step in the database, that is, how a

new\\ 

\> composite part is manufactured from subparts. *)\\

\\

\>{\k Let} NameQuantity = {\k Tuple} name :String quantity :Int {\k end}\\

\>\\

\>{\k let} addCompositePart(part :Part\\

\>\>componentNames :iter.T(NameQuantity)\\

\>\>var parts :iter.T(Part)) :{\k Ok} =\\

\>\>\>{\k begin}\\

\>\>\>\>{\k let var} thisComponents = iter.new(:Component)\\

\>\>\>\>iter.forEach(componentNames\\

\>\>\>\>\>{\k fun}(component :NameQuantity)\\

\>\>\>\>\>\>{\k begin}\\

\>\>\>\>\>\>\>{\k try}\\

\>\>\>\>\>\>\>\>{\k let} subPart = findPart(component.name parts)\\

\>\>\>\>\>\>\>\>thisComponents := iter.cons(tuple\\

\>\>\>\>\>\>\>\>\>\>component.quantity\\

\>\>\>\>\>\>\>\>\>\>subPart\\

\>\>\>\>\>\>\>\>\>end\\

\>\>\>\>\>\>\>\>\>thisComponents)\\

\>\>\>\>\>\>\>{\k when} notFound {\k then} \\

\>\>\>\>\>\>\>\>print.string (''Part '' $<>$ component.name\\

\>\>\>\>\>\>\>\>\>$<>$ '' is not in the database$\setminus$n'')\\

\>\>\>\>\>\>\>{\k end}\\

\>\>\>\>\>\>{\k end})\\

\>\>\>\> parts := iter.cons({\k tuple\_case}\\

\>\>\>\>\> compositePart {\k of} Part {\k with}\\

\>\>\>\>\>\> part.name\\

\>\>\>\>\>\> part!compositePart.assemblyCost\\

\>\>\>\>\>\> part!compositePart.massIncrement\\

\>\>\>\>\>\> thisComponents\\

\>\>\>\>\> {\k end}\\

\>\>\>\>\> parts)\\

\>\>\> end;\\

\end{tabbing}

\end{sl}



\caption{Das {\sc Tl}-Programm zum Einf"ugen eines zusammengesetzen Bauteils}

\label{tl-4task}

\end{figure}



Im Programm zur letzten Aufgabe in Abbildung \ref{tl-4task} wird ein neues

zusammengesetztes Bauteil in die Datenbank eingef"ugt. Vor dem Aufruf der

Funktion m"ussen

alle verwendeten Bauteile mit ihrem Namen und ihren Anzahlen in einer Iteration

festgelegt werden. Au"serdem erwartet die Funktion {\sl

addCompositePart(\dots)\/} das einzuf"ugende Bauteil ohne Komponentenliste und

die Datenbank aller Bauteile. Sie gibt den Wert {\k ok} zur"uck, erzeugt also nur

Seiteneffekte auf der Datenbank. Bevor das neue Bauteil eingef"ugt werden kann,

mu"s die Komponenteniteration aus der Iteration "uber Namen und Mengen erzeugt

werden. An dieser Stelle werden auch die Integrit"atsbedingungen der Datenbank 

"uberpr"uft. Alle angegebenen Bauteile m"ussen schon

in der Datenbank enthalten sein. Wenn ein Bauteil fehlt, wird das neue Bauteil nicht

eingef"ugt und eine Ausnahme erzeugt. Die Liste mit Namen wird wieder mit {\sl

iter.forEach(\dots)\/} durchlaufen. Dabei wird f"ur jedes Element versucht, es

in der Datenbank zu finden. Bei solchen unsicheren

Funktionen wird das {\k try}-Konstrukt verwendet. Falls in der {\sl findPart(\dots)\/}-Funktion eine

Ausnahme erzeugt wird, wird sie mit dem {\k when}-Fall abgefangen und es

wird eine Meldung an den Benutzer ausgegeben. Wenn {\sl

findPart(\dots)\/} ein Bauteil zur"uckgibt, wird es mit {\sl iter.cons(\dots)\/} in

die Komponenteniteration eingef"ugt. Am Ende wird die Komponenteniteration in

das Tupel mit dem aktuellen Bauteil "ubernommen und dieses Bauteil in die Datenbank

eingef"ugt.



Die "Uberpr"ufung der Integrit"at der Datenbank kostet an dieser Stelle einige

Zeilen Programmtext, l"a"st sich aber mit der Ausnahmebehandlung leicht

programmieren.



Insgesamt kann man an den vier Beispielen sehen, da"s mit dem iter-Modul ein

m"achtiges Werkzeug f"ur die Behandlung von Massendaten geschaffen wurde.

