🤩

Value-Based Error Handling Mechanisms

2021/10/05に公開

追記 2024-03-07

OTP-27.0-RC1 がリリースされ maybe を利用する場合には特に設定が不要になりました

追記 2023-03-31

OTP-26.0-RC2 がリリースされ maybe を利用する場合は -feature(maybe_expr, enable). を指定することをオススメします。

OTP-27.0 で maybe は何も指定せずに利用できるようになります。

追記 2022-02-21

OTP-25.0-RC1 がリリースされ maybe が利用可能になりました。利用するには {enable_feature, maybe_expr} を有効にする必要があります。

commit_write(OpaqueData) ->
    maybe
        ok ?= disk_log:sync(OpaqueData#backup.file_desc),
        ok ?= disk_log:close(OpaqueData#backup.file_desc),
        ok ?= file:rename(OpaqueData#backup.tmp_file, OpaqueData#backup.file),
        {ok, OpaqueData#backup.file}
    else
        error ->
            {error, invalid_write};
        {error, Reason} ->
            {error, Reason}
    end.

追記 2021-11-17

<~ から ?= に変更されました。

仕様が確定し、最終実装のドラフトがでました。

Implement EEP 49 (Value-Based Error Handling Mechanisms) by bjorng · Pull Request #5405 · erlang/otp

上記 PR をビルド、その後  BRAM ビルド時のコンパイルオプションに {enable_feature, maybe_expr} を指定することで、 maybe 構文が利用できるようになった。

追記 2021-11-03

リスト/バイナリ内包表記で利用されている <- を使うのはやめて、 <- から <~ に変更されました。

Update eep-0049 with decisions from OTP Technical Board by bjorng · Pull Request #35 · erlang/eep

それを踏まえた OTP チームからの実装が出てきました。

Implement the compiler part of EEP 49 by bjorng · Pull Request #5340 · erlang/otp

本編

maybe ... end 構文の提案が進められています。

Erlang は素敵な文法ではないため case ... of だとネストの嵐になります。

commit_write(OpaqueData) ->
    B = OpaqueData,
    case disk_log:sync(B#backup.file_desc) of
        ok ->
            case disk_log:close(B#backup.file_desc) of
                ok ->
                    case file:rename(B#backup.tmp_file, B#backup.file) of
                       ok ->
                            {ok, B#backup.file};
                       {error, Reason} ->
                            {error, Reason}
                    end;
                {error, Reason} ->
                    {error, Reason}
            end;
        {error, Reason} ->
            {error, Reason}
    end.

これが以下のように書けるようになります。

commit_write(OpaqueData) ->
    maybe
        ok ?= disk_log:sync(OpaqueData#backup.file_desc),
        ok ?= disk_log:close(OpaqueData#backup.file_desc),
        ok ?= file:rename(OpaqueData#backup.tmp_file, OpaqueData#backup.file),
        {ok, OpaqueData#backup.file}
    end.
commit_write(OpaqueData) ->
    maybe
        ok ?= disk_log:sync(OpaqueData#backup.file_desc),
        ok ?= disk_log:close(OpaqueData#backup.file_desc),
        ok ?= file:rename(OpaqueData#backup.tmp_file, OpaqueData#backup.file),
        {ok, OpaqueData#backup.file}
    else
        {error, Reason} -> {error, Reason}
    end.

提案は EEP 49: Value-Based Error Handling Mechanisms で確認することができます。しっかりと書かれているので、一読する価値はあります。

そして実装については EEP-49: First proof of concept implementation by ferd · Pull Request #5216 · erlang/otp で確認することが可能です。

おまけとして parse_transform を利用して同じような仕組みを実現していた rabbitmq/erlando: Erlando があります。これ以前、使っていたんですが凄く便利でした。

何にせよ、可読性は高くなりつつも、綺麗に書けるようになる文法提案だと思っています。OTP-25 への導入に向けて進めているようなので、とても楽しみです。

Discussion