Fachkonzept - Fehlerbehandlung
Vermeidung von Laufzeitfehlern
Wenn man Programme entwickelt, macht man Fehler. Es gibt dabei verschiedene Arten von Fahler:
- Syntaxfehler: Syntaxfehler entstehen, wenn man man die Regeln der Programmiersprache nicht beachtet. Wenn es z.B. zu einer öffnenden Klammer keine korrespondierende schließende Klammer gibt, dann ist das ein Syntaxfehler. Syntaxfehler werden vom Compiler erkannt. Es gibt dann vorab eine Fehlermeldung.
- Semantische Fehler: Bei einem semantischen Fehler hat man ein lauffähiges Programm entwickelt. Es macht nur nicht das, was es machen soll. Es gibt dann keine Fehlermeldung. Solche Fehler muss man dann selbst erkennen und beheben.
- Laufzeitfehler: Bei einem Laufzeitfehler hat man ein syntaktisch korrektes Programm entwickelt. Während der Laufzeit kommt es aber zu einer Schwierigkeit (run time error), so dass das Programm abbricht. Ein Laufzeitfehler kann z.B. auftreten, wenn das Programm versucht, eine dafür nicht geeignete Zeichenkette in eine Zahl umzuwandeln.
Laufzeitfehler sind sehr unangenehm, da sie zu Programmabstürzen führen können. Man versucht daher, Laufzeitfehler zu vermeiden. Viele Programmiersprachen überlassen es den Programmentwickler(inne)n, die Programme so zu entwickeln, dass sie keine Laufzeitfehler verursachen.
Die Programmiersprache Elm ist so konzipiert, dass keine Laufzeitfehler auftreten können. Das ist ein großes Plus dieser Programmiersprache.
Elm erreicht das mit Hilfe des Typsystems. Zur Charakterisierung potentiell unsicherer Operationen nutzt man geeignete Datentypen
und behandelt Fehler als Daten. Wie das funktioiert wird im Folgenden am Datentyp Maybe
verdeutlicht.
Der Datentyp Maybe
Der Datentyp Maybe
ist so definiert:
type Maybe a
= Just a
| Nothing
Die Typvariable a
kann dabei mit einem beliebigen vordefinierten Typ oder
einem selbst definierten Typ konkretisiert werden.
Beispiel: Maybe Int
Die Typdefinition von Maybe
besagt, dass es genau zwei Sorten von Daten vom Datentyp Maybe Int
gibt:
Zum einen Daten wie Just 2
oder Just -4
, die besagen,
dass genau die betreffende Int
-Zahl gemeint ist.
Zum anderen gibt es den Datenwert Nothing
, der besagt,
dass es sich um nichts dergleichen (hier: um keine Int
-Zahl) handelt.
Der Datentyp Maybe
wird des öfteren bei gängigen vordefinierten Funktionen benutzt.
Beispiel: Zahlumwandlung mit String.toInt
und String.toFloat
> String.toInt
<function> : String -> Maybe Int
> String.toInt "2"
Just 2 : Maybe Int
> String.toInt " 2"
Nothing : Maybe Int
> String.toFloat
<function> : String -> Maybe Float
> String.toFloat "2"
Just 2 : Maybe Float
> String.toFloat ".2"
Just 0.2 : Maybe Float
> String.toFloat " .2"
Nothing : Maybe Float
Immer, wenn eine Umwandlung einer Zeichenkette in eine Zahl schiefläuft, wird der Datenwert Nothing
zurückgegeben.
Beispiel: Listenzugriff mit List.head
und List.tail
> List.head
<function> : List a -> Maybe a
> List.head [1,2,3]
Just 1 : Maybe number
> List.head []
Nothing : Maybe a
> List.tail
<function> : List a -> Maybe (List a)
> List.tail [1,2,3]
Just [2,3] : Maybe (List number)
> List.tail []
Nothing : Maybe (List a)
Bei einer leeren Liste ist kein Zugriff möglich. Dann wird der Datenwert Nothing
zurückgegeben.
Die Beispiele verdeutlichen, dass in Fehlersituationen ein Fehler-Datenwert zurückgeliefert wird, der dann weiterverarbeitet werden kann. Es tritt dadurch kein Laufzeitfehler auf.
Verarbeitung von Maybe-Daten
Eine Verarbeitung von Maybe
-Daten wird häufig mit einem case
-Ausdruck beschrieben.
stringToInt: String -> Int
stringToInt zeichenkette =
let
maybezahl = String.toInt zeichenkette
in
case maybezahl of
Just x -> x
Nothing -> 0
Hier wird im Nothing
-Fall ein selbst gewählter Defaultwert zurückgegeben.
Eine Verarbeitung von Maybe
-Daten ist auch mit der vordefinierten Funktion withDefault
möglich.
stringToInt: String -> Int
stringToInt zeichenkette = Maybe.withDefault 0 (String.toInt zeichenkette)
Hier wird der selbst gewählte Defaultwert innerhalb der Funktion withDefault
gesetzt.
In beiden Implementierungen liefert die selbst Funktion stringToInt
keine Laufzeitfehler,
da sie für alle möglichen Übergabewerte vom Typ String
einen Rückgabewert vom Typ Int
erzeugt.