Erlang: success case and error handling as a transaction

(I want to give another question as a reference: How can I elegantly check many conditions in Erlang? )

The general form of “success code separated from error handling” is as follows:

try
    ok = do_x(),
    ...
    ok = do_y()
catch
    error:{badmatch, x_failure} -> do_something1();
    ...
    error:{badmatch, y_failure} -> do_something2();

How to use this template when functions in a try clause do something with a side effect, for example, write a file, send a network packet, write a string to a database, etc.? Is there a common pattern for rollback in a catch clause? Example:

try
    %How to make this whole block a transaction?
    ok = step_1_write_file(),
    ok = step_2_write_database(),
    ok = step_3_send_packet(),
    ...
catch
    error:{badmatch, database_failure} -> clean_up_step_1() %delete file?
    error:{badmatch, sendpacket_failure} -> clean_up_step_1_and_2() %??

It seems that error handling becomes burdensome when the cleanup to be performed depends on the step in the block trythat failed.

, , try, , `` '?

+4
1

, "" , , "" .

, :

noop() -> ok.

transaction([{Fun, Rollback} | Rest]) ->
    try
        {ok, Result} = Fun(),
        [Result | transaction(Rest)]
    catch Type:Reason ->
        Rollback(),
        erlang:raise(Type, Reason, erlang:get_stacktrace())
    end;
transaction([Fun | Rest]) ->
    % not every action require cleanup on error
    transaction([{Fun, fun noop/0} | Rest]);
transaction([]) -> [].


main() ->
    Actions = [
       {fun write_file/0, fun cleanup_file/0},
       {fun write_database/0, fun cleanup_database/0},
       fun do_safe_thing/0,
       {fun send_packet/0, fun cancel_send_packet/0},
    ],
    transaction(Actions).

, , , - , .

, do_safe_ting/0 , noop/0, cleanup_database/0 cleanup_file/0.

, -, , , , {ok, Result} {error, Reason}. .

+3

Source: https://habr.com/ru/post/1661105/


All Articles