[eeps] EEP 049: Value-Based Error Handling Mechanisms

Kenneth Lundin kenneth@REDACTED
Fri Dec 7 12:28:11 CET 2018


EEP-0049 Value-based Error handling mechanisms The problem to solve

Our interpretation of the EEP is that the author tries to solve 2 somewhat
separate problems with one single solution.

   - Replace or simplify deeply-nested case ... end expressions, and
   prevent using exceptions for control flow (with language support in this
   case).
   - Encouraging standards {ok,Result}|{error,Reason}

We agree about that it would be nice to be able to replace or simplify
deeply-nested case ... end expressions except that we don't see a problem
with using throw (a mechanism to make a non local return) as long as it is
done in a safe way. We don't regard throw as an exception, and it is
documented as "a non local return".

We also agree that it would be good to encourage a standard pattern for
function returns. But we are not there now and we would like to see
something more flexible than hard coded support in the language.
Comments on the solution

In most of the examples where we find usage of nested case, the unwanted
result ({error, Reason} in this case) is not just returned, there is some
other actions performed as well before returning. In those cases the
proposed solution does not help.

   - We don't like a language construct which is hard coded to support
ok,{ok,Result},
   {error,Reason}.
   - the use of underscore _ <~ to mean a match with ok is not a hit, it
   will make programs harder to read
   - We are against the introduction of *unwrapexprs* that cannot be used
   everywhere where expressions are allowed.
   - The *unwrapexpr* changes the scoping rules and can not be used in
   nested expressions and not outside begin ... end.

It is perfectly possible to use throw and try catch to replace or simplify
deeply-nested case ... end expressions in the same way as the proposed
language extension does.
Example

commit_write2(OpaqueData) ->
    Ref = erlang:make_ref(),
    Ok = fun(ok) -> ok; ({ok,R}) -> R; ({error,_Reason} = E) -> throw(
{Ref,E}) end,
    try
        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}
    catch
        {Ref,E} ->
            E
    end.

%% We could simplify for the user even more and at the same time encourage the
%% standard pattern ok, {ok,Result}, {error,Reason} by creating a library
%% function like this (and perhaps place it in stdlib). To start with
the user can
%% make his own function or fun for this.

ok() ->
    Ref = erlang:make_ref(),
    Ok = fun(ok) -> ok;
            ({ok,R}) -> R;
            ({error,_Reason} = E) ->
                 throw( {Ref,E})
         end,
    {Ref,Ok}.


%% Here is the same example using the library function
commit_write3(OpaqueData) ->
    {Ref,Ok} = ok(),
    try
        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}
    catch
        {Ref,E} ->
            E
    end.


%% This is another example from EEP 49
maybe() ->
    case file:get_cwd() of
        {ok, Dir} ->
            case
                file:read_file(
                  filename:join([Dir, "demo", "data.txt"]))
            of
                {ok, Bin} ->
                    {ok, {byte_size(Bin), Bin}};
                {error, Reason} ->
                    {error, Reason}
            end;
        {error, Reason} ->
            {error, Reason}
    end.


%% The example above can be written like this without any new language
%% constructs and the ok() function as a library function
%%
-spec maybe2() -> {ok, non_neg_integer()} | {error, term()}.
maybe2() ->
    {Ref,Ok} = ok(),
    try
        Dir = Ok(file:get_cwd()),
        Bin = Ok(file:read_file(filename:join([Dir, "demo", "data.txt"]))),
        {ok, {byte_size(Bin), Bin}}
    catch
        {Ref,ErrorReason} ->
            ErrorReason
    end.

Summary

   - We say no to the proposed language extensions. We don't think they are
   general enough and we also see some problems with them.
   - The same effect can be achieved safely with the current language using
   throw, try...catch.
   - Encouraging ok, {ok,Result}, {error,Reason} as results from functions
   can be done in other ways, for example through library functions. These
   values should not be special to the *language*.
   - We also want to thank the author for a very well thought through and
   well documented proposal which has triggered us to think about possible
   solutions in this area. We really appreciate the effort.

/Kenneth, Erlang/OTP Ericsson
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://erlang.org/pipermail/eeps/attachments/20181207/656bb033/attachment.htm>


More information about the eeps mailing list