[erlang-questions] Human readable errors in lists module

Raimo Niskanen <>
Tue Feb 10 16:51:36 CET 2009


On Tue, Feb 10, 2009 at 03:22:24PM +0000, Adam Lindberg wrote:
> Hi,
> 
> Is there any reason that for example lists:zipwith/3 returns a function clause instead of a human readable error when the lists are of different length? It might seem obvious at first but the reason I'm asking is because a colleague of mine just spent a long time debugging code with used list:zipwith/3 and it threw this error. What he did at first was to check that all arguments to lists:zipwith/3 was not zero (this is what the function clause error indicated).
> 
> lists:zipwith/3 could have been implemented as below (or something similar):
> 
> zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)];
> zipwith(F, [], []) when is_function(F, 2) -> [].
> zipwith(F, [], Ys) -> error(lists_of_different_length). %% Just a proposal, insert
> zipwith(F, Xs, []) -> error(lists_of_different_length). %% preferred error mechanism here.

That would be:
zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)];
zipwith(F, [], []) when is_function(F, 2) -> [];
zipwith(F, [], [_ | _]) when is_function(F, 2) ->
    erlang:error({badarg,lists_of_different_length});
zipwith(F, [_ | _], []) when is_function(F, 2) ->
    erlang:error({badarg,lists_of_different_length});
zipwith(F, Xs, Ys) ->
    erlang:error(badarg, [F, Xs, Ys]).

or else it would give the wrong error reason
for zipwith(Fun, [], atom).

Yes, zipwith/2,3 are very good candidates for this change.
It is a minor change and it is unlikely that the change
from a runtime error functin clause to badarg would break
old code. Note that a {badarg,Reason} tuple is not that
common in OTP, but an lists_of_different_length reason
or any other odd error atom is less common.

But if we change zipwith/2,3 we must in the name of consistency
do it for all functions in the lists module, and for all other
modules in stdlib, and kernel, and erts. And for some other functions
it may not be obvious that a simple erlang:error/1 at the bottom
of an iteration could know the cause as it is obvious for
zipwith. Then we would have to think about using a try..catch
at the top level and perhaps throw a cause and figure out
in the try..catch what was wrong. And the same applies when
a library function calls helper functions and you get a hard
time knowing what was the API function you gave the wrong
arguments really...

So users that get a weird function clause and a stack
trace pretty soon figure out how to debug. And it improves
their degbugging skills for their own code.

It could be better but this works rather good.



> 
> The function clause, noting the arguments as [#Fun..., [], [5,6,7,...]], is kind of misleading since it happens inside the lists:zipwith/3 function.
> 
> I can see the purists' argument here "that it is really a function clause" but I also see the pragmatist argument "that it is much easier to debug."
> 
> Cheers,
> Adam
> _______________________________________________
> erlang-questions mailing list
> 
> http://www.erlang.org/mailman/listinfo/erlang-questions

-- 

/ Raimo Niskanen, Erlang/OTP, Ericsson AB



More information about the erlang-questions mailing list