[erlang-questions] feedback on my solutions to programming erlang second edition
Garrett Smith
g@REDACTED
Fri Aug 14 13:39:44 CEST 2015
Treat UI/UX as a separate concern. If you raise an exception there,
something ought to handle that if you're presenting it to a non
technical user.
But consider that in many applications, a user is an admin looking
through log files or getting emails/text messages with cryptic blurbs.
As long as that information is useful, fine. These people *love*
arcane messages :)
Consider also that even a nicely written message to a human may make
no sense. If your program runs into some unexpected error, it could be
a programming bug (or you're not handling a normal case) or some weird
event that's hard to predict and probably rare. In both cases, users
aren't going to be satisfied with anything you send them. They don't
care about your bugs and they will have no clue about a rare weird
event.
So handle cases that make sense - e.g. if a user selects a file that
doesn't exist, *that's* a reasonable "error" to handle gracefully. But
that falls into the data validation case and it's business logic IMO -
not lower level "what do I do if I can't read a file" error.
In any case, I'd handle user facing issues from a level above the
function in question.
On Fri, Aug 14, 2015 at 6:29 AM, Roelof Wobben <r.wobben@REDACTED> wrote:
> Thanks,
>
> So it's fine to give this a error message :
>
> ** exception error: {read_file,"wrong.txt",enoent} in function
> my_file:read/1 (my_file.erl, line 8))
>
> I think a unexperience users still does not know what went wrong.
> That's is why I choose for a more human readable error message( File not
> found or something else).
>
> Roelof
>
> ** exception error: {read_file,"wrong.txt",enoent}
> in function my_file:read/1 (my_file.erl, line 8)
>
>
>
> Op 14-8-2015 om 13:17 schreef Garrett Smith:
>
>> This function does two things that are unrelated, it returns a file
>> and it prints a message to stdout.
>>
>>
>> https://github.com/rwobben/programming_erlang_exercises/blob/master/chapter_6/my_file.erl#L8
>>
>> 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:
>>
>> http://erlang.org/doc/man/dict.html#find-2
>>
>> 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:
>>
>> http://erlang.org/doc/man/dict.html#fetch-2
>>
>> 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) {
>> e.printStackTrace();
>> 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),
>> Bin.
>>
>> 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})
>> end.
>>
>> 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
>
>
>
> ---
> 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