[erlang-questions] feedback on my solutions to programming erlang second edition

Garrett Smith g@REDACTED
Fri Aug 14 13:17:45 CEST 2015

This function does two things that are unrelated, it returns a file
and it prints a message to stdout.


Erlang uses two different patterns for surfacing errors from functions...

Tagged tuples:

do() -> {ok, Value} | {error, Error} | error

and exceptions:

do() -> Value

In the first form, the caller must handle the value accordingly.
dict:find/2 is an example of this:


It's important in this case to use patterns that can differentiate
between a good value and an error. We use {ok, Value} and {error,
Error} respectively. If the error doesn't have or need any detail, use
the atom error by itself. Don't do this though:

do() -> Value | {error, Error} | error

as it forces an order of pattern matching (and precludes error
tuples/atom as valid results), which is easily avoided by tagging the
good value as {ok, Value}.

In the second form, the caller can safely assume that the value is
correct, otherwise an exception would be raised (which doesn't have to
be handled, though can be). dict:fetch/2 is an example of this:


There's never a case IMO where a function should handle an error
internally by printing a message somewhere and returning the atom ok,
which is what your function does. Imagine the chagrin of a caller
assuming everything is fine, then discovering that ok is not a binary,
failing. Then what, a human has to screen scrape stdout somewhere?
This reminds me of a common Java pattern:

try {
    return do();
catch (Exception e) {
    return null;

As horrifying as this code is (truly) I see it *all the time*.

People often ask which form of error surfacing is correct here -
tagged tuples or exception. Both are correct and should be used - but
according to the intent of the function. The dict module I think is a
great example (find vs fetch).

In my own practice, I tend to follow this order of progression:

- Functions return good values, not tagged tuples

- Avoid any non "happy path" code and let Erlang deal with the
unhandled cases - i.e. your code only reflects what you care about
handling, nothing else

- In cases where a function itself (internally) has to deal with a
tagged error, raise an error with appropriate context

- If the function is low enough level that *it's job* is to surface
*either* success or failure, then IMO it must use tagged tuples -
that's its design and not a matter of aesthetics

So, again, in progression of thinking, I'd start your function off like this:

read(File) ->
    {ok, Bin} = file:read_file(File),

The caller doesn't have to worry about getting some weird value and
Erlang will stop dead in its tracks if there's a problem reading that
file. Perfect!

Except that in this case, you'll get a 'bad match' error that will
complain about an error result, but it won't tell you about the file
involved. So being slightly speculative about needing that information
in case of an error, I'd write it this way:

read(File) ->
    case file:read_file(File) of
        {ok, Bin}-> Bin ;
        {error, Err} -> error({read_file, File, Err})

But I concede that this is speculative - it's just in this case I have
enough experience with this type of function to know that I'll
certainly be interested in the file if something's wrong (this is
obvious). But by default, I'd avoid that speculation and get some
experience running the code to tell me if I need to take the next step
of handling error cases explicitly and generating fuller-context
exceptions. My motive there is to avoid typing *any* error handling
code at all. That's IMO the right starting point. Then, let yourself
be dragged reluctantly into error handling code by experience.

In any case, stick to the two conventions here of surfacing errors.

Otherwise from what I saw things look great!

Now find *something to build* :)

On Fri, Aug 14, 2015 at 3:29 AM, Roelof Wobben <r.wobben@REDACTED> wrote:
> Hello,
> I did the first 6 chapters of the Erlang programming book second editon.
> Now I wonder if I did things the righr way and if there are any
> improvements.
> My code can be found here:
> https://github.com/rwobben/programming_erlang_exercises
> I did not do the chapter 5 exercises because then you need to use map and
> that one is in R17 where I work on R16.
> Roelof
> ---
> Dit e-mailbericht is gecontroleerd op virussen met Avast antivirussoftware.
> https://www.avast.com/antivirus
> _______________________________________________
> erlang-questions mailing list
> erlang-questions@REDACTED
> http://erlang.org/mailman/listinfo/erlang-questions

More information about the erlang-questions mailing list