错误处理
计算 M 表达式的结果将生成以下结果之一:
生成单个值。
引发错误指示计算表达式的过程不能生成值 。 错误包含单个记录值,可用于提供有关导致不完整计算的原因的其他信息。
可从表达式中引发错误,也可从其中处理错误。
引发错误
引发错误的语法如下所示:
error-raising-expression:
error
expression
文本值可用作错误值的简写形式。 例如:
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:
表达式
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 时,存在以下情况:
必须在 error-handler 之前计算 protected-expression。
当且仅当计算 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 ...