Share via


Tratamiento de errores

El resultado de la evaluación de una expresión de M genera uno de los resultados siguientes:

  • Se genera un solo valor.

  • Se genera un error, lo que indica que el proceso de evaluación de la expresión no ha podido generar un valor. Un error contiene un valor de registro único que se puede usar para proporcionar información adicional sobre la causa de la evaluación incompleta.

Los errores se pueden generar desde una expresión y se pueden controlar desde una expresión.

Generación de errores

La sintaxis para generar un error es la siguiente:

error-raising-expression:
      errorexpression

Los valores de texto se pueden usar como abreviaturas para los valores de error. Por ejemplo:

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

Los valores de error completos son registros y se pueden construir con la función Error.Record:

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

La expresión anterior es equivalente a:

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

La generación de un error detendrá la evaluación de la expresión actual y la pila de evaluación de la expresión se desenredará hasta que se produzca uno de los siguientes casos:

  • Se alcanza un campo de registro, un miembro de sección o una variable let (colectivamente, una entrada). La entrada se marca como con un error, el valor de error se guarda con esa entrada y, después, se propaga. Cualquier acceso posterior a esa entrada hará que se produzca un error idéntico. Otras entradas del registro, la sección o la expresión let no se ven afectadas necesariamente (a menos que accedan a una entrada marcada previamente como con error).

  • Se alcanza la expresión de nivel superior. En este caso, el resultado de la evaluación de la expresión de nivel superior es un error en lugar de un valor.

  • Se alcanza una expresión try. En este caso, el error se captura y se devuelve como un valor.

Control de errores

Una expresión de control de errores (conocida informalmente como "try expression") se usa para controlar un error:

error-handling-expression:
      tryprotected-expression error-handleropt
protected-expression:
      expresión
error-handler:
      otherwise-clause
      catch-clause
otherwise-clause:

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

Al evaluar una expresión de control de errores sin un controlador de errores, sucede lo siguiente:

  • Si la evaluación de la expresión protegida no produce un error y genera un valor x, el valor generado por la expresión de control de errores es un registro con el formato siguiente:
    [ HasErrors = false, Value = x ]
  • Si la evaluación de la expresión protegida genera un error, el resultado de la expresión de control de errores es un registro con el formato siguiente:
    [ HasErrors = true, Error = e ]

Al evaluar una expresión de control de errores con un controlador de errores, sucede lo siguiente:

  • Se debe evaluar la expresión protegida antes que el controlador de errores.

  • Se debe evaluar el controlador de errores solo si la evaluación de la expresión protegida genera un error.

  • Si la evaluación de la expresión protegida genera un error, el valor generado por la expresión de control de errores es el resultado de la evaluación del controlador de errores.

  • Se propagan los errores generados durante la evaluación del controlador de errores.

  • Cuando el controlador de errores que se evalúa es una cláusula catch, se invoca la función catch. Si esa función acepta un parámetro, el valor de error se pasará como su valor.

En el ejemplo siguiente se muestra una expresión de control de errores en un caso en el que no se produce ningún error:

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

En el ejemplo siguiente se muestra cómo generar un error y después controlarlo:

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

El ejemplo anterior se puede reescribir con menos sintaxis mediante una cláusula catch con una función catch que acepta un parámetro:

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

Se puede usar una cláusula otherwise para reemplazar los errores controlados por una expresión try con un valor alternativo:

try error "A" otherwise 1 
// 1

Una cláusula catch con una función catch de parámetro cero es eficazmente una sintaxis alternativa más larga para una cláusula otherwise:

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

Si el controlador de errores también produce un error, lo mismo sucede con la expresión try completa:

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"

Errores en registros e inicializadores let

En el ejemplo siguiente se muestra un inicializador de registros con un campo A que genera un error y al que acceden otros dos campos B y C. El campo B no controla el error generado A, pero C sí lo hace. El último campo D no accede a A y, por tanto, no se ve afectado por el error en 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 
]

El resultado de evaluar la expresión anterior es el siguiente:

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

En M, el control de errores se debe realizar cerca de la causa de los errores para tratar los efectos de la inicialización diferida de campos y las evaluaciones de cierre en diferido. En el ejemplo siguiente se muestra un intento incorrecto de control de un error mediante una expresión try:

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

En este ejemplo, la definición g estaba pensada para controlar el error que se produce al llamar a f. Pero el error lo genera un inicializador de campo que solo se ejecuta cuando es necesario y, por tanto, después de que el registro se haya devuelto desde f y se haya pasado a través de la expresión try.

Error no implementado

Mientras se desarrolla una expresión, es posible que un autor quiera dejar de usar la implementación en algunas partes de la expresión, pero que pueda seguir ejecutándola. Una manera de controlar este caso es generar un error para las partes no implementadas. Por ejemplo:

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

El símbolo de puntos suspensivos (...) se puede usar como método abreviado para error.

not-implemented-expression:
      ...

Por ejemplo, el ejemplo siguiente es equivalente al anterior:

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