Elixirのすべてのエラーは、_例外ビヘイビア_を実装しています。_アクセスビヘイビア_と同じように、_例外ビヘイビア_は、ビヘイビアというソフトウェア上の契約を満たすためにモジュールが実装しなければならないコールバック関数を定義します。エラーを定義すると、次のような性質を持つようになります:
:messageフィールドがあります。raise/1とraise/2でそのモジュールを使い、目的のエラーを発生させることができます。_例外ビヘイビア_は、message/1とexception/1という2つのコールバックも指定します。実装しなかった場合は、デフォルトの実装が使われます。message/1は、raiseから呼ばれると、エラー構造体を読みやすいメッセージに変換します。exception/1は、raise/2から呼ばれると、メッセージに追加の文脈を加えられるようにします。
エラーモジュールから例外を定義するには、defexceptionマクロを使います:
# Defines a minimal error, with the name `MyError`
defmodule MyError do
defexception message: "error"
end
# Defines an error with a customized exception/1 function
defmodule MyCustomizedError do
defexception message: "custom error"
@impl true
def exception(value) do
case value do
[] ->
%MyCustomizedError{}
_ ->
%MyCustomizedError{message: "Alert: " <> value}
end
end
end
定義したエラーは、組み込みのエラーと同じように、raise/1またはraise/2を使って発生させられます。
raise/1は、モジュール名を指定して特定のエラーを発生させます。引数が文字列の場合は、その文字列をメッセージとするRuntimeErrorを発生させます。raise/2は、モジュール名を指定して特定のエラーを発生させます。属性の引数を受け取り、それを使って適切なメッセージを持つエラーを取得します。_Instruments of Texas_での仕事を続けるなか、RPN電卓のElixir実装が進んでいます。チームは、標準ライブラリが提供する一般的なエラーよりも具体的なエラーを発生させられるようにしたいと考えています。まだ調査中の部分もありますが、_Exception Behaviour_を実装する新しいエラーを2つ作ることにしました。
数値をゼロで割ると結果は未定義になり、チームはこれをエラーで表すのが最適だと判断しました。
DivisionByZeroErrorモジュールを実装し、エラーメッセージを"division by zero occurred"にします。
raise DivisionByZeroError
# => ** (DivisionByZeroError) division by zero occurred
RPN電卓は、計算する前の数値を保持しておくために_スタック_を使います。チームはこの_スタック_を数値(整数と浮動小数点数)の配列で表します。例えば[3, 4.0]です。各演算では、計算を行うために、スタック上に決まった個数の数値が必要です。スタック上の数値が足りないとき、これをスタックアンダーフローエラーと呼びます。デフォルトのメッセージと、省略可能な追加のコンテキストを提供するStackUnderflowError例外を実装します。
raise StackUnderflowError
# => ** (StackUnderflowError) stack underflow occurred
raise StackUnderflowError, "when dividing"
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing
スタック_(2つの数値の配列)_を引数に取るdivide/1関数を実装します。この関数は次の処理を行います。
RPNCalculator.Exception.divide([])
# => ** (StackUnderflowError) stack underflow occurred, context: when dividing
RPNCalculator.Exception.divide([0, 100])
# => ** (DivisionByZeroError) division by zero occurred
RPNCalculator.Exception.divide([4, 16])
# => 4
配列の順序が逆になっている点に注意してください!