[erlang-questions] Human readable errors in lists module

Kenneth Lundin <>
Wed Feb 11 15:04:14 CET 2009


Hi,

As responsible for the Erlang/OTP team at Ericsson I can just say that
we agree with Joe in this matter.
We have no intention to add human readable errors to the list module
or other library modules in standard lib.

As a matter of fact I think a crash with function clause is quite
clear because it informs you about
that a named function did not have any clause which matched the
arguments you called it with. And the arguments
are also available in the crash info.
As the source code and the documentation for lists in this case is
available it should be possible to understand why the arguments was
not accepted.

A beginner might not have so easy to understand whats wrong but after
just a few occurences of errors like that it should be
quite obvious.

/Kenneth Erlang/OTP Ericsson

On Tue, Feb 10, 2009 at 10:46 PM, Joe Armstrong <> wrote:
> On Tue, Feb 10, 2009 at 4:22 PM, 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?
>
> Yes
>
> Everything has a cost - if we applied this principle rigidly to all
> functions in
> the system then every function in the entire system would be extended with
> additional code. This make the code more difficult to read, and makes the
> program larger (think cache hits).
>
> Suppose we have a function that maps a to 1 and b to 2, I'd write it like this:
>
> f(a) -> 1;
> f(b) -> 2.
>
> should I then add an additional clause to warn for bad arguments?
>
> f(a) -> 1;
> f(b) -> 2;
> f(_) -> error('arg is not a or b').
>
> This adds nothing to the clarity of the code since the original expresses exact
> the intention of the program *and nothing else*
>
> Programs that are cluttered with additional error messages are difficult to read
> (which increases the chances of an error) an less efficient
> (everything has a cost)
>
>> 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).
>
> *everybody* spends a long time figuring out what when wrong the first time
> they get an error of a particular type - then they learn - when you've seen
> these errors a few times you'll find that you can find the error very quickly
>
> The fact that erlang crashes at the first error and prints something really
> aids debugging ...
>>
>> 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.
>>
>> 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."
>
> Nw let's look at the error message - here's an experiment
>
> 1> lists:zipwith(fun(X,Y) -> X + Y end, [1,2,3],[4,5]).
> ** exception error: no function clause matching
>                    lists:zipwith(#Fun<erl_eval.12.113037538>,[3],[])
>     in function  lists:zipwith/3
>
> To the experienced eye the error is clear
>
> zipwith(Fun, [3], []) doesn't match any of the clauses defining zipwith
>
> Show me the code Luke ... (just run less on
> /usr/local/lib/erlang/stdlib ... ish)
>
> zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)];
> zipwith(F, [], []) when is_function(F, 2) -> [].
>
> This is two lines of code.
>
> So zipwith(Fun, [3], []) doesn't match one of these two lines of code ...
>
> this is actually *shorter* than the documentation (a lot shorter)
>
> what does the documentation say?
>
>  zipwith(Combine, List1, List2) -> List3
>       Types  Combine = fun(X, Y) -> T
>       List1 = [X]
>       List2 = [Y]
>       List3 = [T]
>       X = Y = T = term()
>      Combine the elements of two lists of equal length into one list.
>                                                                 **************
> What I have noticed teaching Erlang is that beginners make almost exactly
> the same mistakes as experienced users - the difference is in the time
> it takes them to debug an error.
>
> The first time is *always* slow - then you learn.
>
> (this is universally true - while I can fix erlang programs pretty quickly
> I can stare at simple javascript errors for ages before twigging what
> went wrong)
>
> But there's a more subtle problem.
>
> Let's try your suggestion. (I put your zipwith in a module test4)
>
> try4:zipwith(fun(X,Y) -> X + Y end, [1,2,3],[4,5]).
> ** exception error: lists_of_different_length
>     in function  try4:zipwith/3
>     in call from try4:zipwith/3
>
> It works - great - we think ... but what about this?
>
> try4:zipwith(fun(X,Y) -> X + Y end, {1,2},[4,5]).
> ** exception error: no function clause matching
>                    try4:zipwith(#Fun<erl_eval.12.113037538>,{1,2},[4,5])
>
> Now what? Opps the guards were wrong - need to add a few
> when is_list(..) guards. Or do we want an error message that says
> error,arg1 should not be a tuple ....
>
> There are a very large number of ways we can supply incorrect arguments
> and we can't program all of them.
>
> So what do we do - we only write patterns that match the desired cases
> *and nothing else* - this is part of the erlang "let it crash" philosophy.
>
> In erlang we don't do defensive programming - (or rather we do do it using
> patterns)
>
> Best
>
> /Joe Armstrong
>
>
>> Cheers,
>> Adam
>> _______________________________________________
>> erlang-questions mailing list
>> 
>> http://www.erlang.org/mailman/listinfo/erlang-questions
>>
> _______________________________________________
> erlang-questions mailing list
> 
> http://www.erlang.org/mailman/listinfo/erlang-questions
>



More information about the erlang-questions mailing list