error-handlingerlangruntime-errordialyzer

Different between throw and catch in handing exception Erlang


I have a simple code like below to test how Erlang handling exceptions. The **** reference for throw and catch.

-module(exception).
-export([sum/2]).
-export([is_odd/1]).

sum(A, B) ->
    case is_odd(A) of
    odd ->
        2*A+B;
    Result ->
        Result
    end.


is_odd(A) ->
    case is_integer(A) of
    true ->
        odd;
    _ -> ****({error, "Input error"})
    end.

When I ran dialyzer with throw, it show warning:

exception.erl:9: The variable Result can never match since previous clauses completely covered the type 'odd'

This warning can be fixed by adding

case catch

for funtion fun/2.

When I ran dialyzer with catch, dialyzer passed sucessesful.

I wonder a bit about catch and throw. Which situation we should you throw or catch?


Solution

  • If you use throw (for the ****), your function is_odd(A) will only ever return one normal value: the atom 'odd'. That's what dialyzer is telling you: the clause at line 9 (Result ->) can never match. If is_odd(A) returns a value, it must be 'odd', so the first clause will always be chosen.

    If you write 'case catch is_odd(A) of ...', then any thrown exception will be caught and turned into a value. In your case, that's the tuple {error, "Input error"}. (Note that this is an "old-style" catch Expression, and it's generally preferred to use the modern try ... catch ... end instead in all new code.) Now suddenly, there are two possible values again and the clause at line 9 can also get chosen, so dialyzer will not complain.

    In general, use exceptions for relatively rare conditions, and use normal return values for common things. However, sometimes it can be a good idea to use exceptions as a long jump ("nonlocal return") to get out of a deep recursion.