错误处理
错误处理
计算M表达式将会产生下面的结果之一:
- 单个值。
- 引发错误用以表示计算表达式时无法生成值。错误包含一个记录值,记录值用于记录无法计算的相关信息。
可以从表达式中引发错误,也可以从其中处理错误。
引发错误
引发错误的语法如下:
error-raising-expression:
error
expression
可以使用多种方式:
- 使用仅提供文本值的简写形式。
// Expression.Error: 错误信息
error "错误信息"
- 使用
Error.Record
或记录进行构造。
/*
错误类型: 错误信息
详细信息:
详细信息
*/
error Error.Record("错误类型","错误信息","详细信息")
// 等效形式
error [
Reason = "错误类型",
Message = "错误信息",
Detail = "详细信息"
]
引发错误将导致计算终止并展开表达式的计算堆栈,直到发生下面的情况之一:
- 已达到记录字段、节成员或let变量(统称为:条目)。该条目被标记为有错误,错误值将与该条目一起保存,然后传播。对该条目后续的访问都将引发相同的错误。记录、节或let表达式的其他条目不一定会受到影响(如果访问被标记为有错误的条目就会受影响)。
- 已到达顶级表达式。这种情况下,顶级表达式的计算结果是一个错误而不是一个值。
- 已到达
try
表达式。这种情况下,将捕获错误并以值的形式返回。
处理错误
使用error-handling-expression处理错误:
error-handling-expression:
try
protected-expression otherwise-clauseopt
protected-expression:
expression
otherwise-clause:
otherwise
default-expression
default-expression:
expression
在不使用otherwise-clause时,存在以下情况:
- 如果计算protected-expression没有生成错误并成功生成值
x
,则由error-handling-expression生成值为以下形式的记录:
[HasErrors = false, Value = x]
- 如果计算protected-expression引发错误
e
,则由error-handling-expression生成值为以下形式的记录:
[HasErrors = true, Error = e]
例如下面的表达式:
let
a = try 1 & "kkk"
in
a
上面的表达式会生成错误:
[
HasError = true,
Error = [
Reason = "Expression.Error",
Message = "无法将运算符 & 应用于类型 Number 和 Text。",
Detail = [
Operator = "&",
Left = 1,
Right = "kkk"
],
Message.Format = "无法将运算符 #{0} 应用于类型 #{1} 和 #{2}。",
Message.Parameters = {"&", "Number", "Text"}
]
]
在使用otherwise-clause时,存在以下情况:
- 必需在计算otherwise-clause之前计算protected-expression。
- 当且仅当protected-expression引发错误时才必须计算otherwise-clause。
- 如果计算protected-expression引发错误,则由error-handling-expression生成的值是计算otherwise-clause产生结果。
- 传播在计算otherwise-clause时引发的错误。
不使用otherwise-clause处理错误:
let
x = try "A"
in
if x[HasError] then x[Error] else x[Value]
// "A"
不使用otherwise-clause处理错误:
let
x = try error "A"
in
if x[HasError] then x[Error] else x[Value]
// [ Reason = "Expression.Error", Message = "A", Detail = null ]
使用otherwise-clause处理错误:
try error "A" otherwise 1
// 1
如果otherwise-clause存在错误,也会引发错误:
try error "A" otherwise error "B"
// error with message "B"
catch关键字
该关键字于2022年5月引入,用法基本上和otherwise相同,只是它后面跟一个函数值。
try 1 & "aa" catch () => 666
try 1 & "aa" otherwise 666
catch
后面可以跟无参或拥有一个参数的函数,区别是当遇到错误时:
- 无参函数:直接执行,此时和
otherwise
没区别,只是由函数计算替代值。 - 一参函数:将错误信息传给该函数。
// 错误:无法将运算符 & 应用于类型 Number 和 Text。
try 1 & "aa" catch (e) => "错误:" & e[Message]
等效形式:
let
a = try 1 & "aa"
in
if a[HasError] then "错误:" & a[Error][Message] else a[Value]
初始化记录和let时的错误
下面的例子演示记录初始化时对于错误的处理:
[
A = error "A", // 产生错误
B = A + 1, // 字段值不计算,此处不影响B,除非访问B
C =
let
x = try A // C会处理A中的错误
in
if not x[HasError] then x[Value] else x[Error],
D = 1 + 1 // D不访问其他字段,不影响
]
[
A = error "A", // 产生错误
B = A + 1, // 字段值不计算,此处不影响B,除非访问B
C = [ // C会处理A中的错误
x = try A
result = if not x[HasError] then x[Value] else x[Error]
][result],
D = 1 + 1 // D不访问其他字段,不影响
]
结果:
[
A = // error "A"
B = // error "A"
C = // 错误记录
D = 2
]
M中的错误处理应尽可能接近错误可能出现的位置,以处理延迟字段初始化和延迟闭包计算的影响。
let
f = (x) => [ a = error "bad", b = x ],
g = try f(42) otherwise 123
in
g[a] // error "bad"
在上面的示例中,g
用于处理调用f
时引发的错误。但是,错误是由字段初始化程序引发的,该初始化程序仅在需要时运行(即从f
返回记录并通过try
表达式传递后)。
未实现的错误
在书写表达式时,如果暂时不会开发某些功能,但仍然希望表达式能够执行。这种情况下,可以使用未实现表达式。
not-implemented-expression:
...
未实现表达式是error
的快捷方式。
...
// 等效形式(提示文本可能不同)
error "未指定值"