Tratamento de erros

O resultado da avaliação de uma expressão do M produz um dos seguintes resultados:

  • Apenas um valor é produzido.

  • Um erro é gerado, indicando que o processo de avaliação da expressão não pôde produzir um valor. Um erro contém um valor de registro que pode ser usado para fornecer informações adicionais sobre o que causou a avaliação incompleta.

Os erros podem ser gerados de dentro de uma expressão e podem ser manipulados de dentro de uma expressão.

Como gerar erros

A sintaxe para gerar um erro é a seguinte:

expressão-para-geração-de-erros:
      errorexpressão

Valores de texto podem ser usados como abreviação para valores de erro. Por exemplo:

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

Os valores de erro completos são registros e podem ser construídos usando a função Error.Record:

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

A expressão acima é equivalente a:

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

Gerar um erro fará com que a avaliação da expressão atual pare e a pilha de avaliação da expressão se desenrole até que uma das seguintes situações ocorra:

  • Um campo de registro, um membro de seção ou uma variável let (coletivamente: uma entrada) é alcançado. A entrada está marcada como tendo um erro, o valor do erro é salvo com essa entrada e, em seguida, propagada. Qualquer acesso subsequente a essa entrada fará com que um erro idêntico seja gerado. Outras entradas do registro, da seção ou da expressão let não serão necessariamente afetadas (a menos que acessem uma entrada marcada anteriormente como tendo um erro).

  • A expressão de nível superior é alcançada. Nesse caso, o resultado da avaliação da expressão de nível superior é um erro em vez de um valor.

  • Uma expressão try é alcançada. Nesse caso, o erro é capturado e retornado como um valor.

Gerenciando erros

Uma error-handling-expression (informalmente conhecida como "expressão try") é usada para lidar com um erro:

expressão-de-tratamento-de-erro:
      tryprotected-expression error-handleropt
expressão-protegida:
      expressão
error-handler:
      otherwise-clause
      catch-clause
cláusula-otherwise:

      otherwisedefault-expression
default-expression:
      expressão
catch-clause:
      catchcatch-function
catch-function:
      (parameter-nameopt)=>function-body

Os seguintes preceitos serão válidos ao avaliar um error-handling-expression sem um error-handler:

  • Se a avaliação de protected-expression não resultar em um erro e produzir um valor x, o valor produzido por error-handling-expression será um registro com o seguinte formato:
    [ HasErrors = false, Value = x ]
  • Se a avaliação de protected-expression gerar um valor de erro e o resultado de error-handling-expression for um registro com o seguinte formato:
    [ HasErrors = true, Error = e ]

Os seguintes preceitos serão válidos ao avaliar um error-handling-expression com um error-handler:

  • O protected-expression precisa ser avaliada antes de error-handler.

  • O error-handler precisará ser avaliado se e somente se a avaliação de protected-expression gerar um erro.

  • Se a avaliação da protected-expression gerar um erro, o valor produzido por error-handling-expression será o resultado da avaliação de error-handler.

  • Erros gerados durante a avaliação de error-handler são propagados.

  • Quando o error-handler que está sendo avaliado é uma catch-clause, o catch-function é invocado. Se essa função aceitar um parâmetro, o valor de erro será passado como seu valor.

O seguinte exemplo ilustra uma expressão-de-tratamento-de-erro em um caso em que nenhum erro é gerado:

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

Este exemplo mostra como gerar um erro e tratá-lo:

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

O exemplo anterior pode ser reescrito com menos sintaxe usando um catch-clause com um catch-function que aceita um parâmetro:

let
    x = try error "A" catch (e) => e
in
    x
// [ Reason = "Expression.Error", Message = "A", Detail = null ]

Um otherwise-clause pode ser usado para substituir erros gerenciados por uma expressão try com um valor alternativo:

try error "A" otherwise 1 
// 1

Um catch-clause com um catch-function parâmetro zero é efetivamente uma sintaxe alternativa mais longa para uma otherwise-clause:

try error "A" catch () => 1 
// 1

Se error-handler também gerar um erro, a expressão try inteira fará o mesmo:

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"

Erros no registro e inicializadores let

O exemplo a seguir mostra um inicializador de registro com um campo A que gera um erro e é acessado por dois outros campos, B e C. O campo B não gerencia o erro que é gerado pelo A, mas C o gerencia. O campo final D não acessa A e, portanto, não é afetado pelo erro em 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 
]

O resultado da avaliação da expressão acima é:

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

O tratamento de erro em M deve ser executado próximo à causa de erros para lidar com os efeitos da inicialização de campo lenta e das avaliações de fechamento adiadas. O seguinte exemplo mostra uma tentativa malsucedida de tratar um erro usando uma expressão try:

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

Neste exemplo, a definição g foi destinada a tratar o erro gerado ao chamar f. No entanto, o erro é gerado por um inicializador de campo que só é executado quando necessário. Assim, isso só ocorrerá após o registro ter sido retornado de f e passado pela expressão try.

Erro "não implementado"

Enquanto uma expressão está sendo desenvolvida, um autor pode querer não incluir a implementação para algumas partes da expressão, mas ainda pode querer ser capaz de executar essa expressão. Uma forma de lidar com esse caso é gerar um erro para as partes não implementadas. Por exemplo:

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

O símbolo de reticências (...) pode ser usado como um atalho para error.

expressão-não-implementada:
      ...

Por exemplo, o seguinte é equivalente ao exemplo anterior:

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