Elixir Schoolの勉強メモです。
一般的な規則
- 関数の通常の操作の一部であるエラー(ユーザーが間違った入力をした場合など)の場合、関数はそれに応じて
{:ok, result}
と{:error, reason}
を返す - 通常の操作の一部では無いエラー(構成データを解析できないなど)の場合は、例外をスローする。
通常、標準エラーはパターンマッチで処理する。
エラーハンドリング
raise/1
やraise/2
でエラーを発生させることができる。
iex(4)> raise "Oh no!"
** (RuntimeError) Oh no!
iex(4)> raise ArgumentError, message: "the argument value is invalid"
** (ArgumentError) the argument value is invalid
エラーが起きることが分かっている場合、try/reduce
というパターンマッチを使ってエラーハンドリングできる。
try do
raise "Oh no!"
rescue
e in RuntimeError -> IO.puts("An error occurred: " <> e.message)
end
reduce節で複数のエラーにマッチさせることができる。
After
エラーの有無に関わらず、tru/reduce
の後に何らかの処理が必要とする場合、
try/after
が存在する。
iex(5)> try do
...(5)> raise "Oh no!"
...(5)> rescue
...(5)> e in RuntimeError -> IO.puts("An error occurred: " <> e.message)
...(5)> after
...(5)> IO.puts "The end!"
...(5)> end
An error occurred: Oh no!
The end!
:ok
これはファイルや接続が必ず閉じられなければならない場合に最もよく使われる。
新しいエラー
モジュールを作ることで独自の例外を定義することができる。
defexception
を使って、例外のいろいろなフィールドをデフォルト値とともに定義できる。
iex(7)> defmodule ExampleError do
...(7)> defexception message: "an example error has occurred"
...(7)> end
iex(8)> try do
...(8)> raise ExampleError
...(8)> rescue
...(8)> e in ExampleError -> e
...(8)> end
%ExampleError{message: "an example error has occurred"}
Throw
throw/1
関数を使えば、catch
できる特定の値で実行を中断できる。
iex(9)> try do
...(9)> for x <- 0..10 do
...(9)> if x == 5, do: throw(x)
...(9)> IO.puts(x)
...(9)> end
...(9)> catch
...(9)> x -> "Caught: #{x}"
...(9)> end
0
1
2
3
4
"Caught: 5"
throw/catch
はかなり珍しく、概してライブラリが十分なAPIを提供していない場合の間に合わせとして存在する。
終了
Elixirが提供している最後のエラー処理機構はexit
。
終了シグナルはプロセスが死に、それがElixirのフォールトトレランスの重要な部分であるときに発せられる。
明示的に終了するにはexit/1
を使う。
iex(10)> spawn_link fn -> exit("oh no") end
** (EXIT from #PID<0.106.0>) shell process exited with reason: "oh no"
try/catch
で終了を補足できるが、そうすることは非常にまれ。
ほとんど全ての場合ではsupervisiorにプロセスの終了をハンドリングさせるほうが都合がよい。
iex(1)> try do
...(1)> exit "oh no!"
...(1)> catch
...(1)> :exit, _ -> "exit blocked"
...(1)> end
"exit blocked"