エラー処理

M 式の評価結果は、次の結果のうちいずれかとなります。

  • 1 つの値が生成される。

  • "エラーが発生する"。式を評価するプロセスで値が生成できなかったことを示します。 エラーには、不完全な評価の原因に関する追加情報を提供するために使用できる 1 つのレコード値が含まれています。

エラーは、式内から発生し、式内から処理できます。

エラーの発生

エラーが発生する構文は次のとおりです。

error-raising-expression:
      error

テキスト値は、エラー値の短縮形として使用できます。 例:

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

完全なエラー値はレコードであり、Error.Record 関数を使用して作成できます。

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

上の式は、次の式と同じです。

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

エラーを発生させると、現在進行中の式の評価が停止し、次のいずれかの状況が発生するまで、式の評価スタックがアンワインドされます。

  • 総称して entry と呼ばれるレコード フィールド、セクション メンバー、または let 変数に到達する。 そのエントリは、エラーが発生しているものとしてマークされ、エラー値はそのエントリと共に保存され、その後伝達されます。 そのエントリへの以降のアクセスでは、同じエラーが発生します。 レコード、セクション、または let 式のその他のエントリは必ずしも影響を受けません (エラーが発生しているものとしてマークされたエントリにアクセスする場合を除きます)。

  • 最上位レベルの式に到達する。 この場合、最上位レベルの式を評価した結果は、値ではなくエラーになります。

  • try 式に到達する。 この場合、エラーがキャプチャされ、値として返されます。

エラーの処理

エラーを処理するために、error-handling-expression が使用されます。

_error-handling-expression:
      tryprotected-expression otherwise-clauseopt
protected-expression:
      expression
otherwise-clause:

      otherwisedefault-expression
default-expression:
      expression

otherwiseclause を使用せずに error-handling-expression を評価するときは、次が当てはまります。

  • protected-expression の評価でエラーが発生せず、値 x が生成される場合、error-handling-expression によって生成される値は、次の形式のレコードになります。
    [ HasErrors = false, Value = x ]
  • protected-expression の評価でエラー値 e が発生する場合、error-handling-expression の結果は、次の形式のレコードになります。
    [ HasErrors = true, Error = e ]

otherwiseclause を使用して error-handling-expression を評価するときは、次が当てはまります。

  • protected-expression は、otherwise-clause の前に評価する必要があります。

  • otherwise-clause は、protected-expression の評価でエラーが発生する場合にのみ評価する必要があります。

  • protected-expression の評価でエラーが発生する場合、error-handling-expression によって生成された値が、otherwise-clause の評価の結果となります。

  • otherwise-clause の評価中に発生したエラーが伝達されます。

次の例は、エラーが発生していない場合の error-handling-expression を示しています。

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

次の例は、エラー発生とその処理について示しています。

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

otherwise 句を使用して、try 式で処理されたエラーを別の値に置き換えることができます。

try error "A" otherwise 1 
// 1

otherwise 句でもエラーが発生する場合は、try 式全体が実行されます。

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

レコードと let 初期化子のエラー

次の例は、エラーが発生しており、他の B フィールドと C フィールドによってアクセスされるフィールド A を持つレコード初期化子を示しています。 フィールド B では、A で発生したエラーは処理されませんが、C では処理されます。 最終フィールド D は、A にアクセスしないため、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 
]

上の式の評価結果は次のようになります。

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

M でのエラー処理は、レイジー フィールド初期化と遅延クロージャ評価の影響に対処するため、エラーの原因に近い状態で実行する必要があります。 次の例では、try 式を使用したエラー処理の試行が失敗したことを示しています。

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

この例では、定義 g は、f の呼び出し時に発生したエラーを処理することを意図しています。 ただし、このエラーは、必要な場合にのみ、すなわちレコードが f から返され、try 式で渡された後に実行されたフィールド初期化子によって発生します。

未実装のエラー

式の開発中、作成者は、式の一部を実装しないままにしておきながら、式を実行可能にする必要がある場合があります。 このケースを処理するには、実装されていない部分に対してエラーを発生させるという方法があります。 例:

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

省略記号 (...) は、error のショートカットとして使用できます。

not-implemented-expression:
      ...

たとえば、次の例は、上の例と同じです。

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