Udostępnij za pośrednictwem


Obsługa błędów

Wynik oceny wyrażenia języka M daje jeden z następujących wyników:

  • Jest generowany pojedyncza wartość.

  • Zostanie zgłoszony błąd wskazujący, że proces obliczania wyrażenia nie może wygenerować wartości. Błąd zawiera pojedynczą wartość rekordu, która może służyć do dostarczania dodatkowych informacji o przyczynie niekompletnej oceny.

Błędy mogą być wywoływane z poziomu wyrażenia i mogą być obsługiwane z poziomu wyrażenia.

Zgłaszanie błędów

Składnia zgłaszania błędu jest następująca:

wyrażenie-zgłaszania błędów:
      error wyrażenie

Wartości tekstowe mogą służyć jako skrót wartości błędów. Na przykład:

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

Pełne wartości błędów to rekordy i można je skonstruować przy użyciu Error.Record funkcji :

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

Powyższe wyrażenie jest równoważne:

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

Wywołanie błędu spowoduje zatrzymanie bieżącej oceny wyrażenia, a stos oceny wyrażeń zostanie zatrzymany do momentu wystąpienia jednego z następujących elementów:

  • Osiągnięto pole rekordu, składową sekcji lub zmienną let — zbiorczo: wpis. Wpis jest oznaczony jako o błędzie, wartość błędu jest zapisywana przy użyciu tego wpisu, a następnie propagowana. Każdy kolejny dostęp do tego wpisu spowoduje wystąpienie identycznego błędu. Inne wpisy rekordu, sekcji lub wyrażenia let nie muszą mieć wpływu (chyba że uzyskują dostęp do wpisu wcześniej oznaczonego jako błąd).

  • Zostanie osiągnięte wyrażenie najwyższego poziomu. W takim przypadku wynikiem oceny wyrażenia najwyższego poziomu jest błąd zamiast wartości.

  • Zostanie try osiągnięte wyrażenie. W takim przypadku błąd jest przechwytywany i zwracany jako wartość.

Obsługa błędów

Wyrażenie-obsługi błędów (nieformalnie nazywane wyrażeniem try) jest używane do obsługi błędu:

wyrażenie-obsługi błędów:
      tryopcja obsługibłędów wyrażeń chronionych
wyrażenie-chronione:
      wyrażenie
program obsługi błędów:
      klauzula w przeciwnym razie
      catch-klauzula
w przeciwnym razie klauzula:

      otherwisewyrażenie domyślne
wyrażenie-domyślne:
      wyrażenie
catch-klauzula:
      catchcatch-function
catch-function:
      (parametr-nameopt) => function-body

Podczas oceniania wyrażenia-obsługi-błędów bez procedury obsługi błędów są przechowywane następujące zasady:

  • Jeśli ocena wyrażenia chronionego nie powoduje błędu i generuje wartość x, wartość wygenerowana przez wyrażenie-obsługa-błędów jest rekordem następującej formy:
    [ HasErrors = false, Value = x ]
  • Jeśli ocena wyrażenia chronionego zgłasza wartość błędu e, wynik wyrażenia-obsługi błędów jest rekordem następującego formularza:
    [ HasErrors = true, Error = e ]

Podczas oceniania wyrażenia obsługi błędów przy użyciu programu obsługi błędów są przechowywane następujące zasady:

  • Wyrażenie chronione musi zostać ocenione przed procedurą obsługi błędów.

  • Program obsługi błędów musi zostać oceniony, jeśli i tylko wtedy, gdy ocena wyrażenia chronionego zgłasza błąd.

  • Jeśli ocena wyrażenia chronionego zgłasza błąd, wartość wygenerowana przez wyrażenie-obsługi błędów jest wynikiem oceny procedury obsługi błędów.

  • Błędy zgłaszane podczas oceny programu obsługi błędów są propagowane.

  • Gdy oceniana procedura obsługi błędów jest klauzulą catch-, wywoływana jest funkcja catch-. Jeśli ta funkcja akceptuje parametr, wartość błędu zostanie przekazana jako jego wartość.

Poniższy przykład ilustruje wyrażenie-obsługi błędów w przypadku, gdy nie jest zgłaszany żaden błąd:

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

W poniższym przykładzie pokazano, jak zgłaszać błąd, a następnie obsługiwać go:

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

Powyższy przykład można przepisać z mniejszą składnią przy użyciu klauzuli catch-klauzuli z funkcją catch, która akceptuje parametr:

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

Klauzula-w przeciwnym razie może służyć do zastępowania błędów obsługiwanych przez wyrażenie try wartością alternatywną:

try error "A" otherwise 1 
// 1

Klauzula catch-klauzula z funkcją catch-zero parametrów jest w rzeczywistości dłuższą, alternatywną składnią klauzuli w przeciwnym razie:

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

Jeśli program obsługi błędów zgłasza również błąd, to robi to całe wyrażenie 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"

Błędy w rekordzie i inicjatorach let

W poniższym przykładzie przedstawiono inicjator rekordów z polem A , które zgłasza błąd i jest uzyskiwany przez dwa inne pola B i C. Pole B nie obsługuje błędu zgłoszonego przez Aelement , ale C nie. Ostatnie pole D nie ma dostępu i A nie ma to wpływu na błąd w pliku 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 
]

Wynikiem oceny powyższego wyrażenia jest:

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

Obsługa błędów w języku M powinna być wykonywana blisko przyczyny błędów, aby poradzić sobie z skutkami opóźnionej inicjowania pola i odroczonej oceny zamknięcia. W poniższym przykładzie pokazano nieudaną próbę obsługi błędu przy użyciu try wyrażenia:

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

W tym przykładzie definicja g miała obsługiwać błąd zgłoszony podczas wywoływania metody f. Jednak błąd jest zgłaszany przez inicjator pola, który jest uruchamiany tylko w razie potrzeby, a tym samym po powrocie rekordu try z f i przekazany przez wyrażenie.

Błąd nie zaimplementowany

Podczas opracowywania wyrażenia autor może chcieć pominąć implementację niektórych części wyrażenia, ale nadal może chcieć wykonać wyrażenie. Jednym ze sposobów obsługi tego przypadku jest zgłoszenie błędu dla nieimplementowanych części. Na przykład:

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

Symbol wielokropka (...) może służyć jako skrót dla elementu error.

wyrażenie-nie implementowane:
      ...

Na przykład następujące elementy są równoważne poprzedniemu przykładowi:

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