erg/doc/JA/syntax/30_error_handling.md
Shunsuke Shibayama 96132b20f6 initial commit
2022-08-10 23:02:27 +09:00

4.7 KiB

エラーハンドリングシステム

主にResult型を使用する。 ErgではError型オブジェクトを捨てる(トップレベルで対応しない)とエラーが発生する。

例外、Pythonとの相互運用

Ergは例外機構(Exception)を持たない。Pythonの関数をインポートする際は

  • 戻り値をT or Error型とする
  • T or Panic型(実行時エラーを出す可能性がある)とする

の2つの選択肢があり、pyimportではデフォルトで後者となる。前者としてインポートしたい場合は、 pyimportexception_typeErrorを指定する(exception_type: {Error, Panic})。

例外とResult型

Result型はエラーかもしれない値を表現する。Resultによるエラーハンドリングはいくつかの点で例外機構よりも優れている。 まず第一に、サブルーチンがエラーを出すかもしれないと型定義から分かり、実際に使用するときも一目瞭然なのである。

# Python
try:
    x = foo().bar()
    y = baz()
    qux()
except e:
    print(e)

上の例では、例外がどの関数から送出されたものなのか、このコードだけでは分からない。関数定義まで遡っても、その関数が例外を出すかは判別しにくい。

# Erg
try!:
    do!:
        x = foo!()?.bar()
        y = baz!()
        qux!()?
    e =>
        print! e

翻って、こちらの例ではfoo!qux!がエラーを出しうるとわかる。 正確にはyResult型である可能性があるが、中の値を使用するためにはいずれ対処しなくてはならない。

Result型を使用するメリットはそれだけではない。Result型はスレッドセーフでもある。これは、エラー情報を並列実行中に(容易に)受け渡しできるということを意味する。

Context

Error/Result型単体では副作用が発生しないので、例外と違い送出場所などの情報(Context、文脈)を持てないが、.contextメソッドを使えばErrorオブジェクトに情報を付加できる。.contextメソッドはErrorオブジェクト自身を消費して新しいErrorオブジェクトを作るタイプのメソッドである。チェイン可能であり、複数のコンテクストを保持できる。

f() =
    todo() \
        .context "to be implemented in ver 1.2" \
        .context "and more hints ..."

f()
# Error: not implemented yet
# hint: to be implemented in ver 1.2
# hint: and more hints ...

なお、.msg.kindなどのErrorの属性は副次的なものではないのでcontextではなく、最初に生成されたときのまま上書きできない。

スタックトレース

Result型はその利便性から他言語でも多く取り入れられているが、例外機構と比較してエラーの発生元がわかりにくくなるというデメリットがある。 そこで、ErgではErrorオブジェクトに.stackという属性を持たせており、擬似的に例外機構のようなスタックトレースを再現している。 .stackは呼び出し元オブジェクトの配列である。Errorオブジェクトはreturn(?によるものも含む)されるたびにその呼出元サブルーチンを.stackに積んでいく。 そしてreturnができないコンテクストで?されるなり.unwrapされるなりすると、トレースバックを表示しながらパニックする。

f x =
    ...
    y = foo.try_some(x)?
    ...

g x =
    y = f(x)?
    ...

i = g(1)?
# Traceback (most recent call first):
#    ...
#    Foo.try_some, line 10, file "foo.er"
#    10 | y = foo.try_some(x)?
#    module::f, line 23, file "foo.er"
#    23 | y = f(x)?
#    module::g, line 40, file "foo.er"
#    40 | i = g(1)?
# Error: ...

パニック

Ergには回復不能なエラーへの対処として パニッキング という機構も存在する。 回復不能なエラーとは、例えばソフト/ハードウェアの不具合など外的要因によるエラーや、それ以上コードを実行し続けても意味がないほど致命的なエラー、あるいはプログラム作成者の想定だにしないエラーなどである。これが発生した場合、プログラマの努力によって正常系に復帰させることができないため、その場でプログラムを終了させる。これを「パニックさせる」という。

パニックはpanic関数で行う。

panic "something went wrong!"

Previous | Next