[erlang-questions] Error handling with good looking code?

Garrett Smith g@REDACTED
Tue Nov 15 18:11:31 CET 2011

Hi Attila,

On Thu, Nov 3, 2011 at 8:08 AM, Attila Rajmund Nohl
<attila.r.nohl@REDACTED> wrote:
> Hello!
> I have this code (cut out some non-interesting pieces):
> upload_trace_record_file2(Host, Usr, Pwd, RemDir, NetTraceRef) ->
>    case sftp_client:open(Host, ?SFTP_PORT, Usr, Pwd) of
>        {ok, Pid} ->
> ....
>            case sftp_client:send(Pid, TRFile, RemTRFile) of
>                ok ->
>                    ok;
>                {error, Reason} ->
>                    {error, sftp_client:format_error(Reason)}
>            end,
>            sftp_client:close(Pid),
>            file:delete(TRFile);
>        {error, Reason} ->
>            {error, sftp_client:format_error(Reason)}
>    end.

Aren't you just talking about this:

 case open_client(Info) of
     {ok, C} ->
         Result = send_files(C, Files),
         cleanup(C, Files),
     {error, Err} -> {error, Err}


 case open_client(Info) of
     {ok, C} ->
             send_files(C, Files)
             cleanup(C, Files)
     {error, Err} -> {error, Err}

> update_trace_record_file(TRFile, NetTraceRef) ->
>    case exml:read_file(TRFile, [{space, normalize}]) of
>        {ok, ReadExml} ->
>            {traceCollecFile, Attributes, ChildList} = ReadExml,
> ...
>            file:write_file(TmpTRFile, TraceText),
>            TmpTRFile;
>        _ ->
>            throw({error, bad_xml_file})
>    end.

The general pattern seems fine:

 case read_file(Info) of
     {ok, File} ->
     {error, Err} -> throw(Err)

The alternative is to return a tagged value:

 case read_file(Info) of
     {ok, File} ->
         {ok, File};
     {error, Err} -> {error, Err}

Some lesser observations:

- You're losing valuable information by ignoring the error from
exml:read_file/2 - that should probably be in the thrown term

- Use exit(Reason) rather than throw if its part of the API (i.e.
anyone outside your code might handle it) - exit will propagate up the
stack and cause the process to terminate with the term provided minus
the call stack

> There are at least two obvious bugs in the error handling (the error
> from sftp_client:send is not used; the update_trace_record_file throws
> an exception instead of returning an error), but it made think about
> how to handle the errors properly in this code. The "let it crash"
> philosophy doesn't really work here, because we need to produce a
> meaningful error message to the user and the stack trace is not a
> meaningful message to them. I was thinking something like this:
> upload_trace_record_file2(Host, Usr, Pwd, RemDir, NetTraceRef) ->
>    try
>        {sftp_client, {ok, Pid}} = {sftp_client,
> sftp_client:open(Host, ?SFTP_PORT, Usr, Pwd)},
> ....
>        {sftp_client, ok} = {sftp_client, sftp_client:send(Pid,
> TRFile, RemTRFile)},
>        sftp_client:close(Pid),
>        file:delete(TRFile)
>   catch
>        error:{badmatch, {sftp_client, Reason}} ->
>            {error, sftp_client:format_error(Reason)};
>        error:{badmatch, {exml, _}} ->
>                  {error, bad_xml_file}
>    end.
> update_trace_record_file(TRFile, NetTraceRef) ->
>    {exml, {ok, ReadExml}} = {exml, exml:read_file(TRFile, [{space,
> normalize}])},
>    {traceCollecFile, Attributes, ChildList} = ReadExml,
> ...
>    file:write_file(TmpTRFile, TraceText),
>    TmpTRFile.
> but it looks somewhat ugly. Three "assignments" and function calls are
> hidden away in tuples, so it's a little hard to read, even though the
> rest of the code is simpler. Do you have any other ideas?

If I were to restate, you're suggesting that this:

 case foo() of
     {ok, Val} -> good(Val);
     {error, Err} -> bad(Err)

be written like this:

     {foo, {ok, Val}} = {foo, foo()},
     error:{badmatch, {foo, {error, Err}}} ->

When used for good, Erlang lets you write clear, logical, maintainable
code. If your eyes start to bleed when reading code (as is the case
here for me), push through the confusion / fuzziness and work until
the code is obviously descriptive of the problem you intend to solve.

Just my 2 cents :)


More information about the erlang-questions mailing list