Which one should I use exit, error or throw?
There are 3 classes which can be caught with a try ... catch
: throw
, error
and exit
.
throw
is generated usingthrow/1
and is intended to be used for non-local returns and does not generate an error unless it is not caught (when you get anocatch
error).error
is generated when the system detects an error. You can explicitly generate an error usingerror/1
. The system also includes a stacktrace in the generated error value, for example{badarg,[...]}
.exit
is generated usingexit/1
and is intended to signal that this process is to die.
The difference between error/1
and exit/1
is not that great, it more about intention which the stacktrace generated by errors enhances.
The difference between them is actually more noticeable when doing catch ...
: when throw/1
is used then the catch
just returns the thrown value, as is expected from a non-local return; when an error/1
is used then the catch
returns {'EXIT',Reason}
where Reason
contains the stacktrace; while from exit/1
catch
also returns {'EXIT',Reason}
but Reason
only contains the actual exit reason. try ... catch
looks like it equates them, but they are/were very different.
[UPDATED]
I glossed over the important difference between throw and error, pointed out by Robert Virding. This edit is just for the record!
throw error
is to be used where one would use throw
in other languages. An error in a running process has been detected by your code, which signals an exception with error/1
. The same process catches it (possibly higher up in the stack), and the error is to be handled within the same process. error
always brings with it a stacktrace.
throw
is to be used not to signal an error, but just to return a value from a deeply nested function.
Since it unwinds the stack, calling throw
returns the thrown value to the place it was caught. As in the case of error
, we're catching stuff that was thrown, only what was thrown wasn't an error but rather just a value passed up the stack. This is why throw does not bring with it a stacktrace.
As a contrived example, if we wanted to implement an exists
function for lists, (similar to what list:any
does) and as an exercise without doing the recursing ourselves, and using just list:foreach
, then throw
could be used here:
exists(P, List) ->
F = fun(X) ->
case P(X) of
true -> throw(true);
Whatever -> Whatever
end
end,
try lists:foreach(F, List) of
ok -> false
catch
true -> true
end.
A value thrown but not caught is treated as an error
: a nocatch
exception will be generated.
EXIT is to be signaled by a process when it 'gives up'. The parent process handles the EXIT, while the child process just dies. This is the Erlang let-it-crash philosophy.
So exit/1
's EXIT is not to be caught within the same process, but left to the parent. error/1
's errors are local to the process - i.e., a matter of what happens and how it is handled by the process itself; throw/1
is used for control flow across the stack.
[UPDATE]
- This tutorial explains it well: http://learnyousomeerlang.com/errors-and-exceptions
- Note there is also a
exit/2
- called with aPid
of a process to send the EXIT to.exit/1
implies the parent process.