description |
---|
Entweder Erfolgsfall mit Resultat oder Fehlerfall mit Fehlermeldung |
Der Either Type wird häufig in funktionalen Programmiersprachen wie zum Beispiel Haskell oder Scala eingesetzt für das Error Handling. Der Either Type ist ein polymorpher Typ, der zwei Zustände annehmen kann. Für diese zwei Zustände gibt es die Wert-Konstruktoren Left
und Right
. Somit ist ein Either entweder ein Left
oder ein Right
. Beide tragen einen Wert mit sich: Left
wird verwendet um im Fehlerfall die Fehlermeldung zu kapseln; Right
wird verwendet, um im Erfolgsfall den korrekten Wert zu kapseln. Durch den Either Type kann so in rein funktionalen Sprache elegant auf Fehler reagiert werden. Dabei gibt es keine Seiteneffekte, wie es ansonsten mit dem throw
Statement in JavaScript geben würde.
const Left = x => f => _ => f (x);
const Right = x => _ => g => g (x);
Left
und Right
sind zwei Funktionen die jeweils einen Wert und zwei Funktionen entgegen nehmen. Beide Funktionen ignorieren eine der beiden übergebenen Funktionen.Left
wendet die linke (erste übergebene) Funktion auf den Parameter x
an und ignoriert die zweite. Right
wendet die rechte (zweite übergebene) Funktion auf den Parameter x
an und ignoriert die erste. Left
und Right
bilden die Basis für einen weiteren Typ, den Maybe Type.
{% hint style="info" %} Die Titel der Funktionen sind mit einem Link zur Implementation verknüpft. {% endhint %}
Die folgenden Funktionen geben alle ein Either zurück und unterstützen so eine Fehlerbehandlung mit reinen Funktionen ohne Seiteneffekte. Somit können typische Fehler, die zum Beispiel auftreten wenn Werte null
oder undefined
sind, vermieden werden. Eine Funktion die ein Either zurück liefert hilft dem Anwender an den Fehlerfall zu denken und diesen zu behandeln.
Bei Funktionen, die ein Either zurückgeben können an den Funktionsaufruf zwei weitere Parameter übergeben werden. Der erste Parameter ist eine Funktion, die eine Fehlermeldung entgegen nimmt und dann eine Fehlerbehandlung durchführt. Der zweite Parameter ist eine Funktion für den Erfolgsfall, die das Resultat entgegen nimmt.
Allgemeines Schema:
Eine Either Funktion XYZ wird mit einem oder mehreren Parametern aufgerufen. Am Schluss vom Funktionsaufruf werden 2 Funktionen übergeben. Eine Funktion für den Fehlerfall (Left Case) und eine für den Erfolgsfall (Right Case).
// Anwendung
eitherXYZ(someParam)
(error => doSomethingInErrorCase(error) ) // Left Case
(result => doSomethingInSuccessCase(result) ) // Right Case
Die eitherTruthy
Funktion erwartet einen Wert und überprüft ob dieser 'truthy' ist. Im Erfolgsfall wird ein Right
mit dem Element zurück gegeben und im Fehlerfall ein Left
mit der entsprechenden Fehlermeldung.
{% hint style="info" %} Liste mit JavaScript 'falsy' Werten. {% endhint %}
const eitherTruthy = value =>
value
? Right(value)
: Left(`'${value}' is a falsy value`);
Die eitherNotNullAndUndefined
****Funktion erwartet einen Wert und überprüft ob dieser nicht null oder undefined ist.
const eitherNotNullAndUndefined = value =>
value !== null && value !== undefined
? Right(value)
: Left(`element is '${value}'`);
Die eitherElementOrCustomErrorMessage
Funktion erwartet eine Fehlermeldung und ein Element. Die Funktion überprüft das Element auf null oder undefined und gibt entweder ein Right
mit dem Wert oder ein Left
mit der übergebenen Fehlermeldung zurück.
// Implementation
const eitherElementOrCustomErrorMessage = errorMessage => element =>
eitherNotNullAndUndefined(element)
(_ => Left(errorMessage))
(_ => Right(element));
// Anwendung
eitherElementOrCustomErrorMessage("Der Wert ist Null")(null); // Left ("Der Wert ist null")
Die eitherDomElement
Funktion nimmt eine Id für ein Dom-Element entgegen und gibt ein Either Type zurück. Im Erfolgsfall wird das HTML-Element zurückgegeben sonst eine Fehlermeldung, dass ein solches Element nicht existiert.
const eitherDomElement = elemId =>
eitherElementOrCustomErrorMessage
(`no element exist with id: ${elemId}`)
(document.getElementById(elemId));
Die eitherNumber
Funktion überprüft ob ein Wert vom Typ Integer ist.
const eitherNumber = val =>
Number.isInteger(val)
? Right(val)
: Left(`'${val}' is not a integer`);
Die eitherNaturalNumber
Funktion überprüft ob der übergebene Wert eine natürliche JavaScript-Zahl ist.
const eitherNaturalNumber = val =>
Number.isInteger(val) && val >= 0
? Right(val)
: Left(`'${val}' is not a natural number`);
Die eitherFunction
Funktion überprüft ob ein Wert vom Typ function ist.
const eitherFunction = val =>
typeof val === "function"
? Right(val)
: Left(`'${val}' is not a function`);
Die eitherTryCatch
Funktion nimmt eine Funktion f
entgegen, die schief gehen könnte. Diese Funktion wird in einem try-catch Block ausgeführt. Wenn ein Fehler auftritt während der Funktionsausführung wird dieser gefangen und es wird ein Left
mit der Fehlermeldung zurückgegeben, ansonsten ein Right
mit dem Resultat.
{% hint style="info" %}
Diese Funktion hat den Zweck bestehende JavaScript Funktionen die noch auf die nicht funktionale Art Fehler mit throw
werfen abzufangen und diese in die Welt der funktionalen Programmierung einzubetten. Somit fungiert diese Funktion als Brücke von der JavaScript Welt in die Welt der funktionalen Programmiersprachen.
{% endhint %}
const eitherTryCatch = f => {
try {
return Right(f());
} catch (error) {
return Left(error);
}
}
Die Funktion eitherElementsOrErrorsByFunction
nimmt als ersten Parameter eine Funktion und als zweiten Parameter einen Rest Parameter (JavaScript Rest Parameter). Die Funktion die übergeben wird sollte einen Wert entgegen nehmen und ein Either Type zurückgeben. Die Funktion eitherElementsOrErrorsByFunction
wendet dann die übergebene Funktion auf jeden Wert an der durch den Rest Parameter übergeben wurde. Zurück kommt ein Either. Im Erfolgsfall (Right) bekommt der Anwender eine ListMap mit allen "Erfolgs" -Werten. Im Fehlerfall bekommt der Anwender ein Stack mit allen Fehlermeldungen die aufgetreten sind.
{% hint style="info" %}
Sobald ein Funktionsaufruf schief geht, wird ein Left
mit den Fehlermeldungen zurückgegeben.
{% endhint %}
{% hint style="info" %} In Haskell hätte diese Funktion folgenden Typ:
eitherElementsOrErrorsByFunction:: (a -> Either a) -> [a] -> Either [a]
{% endhint %}
Beispiel
eitherElementsOrErrorsByFunction(eitherDomElement)("inputText", "output")
(err => doSomethingWithErrorMessages) // stack mit Fehlermeldungen
(result => { // listMap mit den Resultaten
// Die Resultate als einzelne Variablen
const [inputText, output] = convertListMapToArray(result);
doSomethingWithResult(inputText, output);
})