錯誤處理
評估 M 運算式的結果會產生下列其中一種結果:
產生單一值。
「引發錯誤」,表示評估運算式的過程無法產生值。 錯誤包含單一記錄值,其可用來提供評估為何不完整的其他資訊。
錯誤可以從運算式內引發,且可以從運算式內處理。
引發錯誤
引發錯誤的語法如下:
error-raising-expression:
error
運算式
文字值可以用來當作錯誤值的縮寫。 例如:
error "Hello, world" // error with message "Hello, world"
完整錯誤值是記錄,且可以使用 Error.Record
函式來建構:
error Error.Record("FileNotFound", "File my.txt not found",
"my.txt")
上述運算式等同於:
error [
Reason = "FileNotFound",
Message = "File my.txt not found",
Detail = "my.txt"
]
引發錯誤會導致目前的運算式評估停止,且運算式評估堆疊會回溯,直到發生下列其中一種情況為止:
已達到記錄欄位、區段成員或 let 變數,統稱為入口。 入口標示為發生錯誤,錯誤值會與該入口一起儲存,然後傳播。 對該入口的任何後續存取都會導致引發相同的錯誤。 記錄、區段或 let 運算式的其他入口不一定會受到影響 (除非它們存取先前標示為有錯誤的入口)。
達到最上層運算式。 在此情況下,評估最上層運算式的結果會是錯誤,而不是值。
達到
try
運算式。 在此情況下,會捕獲錯誤並以值的形式傳回。
處理錯誤
error-handling-expression (非正式地稱為「try 運算式」) 用於處理錯誤:
error-handling-expression:
try
protected-expression error-handleropt
protected-expression:
expression
error-handler:
otherwise-clause
catch-clause
otherwise-clause:
otherwise
default-expression
default-expression:
expression
catch-clause:
catch
catch-function
catch-function:
(
parameter-nameopt)
=>
function-body
不使用 error-handler 評估 error-handling-expression 時,會發生下列情況:
- 如果評估 protected-expression 未造成錯誤,且產生值 x,則 error-handling-expression 所產生值是下列形式的記錄:
[ HasErrors = false, Value = x ]
- 如果評估 protected-expression 引發錯誤值 e,則 error-handling-expression 結果是下列形式的記錄:
[ HasErrors = true, Error = e ]
使用 error-handler 評估 error-handling-expression 時,會發生下列情況:
protected-expression 必須在 error-handler 之前評估。
如果評估 protected-expression 引發錯誤,且唯有在這種情況下,才必須評估 error-handler。
如果評估 protected-expression 引發錯誤,則 error-handling-expression 所產生值是評估 error-handler 的結果。
評估 error-handler 期間所引發的錯誤會進行傳播。
當正在評估的 error-handler 是 catch-clause 時,就會叫用 catch-function。 如果該函式接受參數,則會將錯誤值當作其值傳遞。
下列範例說明在沒有引發錯誤時的 error-handling-expression:
let
x = try "A"
in
if x[HasError] then x[Error] else x[Value]
// "A"
下列範例會顯示引發錯誤,然後加以處理:
let
x = try error "A"
in
if x[HasError] then x[Error] else x[Value]
// [ Reason = "Expression.Error", Message = "A", Detail = null ]
使用 catch-clause 搭配可接受參數的 catch-function,即可使用較少的語法來重新撰寫上述範例:
let
x = try error "A" catch (e) => e
in
x
// [ Reason = "Expression.Error", Message = "A", Detail = null ]
otherwise-clause 可用於將 try 運算式所處理的錯誤取代為替代值:
try error "A" otherwise 1
// 1
包含零參數 catch-function 的 catch-clause 實際上是 otherwise-clause 的較長替代語法:
try error "A" catch () => 1
// 1
如果 error-handler 也引發錯誤,則整個 try 運算式也會引發錯誤:
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"
記錄和 let 初始設定式中的錯誤
下列範例顯示記錄初始設定式,其中包含引發錯誤的欄位 A
,且由兩個其他欄位 B
和 C
存取。 欄位 B
不會處理 A
所引發的錯誤,但 C
會處理。 最後一個欄位 D
不會存取 A
,因此不會受到 A
中的錯誤所影響。
[
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
]
評估上述運算式的結果為:
[
A = // error with message "A"
B = // error with message "A"
C = "A",
D = 2
]
M 中的錯誤處理執行時應該接近錯誤原因,以處理延遲欄位初始化和延遲關閉評估的影響。 下列範例顯示使用 try
運算式來處理錯誤的失敗嘗試:
let
f = (x) => [ a = error "bad", b = x ],
g = try f(42) otherwise 123
in
g[a] // error "bad"
在此範例中,定義 g
的目的是要處理在呼叫 f
時所引發錯誤。 不過,此錯誤是由只在必要時執行的欄位初始設定式所引發,因此會在從 f 傳回記錄,並透過 try
運算式傳遞之後。
未實作錯誤
在開發運算式時,作者可能會想要省略運算式某些部分的實作,但可能仍然想要能夠執行運算式。 處理這種情況的其中一種方式是針對未實作部分引發錯誤。 例如:
(x, y) =>
if x > y then
x - y
else
error Error.Record("Expression.Error",
"Not Implemented")
省略號符號 (...
) 可用作 error
的捷徑。
not-implemented-expression:
...
例如,下列等同於先前的範例:
(x, y) => if x > y then x - y else ...