Erlang - Exceptions



Exception handling is required in any programming language to handle the runtime errors so that normal flow of the application can be maintained. Exception normally disrupts the normal flow of the application, which is the reason why we need to use Exception handling in our application.

Normally when an exception or error occurs in Erlang, the following message will be displayed.

{"init terminating in do_boot", {undef,[{helloworld,start,[],[]}, 
{init,start_it,1,[]},{init,start_em,1,[]}]}}

Crash dump will be written to −

erl_crash.dump
init terminating in do_boot ()

In Erlang, there are 3 types of exceptions −

  • Error − Calling erlang:error(Reason) will end the execution in the current process and include a stack trace of the last functions called with their arguments when you catch it. These are the kind of exceptions that provoke the runtime errors above.

  • Exists − There are two kinds of exits: 'internal' exits and 'external' exits. The internal exits are triggered by calling the function exit/1 and make the current process stop its execution. The external exits are called with exit/2 and have to do with multiple processes in the concurrent aspect of Erlang.

  • Throw − A throw is a class of exception used for cases that the programmer can be expected to handle. In comparison with exits and errors, they don't really carry any 'crash that process!' intent behind them, but rather they control the flow. As you use throws while expecting the programmer to handle them, it's usually a good idea to document their use within a module using them.

A try ... catch is a way to evaluate an expression while letting you handle the successful case as well as the errors encountered.

The general syntax of a try catch expression is as follows.

Syntax

try Expression of 
SuccessfulPattern1 [Guards] -> 
Expression1; 
SuccessfulPattern2 [Guards] -> 
Expression2 

catch 
TypeOfError:ExceptionPattern1 -> 
Expression3; 
TypeOfError:ExceptionPattern2 -> 
Expression4 
end

The Expression in between try and of is said to be protected. This means that any kind of exception happening within that call will be caught. The patterns and expressions in between the try ... of and catch behave in exactly the same manner as a case ... of.

Finally, the catch part – here, you can replace TypeOfError by either error, throw or exit, for each respective type we've seen in this chapter. If no type is provided, a throw is assumed.

Following are some of the errors and the error reasons in Erlang −

Error Type of Error
badarg Bad argument. The argument is of wrong data type, or is otherwise badly formed.
badarith Bad argument in an arithmetic expression.
{badmatch,V} Evaluation of a match expression failed. The value V did not match.
function_clause No matching function clause is found when evaluating a function call.
{case_clause,V} No matching branch is found when evaluating a case expression. The value V did not match.
if_clause No true branch is found when evaluating an if expression.
{try_clause,V} No matching branch is found when evaluating the of-section of a try expression. The value V did not match.
undef The function cannot be found when evaluating a function call..
{badfun,F} Something is wrong with a fun F
{badarity,F} A fun is applied to the wrong number of arguments. F describes the fun and the arguments.
timeout_value The timeout value in a receive..after expression is evaluated to something else than an integer or infinity.
noproc Trying to link to a non-existing process.

Following is an example of how these exceptions can be used and how things are done.

  • The first function generates all possible types of an exception.

  • Then we write a wrapper function to call generate_exception in a try...catch expression.

Example

-module(helloworld). 
-compile(export_all). 

generate_exception(1) -> a; 
generate_exception(2) -> throw(a); 
generate_exception(3) -> exit(a); 
generate_exception(4) -> {'EXIT', a}; 
generate_exception(5) -> erlang:error(a). 

demo1() -> 
   [catcher(I) || I <- [1,2,3,4,5]]. 
catcher(N) -> 
   try generate_exception(N) of 
      Val -> {N, normal, Val} 
   catch 
      throw:X -> {N, caught, thrown, X}; 
      exit:X -> {N, caught, exited, X}; 
      error:X -> {N, caught, error, X} 
   end. 
      
demo2() -> 
   [{I, (catch generate_exception(I))} || I <- [1,2,3,4,5]]. 
demo3() -> 
   try generate_exception(5) 
   catch 
      error:X -> 
         {X, erlang:get_stacktrace()} 
   end. 
   
lookup(N) -> 
   case(N) of 
      1 -> {'EXIT', a}; 
      2 -> exit(a) 
   end.

If we run the program as helloworld:demo(). , we will get the following output −

Output

[{1,normal,a},
{2,caught,thrown,a},
{3,caught,exited,a},
{4,normal,{'EXIT',a}},
{5,caught,error,a}]
Advertisements