Question on functional style
Håkan Stenholm
hakan.stenholm@REDACTED
Fri Sep 9 20:44:14 CEST 2005
Vance Shipley wrote:
>Gurus,
>
>Is it bad form to use try/catch to handle non-local returns
>other than exceptions?
>
>I have some code which does a battery of tests in several phases.
>The tests are simple so it makes sense to me to just perform
>them sequentially in one function and throw when I want to skip
>to the next phase:
>
>start(Arg) ->
> try begin
> case lists:member(foo, Arg) of
> true ->
> throw(done);
> _ ->
> ok
> end,
> case lists:member(bar, Arg) of
> true ->
> throw(done);
> _ ->
> ok
> end,
> case lists:member(baz, Arg) of
> true ->
> throw(done);
> _ ->
> ok
> end
> end
> catch
> throw:done ->
> phase2()
> end.
>
>phase2() ->
> done.
>
>
The following replacement code springs to mind:
start(Arg) ->
F = fun(E) ->
lists:member(E, Arg)
end,
Matches = [foo,bar,baz],
case lists:any(F, Matches) of
true -> phase2();
false -> ok
end.
phase2() ->
done.
This does limited match testing with the help of lists:any/2, R10B-6
uses the following code:
any(Pred, [Hd|Tail]) ->
case Pred(Hd) of
true -> true;
false -> any(Pred, Tail)
end;
any(_, []) -> false.
which stops after the first match, without resorting to try, catch or
throw usage. Another choice is to do the following:
start(Arg) ->
try begin
false = lists:member(foo, Arg),
false = lists:member(bar, Arg),
false = lists:member(baz, Arg)
end
catch
throw:error -> % should it be 'exit' ?
phase2()
end.
phase2() ->
done.
This relies on Erlang generating a runtime exception if one of the
lists:member/2 calls become = 'true'. My personal choice would be to
write the code without any exceptions, as their use implies a unexpected
execution path in the code, rather than a standard path.
>
>While this is easiest for me to understand my own code I wonder
>whether it is good functional style. I do end up with a very
>large function and the throw is not really an "exception".
>
>Alternatively I can hard cpode the sequence into the test functions:
>
>start(Arg) ->
> f1(Arg).
>
>f1(Arg) ->
> case lists:member(foo, Arg) of
> true ->
> phase2();
> _ ->
> f2(Arg)
> end.
>
>f2(Arg) ->
> case lists:member(bar, Arg) of
> true ->
> phase2();
> _ ->
> f3(Arg)
> end.
>
>f3(Arg) ->
> case lists:member(baz, Arg) of
> true ->
> phase2();
> _ ->
> ok
> end.
>
>phase2() ->
> done.
>
>
>I find this makes it harder to follow the sequence and the tests
>can't be reused in a different order. I could pass the next function
>as an argument I suppose ...
>
>It seems to me that the above is less deterministic. The return
>from f1/1 depends on what happens in f2,f3,...fn.
>
>Using the first form I can actually perform many of the tests using
>an if statement and pattern matching.
>
>Your opinions are welcome.
>
> -Vance (who has never taken a CS course)
>
>
>
>
More information about the erlang-questions
mailing list