Fehlerbehandlung
Das Ergebnis der Auswertung eines M-Ausdrucks führt zu einem der folgenden Ergebnisse:
Es wird ein einzelner Wert erstellt.
Es wird ein Fehler ausgelöst, der angibt, dass im Rahmen des Ausdrucksauswertungsprozesses kein Wert ermittelt werden konnte. Ein Fehler enthält einen einzelnen Datensatzwert, der verwendet werden kann, um zusätzliche Informationen über die Ursache der unvollständigen Auswertung bereitzustellen.
Fehler können innerhalb eines Ausdrucks ausgelöst und behandelt werden.
Auslösen von Fehlern
Die Syntax zum Auslösen eines Fehlers lautet wie folgt:
Fehlerauslösender Ausdruck:
error
expression
Textwerte können als Kurzform für Fehlerwerte verwendet werden. Beispiel:
error "Hello, world" // error with message "Hello, world"
Vollständige Fehlerwerte sind Datensätze und können mit der Funktion Error.Record
erstellt werden:
error Error.Record("FileNotFound", "File my.txt not found",
"my.txt")
Der obige Ausdruck entspricht dem folgenden:
error [
Reason = "FileNotFound",
Message = "File my.txt not found",
Detail = "my.txt"
]
Durch das Auslösen eines Fehlers wird die Auswertung des aktuellen Ausdrucks beendet, und der Ausdrucksauswertungsstapel wird entladen, bis eine der folgenden Aktionen auftritt:
Ein Datensatzfeld, ein Abschnittselement oder let-Variablen (zusammen Eintrag) werden erreicht. Der Eintrag ist als fehlerhaft gekennzeichnet, und der Fehlerwert wird mit diesem Eintrag gespeichert und dann weitergegeben. Jeder nachfolgende Zugriff auf diesen Eintrag bewirkt, dass ein identischer Fehler ausgelöst wird. Andere Einträge des Datensatzes, Abschnitts oder let-Ausdrucks sind nicht unbedingt betroffen (es sei denn, sie greifen auf einen Eintrag zu, der zuvor als fehlerhaft gekennzeichnet wurde).
Der Ausdruck der obersten Ebene wird erreicht. In diesem Fall ist das Ergebnis der Auswertung des Ausdrucks der obersten Ebene ein Fehler anstelle eines Werts.
Ein
try
-Ausdruck wird erreicht. In diesem Fall wird der Fehler aufgezeichnet und als Wert zurückgegeben.
Behandeln von Fehlern
Mit einem Fehlerbehandlungsausdruck (informell als „Try-Ausdruck“ bezeichnet) wird ein Fehler behandelt:
Fehlerbehandlungsausdruck:
try
protected-expression-Fehlerhandleropt
protected-expression:
expression
Fehlerhandler:
Otherwise-Klausel
Catch-Klausel
otherwise-Klausel:
otherwise
Standardausdruck
Standardausdruck:
expression
Catch-Klausel:
catch
Catch-Funktion
Catch-Funktion:
(
Parameternameopt)
=>
Funktionsrumpf
Beim Auswerten eines Fehlerbehandlungsausdrucks ohne Fehlerhandler gilt Folgendes:
- Wenn die Auswertung des geschützten Ausdrucks nicht zu einem Fehler führt und einen Wert x erzeugt, ist der Wert, der vom Fehlerbehandlungsausdruck erzeugt wurde, ein Datensatz der folgenden Form:
[ HasErrors = false, Value = x ]
- Wenn bei der Auswertung des geschützten Ausdrucks ein Fehlerwert ausgelöst wird, ist das Ergebnis des Fehlerbehandlungsausdrucks ein Datensatz der folgenden Form:
[ HasErrors = true, Error = e ]
Beim Auswerten eines Fehlerbehandlungsausdrucks mit Fehlerhandler gilt Folgendes:
Der geschützte Ausdruck muss vor dem Fehlerhandler ausgewertet werden.
Der Fehlerhandler darf nur dann ausgewertet werden, wenn bei der Auswertung des geschützten Ausdrucks ein Fehler ausgelöst wird.
Wenn bei der Auswertung des geschützten Ausdrucks ein Fehler ausgelöst wird, ist der vom Fehlerbehandlungsausdruck erzeugte Wert das Ergebnis der Auswertung des Fehlerhandlers.
Fehler, die während der Auswertung des Fehlerhandlers ausgelöst werden, werden weitergegeben.
Wenn der ausgewertete Fehlerhandler eine catch-Klausel ist, wird die Catch-Funktion aufgerufen. Wenn diese Funktion einen Parameter akzeptiert, wird der Fehlerwert als Wert übergeben.
Im folgenden Beispiel wird ein Fehlerbehandlungsausdruck in einem Fall veranschaulicht, in dem kein Fehler ausgelöst wird:
let
x = try "A"
in
if x[HasError] then x[Error] else x[Value]
// "A"
Im folgenden Beispiel wird gezeigt, wie ein Fehler ausgegeben und anschließend behandelt wird:
let
x = try error "A"
in
if x[HasError] then x[Error] else x[Value]
// [ Reason = "Expression.Error", Message = "A", Detail = null ]
Das obige Beispiel kann mit weniger Syntax umgeschrieben werden, indem eine Catch-Klausel mit einer Catch-Funktion verwendet wird, die einen Parameter akzeptiert:
let
x = try error "A" catch (e) => e
in
x
// [ Reason = "Expression.Error", Message = "A", Detail = null ]
Eine Otherwise-Klausel kann verwendet werden, um Fehler zu ersetzen, die von einem Try-Ausdruck mit einem alternativen Wert verarbeitet werden:
try error "A" otherwise 1
// 1
Eine Catch-Klausel mit einer Nullparameter-Catch-Funktion ist effektiv eine längere, alternative Syntax für eine Otherwise-Klausel:
try error "A" catch () => 1
// 1
Wenn der Fehlerhandler ebenfalls einen Fehler auslöst, dann ist dies ebenfalls beim gesamten Try-Ausdruck der Fall:
try error "A" otherwise error "B"
// error with message "B"
try error "A" catch () => error "B"
// error with message "B"
try error "A" catch (e) => error "B"
// error with message "B"
Fehler in Datensatz- und let-Initialisierern
Das folgende Beispiel zeigt einen Datensatzinitialisier mit dem Feld A
, das einen Fehler auslöst und auf das von den zwei anderen Feldern B
und C
zugegriffen wird. Nicht Feld B
sondern C
behandelt den Fehler, der von A
ausgelöst wird. Das letzte Feld D
greift nicht auf A
zu und wird daher nicht durch den Fehler in A
beeinträchtigt.
[
A = error "A",
B = A + 1,
C = let x =
try A in
if not x[HasError] then x[Value]
else x[Error],
D = 1 + 1
]
Dies ist das Ergebnis der Auswertung des obigen Ausdrucks:
[
A = // error with message "A"
B = // error with message "A"
C = "A",
D = 2
]
Die Fehlerbehandlung in M sollte in der Nähe der Fehlerursache durchgeführt werden, um die Auswirkungen der verzögerten Feldinitialisierungen und Abschlussauswertungen zu behandeln. Das folgende Beispiel zeigt einen fehlgeschlagenen Versuch zur Behandlung eines Fehlers mithilfe eines try
-Ausdrucks:
let
f = (x) => [ a = error "bad", b = x ],
g = try f(42) otherwise 123
in
g[a] // error "bad"
In diesem Beispiel war die Definition g
für die Behandlung des Fehlers gedacht, der beim Aufrufen von f
ausgelöst wurde. Der Fehler wird jedoch von einem Feldinitialisierer ausgelöst, der nur bei Bedarf und somit nach dem Zurückgeben des Datensatzes von f und dem Durchlaufen des try
-Ausdrucks ausgeführt wird.
Nicht implementierter Fehler
Während der Entwicklung eines Ausdrucks empfiehlt es sich, die Implementierung für einige Teile des Ausdrucks auszulassen, sodass die Ausführung des Ausdrucks dann aber dennoch möglich ist. Eine Möglichkeit für diesen Fall besteht darin, einen Fehler für die nicht implementierten Teile auszulösen. Beispiel:
(x, y) =>
if x > y then
x - y
else
error Error.Record("Expression.Error",
"Not Implemented")
Die Auslassungspunkte (...
) können als Kurzform für error
verwendet werden.
Nicht implementierter Ausdruck:
...
Beispielsweise entspricht Folgendes dem vorherigen Beispiel:
(x, y) => if x > y then x - y else ...