tags:

views:

154

answers:

4

I'm coding in Erlang and a I am bit unsure about how to approach error handling, especially after seeing the ugly errors Erlang always returns.

Should I use try catch in Erlang or should I pass back an error token?

+3  A: 

If you can deal with the exception sensibly locally, catch it and treat it or else let it "bubble up".

If you know nothing can be done with a particular exception, then "fail fast".

jldupont
I would qualify this by saying **sensibly** deal with the exception locally. It is always possible to "deal" with it by adding tests for an error token everywhere, but that is very rarely sensible.
rvirding
+2  A: 

I think this is largely a question of what the user of the function wants to do. An example of a quite low level file API is the standard: open a file and get a file handle, read from the file passing in the file handle, and close the file passing in the file handle.

A higher level API is file:consult/1 where you pass in a filename and expect to get the content back parsed as Erlang terms. A middle ground would be a with-open-file function as is common in lisp, where you pass in a function that is called with the then open file handle as its argument.

Even though they all open and access files, the expectation makes it more and more sensible to use exceptions instead of error tokens to describe how things went.

You want to allow the programmer to write readable code for the successful case. If you expect that a file wont exist, a simple case on the return value tends to be prettier than exception handling. If your successful case is to assume the file exist and just read it, then an exception will make sure you crash as soon as it doesn't hold up.

Choosing any particular approach because you think Erlang has ugly error messages does not seem like a good guideline.

Christian
+4  A: 

The basic principle in Erlang is:

Make it crash!

I found quite useful to avoid the so called defensive programming. The concept is explained in a bit more detail in the Erlang Programming Rules page:

http://www.erlang.se/doc/programming_rules.shtml#HDR11

Moreover, even if some of the Erlang errors can be a bit cryptic, a nice way to deal with is trace them! Tracing in Erlang is pretty simple. Have a look to this quick reference:

http://aloiroberto.wordpress.com/2009/02/23/tracing-erlang-functions/

or simply refer to the official documentation.

This said, I fully agree with @jdupont.

Roberto Aloi
+1  A: 

There are three basic ways to do exception handling in sequential Erlang:

  1. throws (throw(Term))
  2. errors (erlang:error(Reason))
  3. exits (exit(Reason))

Throws are to be used for non-local returns and some kinds of exception you expect to be able to handle (maybe because they occur often). When that one is raised, you should try and stop it before it gets outside your module. The usual way it is used in the stdlib is to throw a tuple of the kind {error, Reason} which will be caught by a top-level function in a try...catch before returning the tuple to the user. The user can then decide what to do based on that return value.

Errors, on the other hand, point to non-recoverable exceptions. They're usually going to require the user to change his code. They include runtime errors like if or case ... of branches that can't be matched, functions that can't match or don't exist, etc. The purpose of this error is to crash, and should not be caught or handled locally to the process in most cases (have a supervisor or monitoring process pick up the error message and then log it for you or handle it for the user at the interface level).

Exits are to be used when you specifically want the process to terminate. It's sometimes a bit unclear when to use exits or errors, but the tip I've been given is to differentiate the intent.

Errors and exits seldom should be caught and handled sequentially (you've really got to be sure you know how to fix things!), as other process are in place to deal with it. Let it crash.

(more details: http://learnyousomeerlang.com/errors-and-exceptions)


The next level is when dealing with errors in a multi-process environment. The standard way to do things at that point is to link processes together (and/or use supervisors) to pick up dead processes and the reason why they did: restart them, log the message, do your maintenance, upgrade while the system keeps rolling.

You get a new exception function for multiple processes: exit(Pid, Reason). This lets you call 'exit' on another process. In this case, error handling has to be done by setting process_flag(trap_exit, true) in the monitor process, after which you can get exit signals through a standard receive expression.

Note that a special kind of exit, namely exit(Pid, kill) will terminate a process without any possibility to catch it. Other processes linked to Pid should then receive a signal of the form {'EXIT', killed}.

Using supervision trees is how you make sure your programs keep running. Crashing early is also essential in order to make sure you won't corrupt anything; the earlier problematic code stops running, the easier it is to clean it all up.

I GIVE TERRIBLE ADVICE