Exceptions In Sprache Lava
 
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern

Siehe auch ExceptionsDiskussion, ExceptionsAlsArchitekturBestandteil, EinGutesExceptionBeispiel, ExceptionsConsideredHarmful, CheckedExceptionsConsideredHarmful.

Aus Sicht von SpracheLava ist ein Exception-Handling-Konzept nicht nur sinnvoll, sondern geradezu unentbehrlich, da wir in SpracheLava generell anstreben, das versehentliche Arbeiten mit undefinierten Variablen-Werten durch statische oder Laufzeit-Checks zu verhindern, denn die Aufklärung solcher Fehler ist oft außerordentlich mühsam und zeitraubend:

Es kommt häufig vor, dass eine Methode einer Klasse nicht die normalerweise erwarteten Ergebnisse (Output-Parameter, Return-Value) liefern kann. Wenn in diesem Fall das Laufzeit-System nicht verhindert, dass der Aufrufer diese Rückgabewerte (soweit sie von der Methode überhaupt schon vor dem Zeitpunkt der Exception gesetzt worden sind!) dennoch unbesehen benutzt, werden oft schwer aufzuklärende Folgefehler auftreten.

Die Behauptung, es käme häufig vor, dass eine Methode/Funktion nicht die normalerweise erwarteten Ergebnisse liefern kann, ist für Sprachen, die das Sprachmittel "Exceptions" nicht kennen, falsch. "Normalerweise nicht erwartete Ergebnisse" gibt es bei in diesen Sprachen formulierten Methoden/Funktionen nicht. -- vgl

Christian Morgenstern, "Die unmögliche Tatsache", letzte Strophe:

Und er kommt zu dem Ergebnis:
"Nur ein Traum war das Erlebnis.
Weil", so schließt er messerscharf,
"nicht sein ''kann'', was nicht sein ''darf''."

Natürlich gibt es in jeder Programmiersprache Situationen, wo eine Funktion nicht das tun kann, wofür sie eigentlich gedacht ist, z. B. die erwarteten Output-Parameter nicht sinnvoll setzen kann. In Sprachen ohne Exceptions kann man dann ersatzweise nur einen entsprechenden Return-Code zurückliefern. Wenn man jedoch kein Ausnahmeprogrammierer ist, sondern vergesslich, eilig oder einfach schlampig (wie ich), so wird man vielleicht versäumen, nach mehr oder weniger jedem Function-Call diesen Return-Code nach allen Regeln der Kunst akribisch auszuwerten und die undefinierten Outputs einfach unbesehen verwenden, was zu Abstürzen oder schwer aufzuklärenden Folgefehlern führen kann. --kg
Daher:

Primäres Ziel eines in der Sprache und dem Laufzeitsystem verankerten Exception-Handling ist es daher, zuverlässig zu verhindern, dass gescheiterte Methodenaufrufe (= "Vertragsbrüche") vom Aufrufer versehentlich ignoriert werden und deren Ergebnisse trotzdem benutzt werden. Zugleich ermöglicht Exception-Handling eine gröbere Fehlerbehandlung auf höherer Ebene, statt dass man nach den meisten Function-Calls einen Return-Code auswerten muss:

Exception-Handling ist eine Methode, "Vertragsbrüche" beim Methodenaufruf nicht-ignorierbar zu machen und ihre Behandlung ohne viel Umstand von der unterst-möglichen auf die am besten geeignete Behandlungsebene zu verschieben.

Maßnahmen in Lava:

  1. Methoden-Parameter werden strikt in Input- und Output-Parameter getrennt; Parameter-Übergabe "by reference" gibt es nicht.
  2. Eine Methode muss jedem nicht-optionalen Output-Parameter in jedem ihrer Zweige einen Wert zuweisen: wird zur Programmierzeit kontrolliert. (Daneben wird auch verhindert, dass die Function einem Input-Parameter einen neuen Wert zuweist.)
  3. Wenn eine Methode in einem Zweig nicht alle Output-Parameter erfolgreich setzen kann, so kann dieser Zweig nur mit einem "fail <expression>"-Statement abgeschlossen werden, das eine Exception auslöst: wird zur Programmierzeit erzwungen; der Wert von <expression> definiert das zugehörige Exception-Objekt. Sein Typ muss durch eine der Klassen beschrieben werden, die in der "throws"-Klausel ("Exception-Spezifikation") der Methoden-Deklaration enthalten sind. Anders als in Java muss die Exception-Spezifikation jedoch keine Exceptions auflisten, die von untergeordneten Methodenaufrufen herrühren können. Vgl. CheckedExceptionsConsideredHarmful.
  4. Lava unterstützt Attached Assertions (ähnlich dem "Design By Contract DBC in SpracheEiffel): In der Methoden-Deklaration kann man Vor- und Nachbedingungen des Methodenaufrufs deklarieren, die bei Verletzung entsprechende Exceptions auslösen. Lava-Klassen (Interface und Implementation) können mit Invarianten versehen werden, die nach jedem Methodenaufruf gültig bleiben müssen, sonst Exception.
Neben dem klassischen "typverzweigenden" Exception-Handling mittels "catch"-Klauseln ermöglicht Lava auch ein mehr objektorientiertes Exception-Handling durch Überschreibung der Methode "Catch" der Klasse "Object": Man benutzt in der "catch"-Klausel lediglich die unspezifische gemeinsame Basisklasse "Exception" aller Exceptions und ruft "Catch" mit dem entsprechenden Exceptionobjekt auf. Object::Catch gibt einfach den aktuellen Call-Stack zum Zeitpunkt der Exception, sowie einen Fehlercode und eine textuelle Fehlermeltung aus, die Bestandteil jedes Exception-Objektes sind.

Auf eine "Aufräum"-Klausel "finally" (Java, C#), die unter allen Umständen bei Verlassen des try-Statements ausgeführt wird, haben wir in Lava verzichtet: Generell wird die Lebensdauer von Lava-Objekten durch Referenzzähler kontrolliert. Dadurch ist gesichert, dass ein Objekt immer zum frühestmöglichen Zeitpunkt automatisch freigegeben wird, nämlich sobald es nicht mehr referiert wird.

Zu diesem wohldefinierten Zeitpunkt wird dann auch automatisch die Finalize-Methode aufgerufen (die entweder von Klasse "Object" geerbt wird oder handprogrammiert sein kann). Durch expliziten Aufruf von Objekt::Finalize kann ein Objekt vorzeitig unbenutzbar gemacht ("zombifiziert") werden: Jeder nachfolgende Benutzungsversuch löst dann eine spezifische Exception aus.

Ein handprogrammiertes Finalize wird man in Lava nur benötigen, wenn es um die korrekte Freigabe von Betriebssystem- oder anderen Nicht-Lava-Objekten geht, deren Lebensdauer nicht durch Lava-Referenzzähler kontrolliert wird.

Ergänzende Anmerkungen: Das versehentliche Benutzen undefinierter Variablen-Werte (und damit die Haupt-Ursache von Exceptions) wird übrigens in Lava nicht nur für Output-Parameter, sondern auch für lokale und Member-Variablen (durch Programmierzeit-Checks) verhindert:

  1. Eine lokale oder Member-Variable muss "im gleichen Programmzweig weiter oben" gesetzt werden, bevor sie lesend benutzt werden kann.
  2. Ein "Initializer" einer Klasse (~ Konstruktor in Lava) muss jede nicht-optionale Member-Variable setzen: wird zur Programmierzeit geprüft.
  3. Ein Lava-Object kann nicht als Input-Parameter an eine Methode übergeben werden, solange es noch nicht vollständig initialisiert ist.
  4. Die (lesende) Benutzung von Variablen und Ausdrücken, deren Wert optional ist (also auch null sein kann) an Stellen, wo ein nicht-optionaler Ausdruck erwartet wird, wird durch statische Checks verhindert. Man wird so gezwungen, explizit den Lava-"else"-Ausdruck oder die "ifdef"-Anweisung zu benutzen, um sicherzustellen, dass man nicht versehentlich über undefinierte optionale Ausdrucks-Werte "stolpert".
Die entsprechenden (statischen) Prüfungen sind lückenlos und umfassend. D.h. sie gelingen nicht nur unter besonders günstigen Voraussetzungen, sondern immer. Dies wird erst dadurch möglich, dass Lava

  1. eine Single-Assignment-Sprache ist und
  2. (als Folge davon) keine Schleifenkonstrukte, sondern nur rekursive Funktionsaufrufe vorsieht.
Lava-Variablen sind also keine "Datenbehälter" mit wechselndem Inhalt, sondern erhalten stets "weiter oben im gleichen Programmzweig" einen Wert, der dann konstant bleibt; sie sind also quasi "berechnete Konstanten".

Siehe dazu auch http://lavape.sourceforge.net/doc/html/RepetComputSamples.htm.

Andere denkbare Maßnahmen, um das irrtümliche Arbeiten mit undefinierten oder semantisch nicht adäquaten Variablen-Werten unwahrscheinlicher zu machen, die z. B. die Ursache für gewisse gescheiterte Weltraum-Missionen waren:

Wenn ein Input-Parameter einer Methode in einem gewissen Wertebereich liegen muss (was beim Ariane5Absturz meines Wissens der Fall war), so müsste ein gewissenhafter Methoden-Programmierer dies entweder durch eine Assertion (eine Methoden-Vorbedingung, oder ein Lava-"assert"-Statement, löst bei Verletzung Exception aus) oder durch eine if-Verzweigung absichern, die nötigenfalls eine spezifische Exception auslöst. Zumindest aber sollte eine solche Vorbedingung als Kommentar in der Methoden-Deklaration ausdrücklich genannt werden, damit man schon zur Programmierzeit auf das Problem aufmerksam wird.

Eine amerikanische Mars-Mission ist meines Wissens gescheitert, weil Maßeinheiten (yard und meter o. ä.) verwechselt worden waren. Dies könnte man vermeiden, indem man für Zahlen-Parameter nicht einfach Float-, Double- oder Integer-Typen benutzt, sondern (sofern diese Basis-Typen, wie in Lava, normale Klassen sind) daraus abgeleitete Klassen, die die Maßeinheit ausdrücken. Dann wäre die Verwechslung schon zur Programmierzeit als Typ-Fehler aufgefallen.

Man braucht dann aber auch ein spezifisches Ausdrucksmittel (wie die "scale"-Expressions in Lava), um einen gewöhnlichen Zahlenwert, der vielleicht aus einer mathematischen Funktion (Sinus o. ä.) herauskommt, in einen solchen "dimensionierten" Wert umzuinterpretieren/zu spezialisieren. Siehe dazu Punkt 8 der Release Notes zu Lava 0.6: http://lavape.sourceforge.net/doc/html/ReleaseNotes.htm#R0_6, und das zugehörige Beispiel http://lavape.sourceforge.net/doc/Samples/ScaleUnits.htm.

Zu Exceptions in SpracheLava siehe auch http://lavape.sourceforge.net/doc/html/ExceptionSamples.htm

Exception-Handling durch Callbacks:

Das "typverzweigende" Exception-Handling durch typspezifische "catch"-Klauseln ist ja eigentlich unbefriedigend und unerwünscht, weil es das objektorientierte Denken untergräbt.

Das objektorientierte Exception-Handling durch eine überschriebene "Object::catch"-Methode (s.o.) ist nur dann sinnvoll anwendbar, wenn derjenige, der die Exception behandelt, zugleich die abgeleitete Exceptionklasse "in der Hand hat", also nach seinen Vorstellungenn gestalten kann, insbesondere die überschriebene "catch"-Methode selbst programmieren kann, was meistens nicht der Fall sein wird, da die Exceptionklasse normalerweise von dem Programmierer der gerufenen Methode gestaltet werden wird.

Ein angemessener objektorientierter Umgang mit diesen Fällen kann in Lava dadurch erreicht werden, dass die exception-auslösende Methode unmittelbar vor dem "Werfen" der Exception ein spezifisches "Signal" im Sinne des Lava-Callback-Konzeptes emittiert, für das der Methoden-Aufrufer eine von ihm programmierte Callback-Methode abonniert hat, die dann also vor dem formellen Auslösen der Exception auf Grund des emittierten Signals automatisch aufgerufen wird und die eigentliche Exceptionbehandlung übernimmt.

--KlausGünther


KategorieProgrammierSprache KategorieProgrammierSprachenKonzepte KategorieLava KategorieException
StartSeite | Neues | TestSeite | ForumSeite | Teilnehmer | Kategorien | Index | Hilfe | Einstellungen | Ändern
Text dieser Seite ändern (zuletzt geändert: 13. Januar 2006 12:53 (diff))
Suchbegriff: gesucht wird
im Titel
im Text