Обработка ошибокError Handling

Результат вычисления выражения M дает один из следующих результатов.The result of evaluating an M expression produces one of the following outcomes:

  • Создается одно значение.A single value is produced.

  • Выдается ошибка, что означает, что при вычислении выражения не удалось получить значение.An error is raised, indicating the process of evaluating the expression could not produce a value. Ошибка содержит одно значение записи, которое можно использовать для предоставления дополнительных сведений о том, что привело к неполному вычислению.An error contains a single record value that can be used to provide additional information about what caused the incomplete evaluation.

Ошибки могут возникать в выражении и могут обрабатываться из выражения.Errors can be raised from within an expression, and can be handled from within an expression.

Возникновение ошибокRaising errors

Синтаксис для вызова ошибки выглядит следующим образом:The syntax for raising an error is as follows:

выражение-вызывающее-ошибку:error-raising-expression:
      error выражение      error expression

Текстовые значения можно использовать в качестве сокращений для значений ошибок.Text values can be used as shorthand for error values. Например:For example:

error "Hello, world" // error with message "Hello, world"

Полные значения ошибок — это записи, которые можно создать с помощью функции Error.Record:Full error values are records and can be constructed using the Error.Record function:

error Error.Record("FileNotFound", "File my.txt not found",
     "my.txt")

Выражение выше эквивалентно следующему:The above expression is equivalent to:

error [ 
    Reason = "FileNotFound", 
    Message = "File my.txt not found", 
    Detail = "my.txt" 
]

Возникновение ошибки приводит к остановке вычисления текущего выражения, и стек вычисления выражения остается развернутым до тех пор, пока не произойдет одно из следующего.Raising an error will cause the current expression evaluation to stop, and the expression evaluation stack will unwind until one of the following occurs:

  • Пока не будет достигнуто поле записи, элемент раздела или переменная let — в совокупности —запись.A record field, section member, or let variable—collectively: an entry—is reached. Запись помечается как содержащая ошибку, значение ошибки сохраняется вместе с этой записью, а затем распространяется.The entry is marked as having an error, the error value is saved with that entry, and then propagated. Любой последующий доступ к этой записи приведет к возникновению идентичной ошибки.Any subsequent access to that entry will cause an identical error to be raised. Другие записи в разделе, записи или выражении let не обязательно затрагиваются (за исключением случаев, когда они обращаются к записи, ранее помеченной как содержащую ошибку).Other entries of the record, section, or let expression are not necessarily affected (unless they access an entry previously marked as having an error).

  • Пока не будет достигнуто выражение верхнего уровня.The top-level expression is reached. В этом случае результатом вычисления выражения верхнего уровня является ошибка, а не значение.In this case, the result of evaluating the top-level expression is an error instead of a value.

  • Пока не будет достигнуто выражение try.A try expression is reached. В этом случае ошибка захватывается и возвращается в виде значения.In this case, the error is captured and returned as a value.

Обработка ошибокHandling errors

Для обработки ошибки используется выражение-обработки-ошибок:An error-handling-expression is used to handle an error:

_выражение-обработки-ошибок:_error-handling-expression:
      try защищенное-выражение-предложения-otherwiseнеобязательно
защищенное-выражение:
      выражение
предложение-otherwise:
      try protected-expression otherwise-clauseopt
protected-expression:
      expression
otherwise-clause:

      otherwise выражение-по-умолчанию
выражение-по-умолчанию:
      выражение
      otherwise default-expression
default-expression:
      expression

При вычислении выражения-обработки-ошибок без предложения-otherwise происходит следующее.The following holds when evaluating an error-handling-expression without an otherwiseclause:

  • Если вычисление защищенного-выражения не приводит к ошибке и создает значение x, то значение, формируемое выражением-обработки-ошибок, является записью следующего вида:If the evaluation of the protected-expression does not result in an error and produces a value x, the value produced by the error-handling-expression is a record of the following form:
    [ HasErrors = false, Value = x ]
  • Если вычисление защищенного-выражения вызывает ошибку e, результатом вычисления выражения-обработки-ошибок является запись следующего вида:If the evaluation of the protected-expression raises an error value e, the result of the error-handling-expression is a record of the following form:
    [ HasErrors = true, Error = e ]

При вычислении выражения-обработки-ошибок с предложением-otherwise происходит следующее.The following holds when evaluating an error-handling-expression with an otherwiseclause:

  • Защищенное-выражение должно вычисляться перед предложением-otherwise.The protected-expression must be evaluated before the otherwise-clause.

  • Предложение-otherwise должно вычисляться только в том случае, если при вычислении защищенного-выражения возникает ошибка.The otherwise-clause must be evaluated if and only if the evaluation of the protectedexpression raises an error.

  • Если вычисление защищенного-выражения вызывает ошибку, значением, которое создает выражение-обработки-ошибок, будет результат вычисления предложения-otherwise.If the evaluation of the protected-expression raises an error, the value produced by the error-handling-expression is the result of evaluating the otherwise-clause.

  • Ошибки, возникающие при вычислении предложения-otherwise, распространяются.Errors raised during the evaluation of the otherwise-clause are propagated.

В следующем примере показано выражение-обработки-ошибок в случае отсутствия ошибок:The following example illustrates an error-handling-expression in a case where no error is raised:

let
    x = try "A"
in
    if x[HasError] then x[Error] else x[Value] 
// "A"

В следующем примере показано возникновение ошибки и ее обработка:The following example shows raising an error and then handling it:

let
    x = try error "A" 
in
    if x[HasError] then x[Error] else x[Value] 
// [ Reason = "Expression.Error", Message = "A", Detail = null ]

Предложение otherwise можно использовать для замены ошибок, обрабатываемых выражением try, на альтернативное значение:An otherwise clause can be used to replace errors handled by a try expression with an alternative value:

try error "A" otherwise 1 
// 1

Если предложение otherwise также вызывает ошибку, то и все выражение try также вызывает ошибку:If the otherwise clause also raises an error, then so does the entire try expression:

try error "A" otherwise error "B" 
// error with message "B"

Ошибки в записи и инициализаторы letErrors in record and let initializers

В следующем примере показан инициализатор записи с полем A, которая вызывает ошибку и к которой обращаются два других поля B и C.The following example shows a record initializer with a field A that raises an error and is accessed by two other fields B and C. Поле B не обрабатывает ошибку, вызванную A, однако C обрабатывает ошибку.Field B does not handle the error that is raised by A, but C does. Последнее поле D не имеет доступа к A, поэтому оно не затрагивается ошибкой в A.The final field D does not access A and so it is not affected by the error in 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 
]

Результат вычисления выражения выше будет следующим:The result of evaluating the above expression is:

[ 
    A = // error with message "A" 
    B = // error with message "A" 
    C = "A", 
    D = 2 
]

Обработка ошибок в M должна выполняться близко к причине ошибок, чтобы учесть влияние отложенной инициализации полей и отложенное вычисление закрытий.Error handling in M should be performed close to the cause of errors to deal with the effects of lazy field initialization and deferred closure evaluations. В следующем примере показана неудачная попытка обработки ошибки с помощью выражения try:The following example shows an unsuccessful attempt at handling an error using a try expression:

let
    f = (x) => [ a = error "bad", b = x ],
    g = try f(42) otherwise 123
in 
    g[a]  // error "bad"

В этом примере определение g было предназначено для обработки ошибки, возникшей при вызове f.In this example, the definition g was meant to handle the error raised when calling f. Однако ошибка вызывается инициализатором поля, который выполняется только при необходимости, и поэтому запись была возвращена из f и передана с помощью выражения try.However, the error is raised by a field initializer that only runs when needed and thus after the record was returned from f and passed through the try expression.

Ошибка отсутствия реализацииNot implemented error

Во время разработки выражения автор может захотеть оставить реализацию для некоторых частей выражения, однако ему по-прежнему может потребоваться возможность выполнить выражение.While an expression is being developed, an author may want to leave out the implementation for some parts of the expression, but may still want to be able to execute the expression. Один из способов решения этого сценария — вызвать ошибку для нереализованных частей.One way to handle this case is to raise an error for the unimplemented parts. Например:For example:

(x, y) =>
     if x > y then
         x - y
     else
         error Error.Record("Expression.Error", 
            "Not Implemented")

Символ многоточия (...) можно использовать в качестве ярлыка для error.The ellipsis symbol (...) can be used as a shortcut for error.

выражение-отсутствия-реализации:not-implemented-expression:
      ...      ...

Следующий пример эквивалентен предыдущему:For example, the following is equivalent to the previous example:

(x, y) => if x > y then x - y else ...