Höllische Programmiersprachen: Sprachkonstrukte für die

Transcription

Höllische Programmiersprachen: Sprachkonstrukte für die
Höllische Programmiersprachen:
Sprachkonstrukte für die Fehlerbehebung
Liesa Urban
Technische Universität München
In dieser Arbeit geht es darum, wie man mit auftretenden Fehlern in
verschiedenen Programmiersprachen umgehen kann und welche Möglichkeiten
es zur Fehlerbehebung gibt. Dabei wird zuerst ein kurzer Überblick gegeben,
der Error-Handling erklärt und welche Fehlerarten überhaupt auftreten können.
Danach wird auf vier beseondere Möglichkeiten eingegangen. Diese sind
Longjumps in C, On-Handler in Java, das Arbeiten mit Exceptions in C++ und
Java, sowie die partielle Funktion Maybe in Haskell.
Keywords: Error-Handling, Longjumps, On-Handler, Exceptions, Maybe,
Java, Haskell, C, C++
1.1 Was ist Error-Handling und wieso nutzt man es
In der Programmierung erkennt man durch Error-Handling Syntax- und Logikfehler,
die das erfolgreiche ausführen des Codes verhindern und den Entwicklungsprozess
beeinträchtigen.
Zum Handling gehören das Voraussehen, Erkennen und letztendlich das Auflösen
eines Errors.
Um Fehler im Code zu berichtigen, gibt es verschiedene Möglichkeiten, die man
nutzen kann. 1
1.2 Welche Möglichkeiten gibt es
Um Syntaxfehler, die sich in der Typografie und unsauberer Nutzung von Zeichen im
geschriebenen Code darlegen und sich während der Arbeit einschleichen können, zu
vermeiden, kann der Programmierer den Code einfach Korrekturlesen.
Logikfehler, auch Bugs genannt, treten auf, wenn der ausgeführte Code nicht das
erwünschte Ergebnis liefert. Ein solcher Fehler wird am besten durch sorgfältiges
Debugging behandelt, was einen fortlaufenden Prozess als Ergänzung zum
traditionellen Debugging und Betatesten darstellt.
Eine dritte Art ist der Laufzeitfehler, oder Runtime-Error. Er tritt während der
Ausführung des Programms durch ungünstige Systemparameter, oder ungültige
Nutzereingaben auf. Dies wäre beispielsweise bei einem Speicherkonflikt zwischen
zwei Programmen der Fall. Ein Runtime-Error kann behoben, oder falls dies nicht
möglich ist, die Auswirkung durch error handler kleinstmöglich gehalten werden.
Für verschiedene Anwendungen gibt es Programme, sogenannte Error Handler, die
für den Nutzer die Fehlerbehebung übernehmen. Die besten dieser Programme
verhindern Fehler, falls dies möglich ist und sorgen so dafür, dass das Programm
ausgeführt werden kann. Falls dies für eine betroffene Anwendung nicht möglich ist,
werden die Informationen in einem log file gespeichert. 2
2.1 Longjumps – Was ist es, wie nutzt man es, wann nutzt man es
Die longjmp() Funktion wird in C genutzt, um den Fluss der Ausführungssequenzen
zu ändern. 3
Longjmp() und setjmp() sind in setjmp.h, der header Datei definiert:
#include <setjmp.h>
als
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val); 4
1
Vgl http://searchsoftwarequality.techtarget.com/definition/error-handling
Vgl http://searchsoftwarequality.techtarget.com/definition/error-handling
3 Vgl http://pubs.opengroup.org/onlinepubs/009696899/functions/longjmp.html
4 Vgl http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
2
Longjmp() stellt die Umgebung wieder her, die an den jüngsten Aufrufen von
setjmp() im selben Thread mit den dazugehörigen jmp_buf Argument gespeichert
wurde.
Falls ein solcher Aufruf nicht existiert, oder die Funktion, die den setjmp() Aufruf
beinhaltet in der Zwischenzeit terminiert, ist das Verhalten undefinierbar. Dies gilt
ebenfalls, wenn der Aufruf von setjmp() innerhalb des Anwendungsbereichs des
Identifier mit variabel modifizierten Typen und Ausführungen den Geltungsbereich in
der Zwischenzeit verlässt. 5
Die Aufrufsumgebung repräsentiert den Status der Register und den Punkt im Code,
an dem die Funktion aufgerufen wurde. Wenn longjmp() aufgerufen wird, wird der in
jmp_buf gespeicherte Status zurück in den Prozessor kopiert und die Berechnung
startet wieder vom letzten Rückgabepunkt von setjmp(). Als Ausgabe gibt setjmp()
beim ersten Aufruf 0 zurück. Danach gibt es den Wert, der als zweites Argument von
longjmp() übergeben wurde zurück. Dazu füllt es den jmp_buf mit dem calling
environment und der singal mask. 6 7
Wenn setjmp() aufgerufen wird, werden die aktuellen Prozessorregister,
eingeschlossen die Stack Pointer, mit einer Markierung gespeichert. Hierzu wird die
Markierung genutzt, um zu bestimmen, an welcher Position die Ausführung nach
setjmp() fortgesetzt werden soll. Wenn an einem darauf folgenden Punkt im
Programm lonjmp() ausgeführt wird, mit der Markierung als Argument, wird der zum
im Prozessorregister gehörende Stackpointer an die Position gesetzt, an der er zuvor
war. Das Programm arbeitet danach weiter, als wäre setjmp() erneut aufgerufen
worden.8 Dazu ein Codebeispiel:
#include <stdio.h>
#include <setjmp.h>
#define
#define
#define
#define
TRY do{ jmp_buf ex_buf__; if( !setjmp(ex_buf__)){
CATCH } else {
ETRY } }while(0)
THROW longjmp(ex_buf__, 1)
int
main(int argc, char** argv) {
TRY {
printf("In Try Statement\n");
THROW;
printf("I do not appear\n"); }
CATCH {
printf("Got Exception!\n"); }
ETRY;
return 0; }
5
Vgl http://pubs.opengroup.org/onlinepubs/009696899/functions/longjmp.html
Vgl http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
7 Vgl http://www.fmc-modeling.org/category/projects/apache/amp/A_5_Longjmp.html
8 Vgl http://www.fmc-modeling.org/category/projects/apache/amp/A_5_Longjmp.html
6
Die Idee hierbei ist es, ein If Statement als TRY zu nutzen. Beim ersten Aufruf wird 0
zurückgegeben und der ausgeführte Code wird in then übergeben.
CATCH kann man als else betrachten. Wenn THROW ausgeführt wird, wird einfach
lonjmp() aufgerufen, deren zweiter Parameter einem Wert ungleich 0 entspricht.
ETRY zeigt das Ende des try-throw-catch Blocks an. Dies wird durch die Ausführung
des Codes im do...while(0) Block benötigt.
Diese Lösung hat zwei Gründe:
 alle TRY-ETRY Ausdrücke werden als einzelnes Statement betrachtet
 man kann mehrere, nicht verschachtelte TRY-ETRY Statements im selben
Block durch die Wiedernutzung der ex_buf __ Variable definieren 9
Longjump ähnelt also „goto“. Im Gegensatz dazu stellt longjump alle programm
states wieder her. Deshalb kann es für Sprünge ohne Zerstörung des call stack genutzt
werden.
Der Nachteil an Longjump ist, dass man auf die zugrundeliegende Systemtechnologie
achten muss, um das Konzept voll zu verstehen. Außerdem wird das Programm
unübersichtlicher, da nicht standardisierte Konzepte genutzt werden.
Ein weiteres Codebeispiel zum Schluss:
/* Copyright (C) 2009-2014 Francesco Nidito
* Permission is hereby granted, free of charge, to any person
obtaining a copy
* of this software and associated documentation files (the
"Software"), to deal
* in the Software without restriction, including without
limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies
* of the Software, and to permit persons to whom the Software
is furnished to do
* so, subject to the following conditions:
* The above copyright notice and this permission notice shall
be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES
OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE
* SOFTWARE.
*/
9
Vgl http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
#ifndef _TRY_THROW_CATCH_H_
#define _TRY_THROW_CATCH_H_
#include <stdio.h>
#include <setjmp.h>
/* For the full documentation and explanation of the code
below, please refer to
* /~nids/docs/longjump_try_trow_catch.html
*/
#define
TRY
do
{
jmp_buf
ex_buf__;
setjmp(ex_buf__) ) { case 0: while(1) {
#define CATCH(x) break; case x:
#define FINALLY break; } default: {
#define ETRY } } }while(0)
#define THROW(x) longjmp(ex_buf__, x)
#endif /*!_TRY_THROW_CATCH_H_*/
switch(
10
2.2 On-Handler - Java
Ein Handler kümmert sich um Events und wird nur Aufgerufen, wenn dieses Event
auftritt. Er ist spezialisiert auf einen bestimmten Datentyp, oder bestimmte Aufgaben.
Es existieren verschiedene Arten, wie:
 Der Event Handler, der Events und Signale vom System (OS, GUI,…)
empfängt und verarbeitet
 Der Memory Handler, der spezielle Aufgaben im Speicher durchführt
 Der File Input Handler, der eine Funktion stellt, die Dateninput erhält und
damit spezielle Aufgaben bearbeitet 11
Mit .on() fügt man eine Eventhandler Funktion für ein, oder mehrere Events an die
gewählten Elemente im jQuery, einer freien JavaScript Bibliothek, an. Sollen Events,
die mit .on() verbunden sind, entfernt werden, so nutzt man .off().
Jeder Eventname kann als Eventargument genutzt werden. JQuery geht die Browser
Standard JavaScript Event Typen durch und ruft die Handler Funktion auf, wenn der
Browser Events aufgrund von Nutzeraktionen, wie einen einfachen Klick, generiert.
Ein Evetnname sollte nur Buchstaben und Zahlen, sowie Unterstriche und
Doppelpunkte enthalten. Ein Eventname kann durch einen Event Namespace
beschrieben werden, welcher es einfacher machet, Events auszulösen, oder zu
löschen.
Mit einem Unterstrich beginnende Namespaces sind für die Nutzung mit jQuery
reserviert.
10
11
Vgl http://www.fmc-modeling.org/category/projects/apache/amp/A_5_Longjmp.html
Vgl http://stackoverflow.com/questions/195357/what-is-a-handler
Ein Namespace Beispiel:
“click.my.plugin.simple“
definiert sowohl myPlugin, als auch einfache Namespace für dieses Clickevent.
Ein Click Event Handler, der durch diesen String angefügt wurde, kann durch
.off(„click.myPlugin“) oder .off(„click.simple“)
wieder entfernt werden, ohne andere Klick Handler zu stören, die mit den Elementen
verbunden sind.
Die Zweite Art von .on() nutzt die einfachen Objektargumente. Hier sind Schlüssel
Strings in derselben Form, wie die Eventargumente mit Space-Seperated Event
Typnamen und optional Namespaces. Der Wert für jeden Schlüssel ist eine Funktion,
oder ein Wert, die als Handler genutzt wird, anstatt des finalen Arguments der
Methode. Beide Arten verhalten sich gleich.
Die meisten Events starten beim Event Target und arbeiten sich dann durch das
Programm.
Falls der Selektor fehlt, oder Null ist, verweist der Event Handler auf directed oder
directly-bound.
Der Handler wird immer aufgerufen, wenn ein Event bei dem gewählten Element
auftritt, egal welcher Fall zutrifft.
Wenn es einen Selektor gibt, verweist der Event Handler auf delegated. Der Handler
wird nicht aufgerufen, wenn das Event direkt beim verbundenen Element auftritt, dies
trifft jedoch nur für Teile, die davon erben, oder innere Elemente zu, die zum Selektor
passen.
JQuery sorgt dafür, dass Events sich vom Eventtarget aus vom mit dem Handler
verbundenen innersten bis zum äußersten Element ausbreiten. Dabei läuft der Handler
für alle Elemente entlang des Pfades, der zum Selektor passt. Event Handler sind nur
an die aktuell gewählten Elemente gebunden und müssen auf der Seite existieren,
wenn im Code .on() aufgerufen wird.
Um sicherzustellen, dass die Elemente existieren und ausgewählt werden können,
wird Event Binding innerhalb eines Document Ready Handler für Elemente im
HTML markup auf der Seite ausgeführt. Wird neuer HTML Code auf der Seite
eingefügt, werden Elemente gewählt und Event Handler angefügt. Alternativ kann
man Events an Event Handler anhängen.
Diese haben den Vorteil, dass sie Events von vererbten Elementen bearbeiten können,
die dem Dokument erst später hinzugefügt wurden.
Wählt man ein Element, das sicher vorhanden ist, wenn der delegierte Event Handler
verbunden ist, so kann man dessen Events nutzen, um frequenzielles anbinden und
entfernen von Event Handlern zu vermieden.
Zu ihrer Fähigkeit Events mit noch nicht erstellten Nachkommen zu handhaben,
kommt noch als Vorteil hinzu, dass sie potenziell für bedeutend weniger Overhead
sorgen, wenn viele Elemente überwacht werden müssen. Für ein Datenverzeichnis mit
1000 Zeilen im tbody, wäre ein Beispiel für einen Handler folgendes:
$ ( "#dataTable tbody tr" ).on( "click", function() {
alert( $( this ).text() );
} );
Das Vorgehen mit vererbten Events trifft einen Event Handler nur an einem Element,
dem tbody und das Event muss nur ein Level aufsteigen und zwar vom angeklickten
tr zum tbody. Das sieht im Code folgendermaßen aus:
$ ( "#dataTable tbody" ).on( "click", "tr", function() {
alert( $( this ).text() );
} );
12
2.3 Arbeit mit Exceptions
Eine Exception wird geworfen, wenn ein Objekt nicht wie geplant arbeiten kann und
der Kontrollfluss des Programms verlassen wird und in die Ausnahmebehandlung,
oder Exception Handling übergeht. Somit werden neue Ausführungspfade in ein
Programm gebracht. Je nach Problem und Herangehensweise wird durch den
Exception Handler bestimmt, an welcher Stelle die Exception gefangen wird und das
Programm danach weiter planmäßig ausgeführt werden kann.
Der große Vorteil einer Exception ist hierbei, dass der normale Ausführungspfad im
Kontrollflussdiagramm von den möglicherweise fehlerhaften Ausführungspfaden
freigehalten wird und unbeteiligte Methoden die Exceptions nicht weiterreichen
müssen. Dadurch helfen Exceptions dabei, den eigentlichen Zweck einer Methode
klarer hervorzuheben.
Deshalb muss, falls mit Exceptions gearbeitet wird, immer damit gerechnet werden,
dass sie den Aufruf einer Operation unterbricht und es muss dafür gesorgt werden,
dass der Code trotzdem weiter ordnungsgemäß läuft und keine Speicherlecks
entstehen, oder Fehler bei der Freigabe belegter Ressourcen. Diese Anforderung
nennt man Exception Safety.
Zum werfen einer Exception wird im Java Code throw new Exception der Klasse
(“Fehlermeldung“); verwendet. Ein Beispiel:
class BeispielException{
void kritischeOperationEins(){
bearbeiteErstenTeil();
aktionMitAusnahme();
bearbeiteZweitenTeil();
}
private void aktionMitAusnahme(){
if (!AktionMoeglich()) {
throw new AktionNichtMoeglichException(“zu spaet“);
}
}
}
12
Vgl http://api.jquery.com/on/
class AktionNichtMoeglichException
extends RuntimeException2{
AktionNichtMoeglichException(String grund) {
Super(“Aktion nicht moeglich:“ + grund);
}
}
class KeinPlanException{
void beispielOperation() {
WirftException benutztesObjekt = new WirftException();
benutztesObject.kritischeOperationEins();
}
}
class LoestException {
void nutzendeOperationEins(){
KeinPlanException benutztesObjekt =
new KeinPlanException();
try {
benutztesObjekt.eineOperation(); }
Catch (AktionNichtMoeglichException exception){
neuerPlan();
}
}
}
In KeinPlanException wird die kritische Operation aufgerufen, kann jedoch nicht
behandelt werden. Dies geschieht in der Klasse LoestException. Dies geht, da
Exceptions von Aufrufer zu Aufrufer weitergeleitet werden. Die Fehlerbehebung wird
hier ausgeführt, bis ein passender Exceptionhandler gefunden wird.
Wird eine Exception des Typs AktionNichtMoeglichException im Try-Teil eines trycatch-Blocks geworfen, so wird sie im catch Block durch ausführen von neuerPlan()
abgefangen und behandelt.
Nach der Ausführung fährt das Programm mit nutzendeOperationEins fort.
Falls ein catch-Statement alle Exceptions einer Klasse abfängt, gilt dies auch für
eventuelle Unterklassenexceptions.
Ein weiteres Hilfsmittel bei der Nutzung von Exceptions ist der sogenannte finally
Block. Dieser wird in zwei Teilen umgesetzt und der enthaltene Code wird in jedem
Fall durchlaufen, auch wenn es im try- Block zu Fehlern kam. Anwendungsbeispiele
hierfür wären das Schließen von geöffneten Dateien, um belegte Ressourcen wieder
freigeben zu können, oder auch das festlegen bereits neugesetzter Variablen, die
andernfalls falsche Werte behalten würden.
In C++ kann ein Mechanismus namens “Ressourcenbelegung ist Initialisierung“, oder
“Resource Acquisition is Initialisation“ (RAII), genutzt werden. Durch das Anlegen
von Ressourcen im Konstruktor eines Objekts und das freigeben in dessen Destruktor,
können hier Ressourcen verwaltet werden. Wird eine solche Variable auf dem Stack
angelegt, so stellt der Kompiler sicher, dass der Destruktor beim Verlassen des
Sichtbarkeitsbereichs aufgerufen wird, wodurch die verwendeten Ressourcen wieder
freigegeben werden. Dies ist besonders nützlich , falls der Sichtbarkeitsbereich durch
eine Exception verlassen werden muss.
Auch hierfür ein Beispiel:
RAII::RAII()
{
pMyResource = new MyResource();
pMyResource->acquire();
}
RAII::~RAII()
{
pMyResource->release();
delete pMyResource;
}
void RAIITester::RunTest()
{
RAII raii;
bool condition_red = false;
MyResource* pResource = raii.GetResource();
// ... Aktionen mit der Ressource ausführen
if (condition_red) {
throw std::exception();}
// .. weitere Aktionen
}
Hier wird eine lokale Variable für RAII angelegt und die benötigte Ressource wird
reserviert. Wird der Sicherheitsbereich von RunTest verlassen, so wird der Destruktor
von RAII aufgerufen und die Ressource so wieder freigegeben, auch wenn während
des Ablaufs eine Execption geworfen wird.
Exceptions sollten verwendet werden, um auf ungewöhnliche Ergebnisse einer
Operation aufmerksam zu machen, auch wenn sie nicht die einzige Möglichkeit
darstellen. Häufig verwendet man einen festgelegten Wert als Ausgabeparameter, der
einen Fehlercode zurückgeben wird.
Exceptions haben allerdings den einschneidenden Vorteil, dass der Programmiere
nichts weiter tun muss, um Fehler weiterzugeben. Wird eine geworfene Exception
nicht gefangen, so wird sie automatisch an den Aufrufer weitergeleitet. Fehlercodes
handhaben so etwas unnötig kompliziert.
Desweiteren zeigt sich eine nicht abgefangene Exception deutlich, wohingegen
Fehlercodes mitunter ignoriert werden. Um Warnungen über die Ausführung einer
Operation an den Aufrufer weiterzuleiten, sollte man allerdings eher auf Fehlercodes
zurückgreifen.
Im Gesamtbild überwiegen die Vorteile von Exceptions, denn auch wenn man als
Programmierer darauf achten muss, seinen Code Exception-sicher zu schreiben, so
machen sie wichtige Abläufe doch klar erkennbar, indem sie bestimmte
Ausführungspfade verstecken und so die Übersichtlichkeit steigern.
Unterstützt eine Programmiersprache eine Exception. So biete sie auch eine
Möglichkeit, alle Exceptions zu fangen. In C++ wäre diese Möglichkeit catch(), in
Java die Basisklasse Throwable. Jedoch sollte man simples protokollieren einer
Exception, ohne dagegen vorzugehen auf jeden Fall vermeiden, da dadurch
Folgefehler entstehen könne, die schwieriger zu finden sind und unter Umständen
auch viel schwerwiegender. 13
2.4 Maybe
Maybe wird in der Programmiersprache Haskell als partielle Funktion verwendet und
stellt die einfachste Lösung dar, um mit Fehlern umzugehen. Man sollte sie jedoch
nur nutzen, falls die Fehlerart unwesentlich ist, oder falls es im Kontext nur eine
Fehlerart gibt.
Würde man Beispielsweise in Java eine Methode über x.f() aufrufen, wobei x
natürlich keine Null-Referenz enthalten darf, so hätte f in Haskall folgenden Typ
F :: a -> Maybe b
Hier ist a der Typ von x und b der Resultattyp von f. Nun kann es entweder passieren,
dass der Aufruf gelingt und y das Ergebnis wäre, oder für eine NullPointerException
Nothing ausgegeben wird. Eine Partielle Funktion muss in Haskell durch die
Funktionskomposition >=> definiert werden als
(>=>)::(a -> Maybe b) -> (b -> Maybe c) -> (a -> Maybe c)
f >=> g = \ x -> case f x of
Nothing -> Nothing
Just x2 -> g x2
Sobald eine Funktion Nothing als Ergebnis liefert, wird mit dem Resultat Nothing
geendet. Auf diese Art lassen sich die Funktionen f1 bis fn zu einer Pipe
zusammensetzen
f1 :: a
-> Maybe b
f2 :: b
-> Maybe ...
...
fn :: ... -> Maybe c
f1 >=> f2 >=> ... >=> fn :: a -> Maybe c 14
In Programmiersprachen wie Haskell, gibt es abstrakte Datentypen, die Monade
genannt werden. Es sind zusammensetzbare Berechnungsbeschreibungen. Das
Besondere hierbei ist die Trennung der Kompositionszeitlinie von der auszuführenden
Zeitlinie der Berechnung. Ebenso die Fähigkeit der Berechnung, um implizite
Extradaten tragen zu können. 15
13Vgl
http://openbook.galileopress.de/oo/oo_05_ablauefevonooprogrammen_005.htm#Rxxob05ablauefevonooprogramme
n005040015c41f042130
14 Vgl http://funktionale-programmierung.de/2013/04/18/haskell-monaden.html
15 Vgl https://www.haskell.org/haskellwiki/Monad
Die Maybe-Monade könnte folgendermaßen aussehen:
instance Monad Maybe where
return x
= Just x
Nothing >>= f = Nothing
(Just x) >>= f = f x
Mit neutralem Element
idMaybe :: a -> Maybe a
idMaybe x = Just x
Hierbei gilt:
idMaybe >=> f
= f
= f >=> idMaybe
Es ist möglich idMaybe direkt ohne Eigenschaften von Maybe zu formulieren, wie
man hier an der dritten Form sehen kann:
idMaybe x = Just x
= idMaybe x = return x
= idMaybe = return . id
Die Monade Instanz für Maybe ist im Haskell Prelude, einem Standartmodul das in
allen Haskell Modulen integriert ist, bereits vordefiniert und kann einfach verwendet
werden.16 17
3 Zusammenfassung
Die hier vorgestellten Handhabungsmöglichkeiten haben alle ihre Vor- und Nachteile
und unterliegen gewissen Beschränkungen. In jeder Programmiersprache gibt es
verschiedene Varianten, wie man mit auftretenden Fehlern umgehen kann. Natürlich
muss man sich erst einmal in vieles hineinarbeiten, doch letztendlich lohnt es sich,
wenn man schnelle Lösungen nutzen kann und so den Code in kurzer Zeit verbessern
und gegen auftretende Probleme die beste Lösung finden kann. Daher lohnt es sich
auf jeden Fall, sich mit Error-Handling auseinanderzusetzen, auch wenn einiges auf
den ersten Blick vielleicht sehr umständlich, oder verwirrend scheinen mag.
16
17
Vgl http://funktionale-programmierung.de/2013/04/18/haskell-monaden.html
Vgl http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html
4 Quellennachweis
1. Einleitung
 http://searchsoftwarequality.techtarget.com/definition/error-handling zuletzt
aufgerufen am 18.12.2014
2.1 Longjumps
 http://www.di.unipi.it/~nids/docs/longjump_try_trow_catch.html
zuletzt
aufgerufen am 18.12.2014
 http://pubs.opengroup.org/onlinepubs/009696899/functions/longjmp.html
zuletzt aufgerufen am 18.12.2014
 http://www.fmcmodeling.org/category/projects/apache/amp/A_5_Longjmp.html
zuletzt
aufgerufen am 18.12.2014
 http://www.fmcmodeling.org/category/projects/apache/amp/A_5_Longjmp.html
zuletzt
aufgerufen am 18.12.2014
2.2 on-handler
 http://stackoverflow.com/questions/195357/what-is-a-handler
zuletzt
aufgerufen am 18.12.2014
 http://api.jquery.com/on/ zuletzt aufgerufen am 18.12.2014
2.2 Exceptions
 http://openbook.galileopress.de/oo/oo_05_ablauefevonooprogrammen_005.htm zuletzt aufgerufen
am 18.12.2014
2.3 Maybe
 http://funktionale-programmierung.de/2013/04/18/haskell-monaden.html
zuletzt aufgerufen am 18.12.2014
 https://www.haskell.org/haskellwiki/Monad
zuletzt
aufgerufen
am
18.12.2014
 http://hackage.haskell.org/package/base-4.7.0.1/docs/Prelude.html
zuletzt
aufgerufen am 18.12.2014