Yes, I love it too. I love Erlang because enables this.<br><br><div class="gmail_quote">On Tue, Feb 10, 2009 at 11:02 PM, Kunthar <span dir="ltr"><<a href="mailto:kunthar@gmail.com">kunthar@gmail.com</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">I love "let it crash" way :)<br>
<br>
Simplicity is the ultimate sophistication!<br>
Leonardo Da Vinci, da great Italian magician<br>
<br>
Peace<br>
Kunth<br>
<div><div></div><div class="Wj3C7c"><br>
<br>
On Tue, Feb 10, 2009 at 11:46 PM, Joe Armstrong <<a href="mailto:erlang@gmail.com">erlang@gmail.com</a>> wrote:<br>
> On Tue, Feb 10, 2009 at 4:22 PM, Adam Lindberg<br>
> <<a href="mailto:adam@erlang-consulting.com">adam@erlang-consulting.com</a>> wrote:<br>
>> Hi,<br>
>><br>
>> 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?<br>
><br>
> Yes<br>
><br>
> Everything has a cost - if we applied this principle rigidly to all<br>
> functions in<br>
> the system then every function in the entire system would be extended with<br>
> additional code. This make the code more difficult to read, and makes the<br>
> program larger (think cache hits).<br>
><br>
> Suppose we have a function that maps a to 1 and b to 2, I'd write it like this:<br>
><br>
> f(a) -> 1;<br>
> f(b) -> 2.<br>
><br>
> should I then add an additional clause to warn for bad arguments?<br>
><br>
> f(a) -> 1;<br>
> f(b) -> 2;<br>
> f(_) -> error('arg is not a or b').<br>
><br>
> This adds nothing to the clarity of the code since the original expresses exact<br>
> the intention of the program *and nothing else*<br>
><br>
> Programs that are cluttered with additional error messages are difficult to read<br>
> (which increases the chances of an error) an less efficient<br>
> (everything has a cost)<br>
><br>
>> 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<br>

>> > was not zero (this is what the function clause error indicated).<br>
><br>
> *everybody* spends a long time figuring out what when wrong the first time<br>
> they get an error of a particular type - then they learn - when you've seen<br>
> these errors a few times you'll find that you can find the error very quickly<br>
><br>
> The fact that erlang crashes at the first error and prints something really<br>
> aids debugging ...<br>
>><br>
>> lists:zipwith/3 could have been implemented as below (or something similar):<br>
>><br>
>> zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)];<br>
>> zipwith(F, [], []) when is_function(F, 2) -> [].<br>
>> zipwith(F, [], Ys) -> error(lists_of_different_length). %% Just a proposal, insert<br>
>> zipwith(F, Xs, []) -> error(lists_of_different_length). %% preferred error mechanism here.<br>
>><br>
>> The function clause, noting the arguments as [#Fun..., [], [5,6,7,...]], is kind of misleading since it happens inside the lists:zipwith/3 function.<br>
>><br>
>> 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."<br>
><br>
> Nw let's look at the error message - here's an experiment<br>
><br>
> 1> lists:zipwith(fun(X,Y) -> X + Y end, [1,2,3],[4,5]).<br>
> ** exception error: no function clause matching<br>
>                    lists:zipwith(#Fun<erl_eval.12.113037538>,[3],[])<br>
>     in function  lists:zipwith/3<br>
><br>
> To the experienced eye the error is clear<br>
><br>
> zipwith(Fun, [3], []) doesn't match any of the clauses defining zipwith<br>
><br>
> Show me the code Luke ... (just run less on<br>
> /usr/local/lib/erlang/stdlib ... ish)<br>
><br>
> zipwith(F, [X | Xs], [Y | Ys]) -> [F(X, Y) | zipwith(F, Xs, Ys)];<br>
> zipwith(F, [], []) when is_function(F, 2) -> [].<br>
><br>
> This is two lines of code.<br>
><br>
> So zipwith(Fun, [3], []) doesn't match one of these two lines of code ...<br>
><br>
> this is actually *shorter* than the documentation (a lot shorter)<br>
><br>
> what does the documentation say?<br>
><br>
>  zipwith(Combine, List1, List2) -> List3<br>
>       Types  Combine = fun(X, Y) -> T<br>
>       List1 = [X]<br>
>       List2 = [Y]<br>
>       List3 = [T]<br>
>       X = Y = T = term()<br>
>      Combine the elements of two lists of equal length into one list.<br>
>                                                                 **************<br>
> What I have noticed teaching Erlang is that beginners make almost exactly<br>
> the same mistakes as experienced users - the difference is in the time<br>
> it takes them to debug an error.<br>
><br>
> The first time is *always* slow - then you learn.<br>
><br>
> (this is universally true - while I can fix erlang programs pretty quickly<br>
> I can stare at simple javascript errors for ages before twigging what<br>
> went wrong)<br>
><br>
> But there's a more subtle problem.<br>
><br>
> Let's try your suggestion. (I put your zipwith in a module test4)<br>
><br>
> try4:zipwith(fun(X,Y) -> X + Y end, [1,2,3],[4,5]).<br>
> ** exception error: lists_of_different_length<br>
>     in function  try4:zipwith/3<br>
>     in call from try4:zipwith/3<br>
><br>
> It works - great - we think ... but what about this?<br>
><br>
> try4:zipwith(fun(X,Y) -> X + Y end, {1,2},[4,5]).<br>
> ** exception error: no function clause matching<br>
>                    try4:zipwith(#Fun<erl_eval.12.113037538>,{1,2},[4,5])<br>
><br>
> Now what? Opps the guards were wrong - need to add a few<br>
> when is_list(..) guards. Or do we want an error message that says<br>
> error,arg1 should not be a tuple ....<br>
><br>
> There are a very large number of ways we can supply incorrect arguments<br>
> and we can't program all of them.<br>
><br>
> So what do we do - we only write patterns that match the desired cases<br>
> *and nothing else* - this is part of the erlang "let it crash" philosophy.<br>
><br>
> In erlang we don't do defensive programming - (or rather we do do it using<br>
> patterns)<br>
><br>
> Best<br>
><br>
> /Joe Armstrong<br>
><br>
><br>
>> Cheers,<br>
>> Adam<br>
>> _______________________________________________<br>
>> erlang-questions mailing list<br>
>> <a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
>> <a href="http://www.erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://www.erlang.org/mailman/listinfo/erlang-questions</a><br>
>><br>
> _______________________________________________<br>
> erlang-questions mailing list<br>
> <a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
> <a href="http://www.erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://www.erlang.org/mailman/listinfo/erlang-questions</a><br>
><br>
_______________________________________________<br>
erlang-questions mailing list<br>
<a href="mailto:erlang-questions@erlang.org">erlang-questions@erlang.org</a><br>
<a href="http://www.erlang.org/mailman/listinfo/erlang-questions" target="_blank">http://www.erlang.org/mailman/listinfo/erlang-questions</a><br>
</div></div></blockquote></div><br><br clear="all"><br>-- <br>--Hynek (Pichi) Vychodil<br><br>Analyze your data in minutes. Share your insights instantly. Thrill your boss.  Be a data hero!<br>Try Good Data now for free: <a href="http://www.gooddata.com">www.gooddata.com</a><br>