You should always measure to find things like this out.
Your code also does not do what you think it does.
-module(glurk).
-compile(export_all).
fn2() ->
try
{ok, Result} = fn1(),
%Do something with Result
ok
catch
throw:Term -> Term;
exit:Reason -> {exit, Reason};
error:Reason -> {error,{Reason,erlang:get_stacktrace()}}
end.
fn1() ->
{error, a}.
Try this out:
c(glurk).
./glurk.erl:6: Warning: variable 'Result' is unused
{ok,glurk}
16> glurk:fn2().
{error,{{badmatch,{error,a}},
[{glurk,fn2,0},
{erl_eval,do_apply,5},
{shell,exprs,6},
{shell,eval_exprs,6},
{shell,eval_loop,3}]}}
This is because fn1 did not raise an exception
it gebnerated a normal retyurn value {error, a} which
does not pattern match against {ok, Result}
The first version of your code works with a function that either returns a normal value
or raises an exception - you have to write it like this:
fn1(....) ->
...
%% success case
Val;
%% failure case
throw(...) | exit(...) | error(...)
You can't just pump the same function into fn1 and fn2.
If you had the case where the called function had to escape from a deep recursion
then the first method would be more efficient than the second - since you could
immediately exit from a deep recursion by saying throw(...).
So the answer depends upon the nature of the function that you are calling.
Code should always be optimised for beauty and not efficiency - since you have
to maintain the stuff - then it should only be optimised in the rare cases
where it is not fast enough. What needs to be optimised should be identified
by measuring the program (you will always be surprised here :-)
Me, I'd write
{ok,Result} = ...
Actually your first code has a more subtle error
fn2() ->
try
{ok, Result} = fn1(),
%Do something with Result
ok
catch
throw:Term -> Term;
exit:Reason -> {exit, Reason};
error:Reason -> {error,{Reason,erlang:get_stacktrace()}}
end.
Think about this. The caught error cases do not themselves handle the error
they just return tuples like {exit, Reason} or {error, Reason} this means that the
next layer up (ie the caller of fn2) will also have to mess around checking
error returns - if this is repeated at all levels the code will be a mess.
The "erlang" way is to have one try-catch at the top of the program and just terminate
abruptly with exit(Why) if an error occurs.
In fact often you should not even do this - you should link your process to another process
then the offending process will die and "the other processes will fix the error".
The exception propagates up the call stack and flies over to the linked processes
for treatment. So we have two types of processes - ones that have no inbuilt error handling
and processes that only do error handling.