Archive

Posts Tagged ‘Philosophy’

in case in case this happens

2009/10/15 2 comments

I truly hate nested case clauses in Erlang they are the root of all that is unholy and should be banned. I have what some consider a fanatic principal when it comes to case clauses and that is: Never have more then one case clause per function. Sure you will argue that it’s not that bad and if you know what you are doing it’s fine and sometimes you can’t avoid it and bla bla bla!

Well guess what, it sucks and I got solid proof to back that!

Why nested case clauses suck

consider this code:

case foo() of
  not_foo ->
    case bar() of
      not_bar ->
        case baz() of
          not_baz ->
            {error, none};
          yes_baz ->
            baz
        end;
     yes_bar ->
        bar
    end;
  yes_foo ->
    foo
end.

And OPS you get a cause_clause error in your live system… and it happens a little bit now and then and you have no idea why or when and your tests sure aren’t finding this really nasty edge case… so What do you do now??. You could try to trace foo(), bar() and baz() but tracing on a live environment isn’t a very good idea… besides you have to leave your trace on for possible hours because the bug might not happen very often! Well you could argue that if you don’t know what the possible return values are you designed it wrong… well that is kindof the point I’m trying to get across! There are a lot of versions on how to “re-write” this.. an example is the classic “construct a tuple” shortcut:

case {foo(), bar(), baz()} of
  {_, _, yes_baz} -> baz;
  {_, yes_bar, _} -> bar;
  {yes_foo, _, _} -> foo
end.

I put the first clause matching the right hand side first on purpose to show that this short cut really doesn’t make sense. In this case you are running 3 commands (which might very well even have dependencies) and you only want to know one of them. In case of true/false functions this is another common type:

case (foo() orelse bar() orelse baz()) of
  true ->
    %% do something...
  false ->
    %% do something else...
end.

problem here is that you don’t know which one succeeded or caused the truth value. In the end I have found that breaking this down in three functions is the best:

f() ->
 case foo() of
   true -> foo;
   false -> b()
 end.

b() ->
 case bar() of
   true -> bar;
   false -> c()
 end.

...

It would still give you a more readable code, if you get an error such as case clause then you know where it is. You wouldn’t want to trace a live system but imagine that you still did, then you would in this case have to trace at least three cases in the nested case clause version while in this version you can trace only 1 (This is still a fairly naive way of looking at it, but it is still better).

NOTE: Seriously, don’t do it though (trace a live system), I turned on a trace in a live system once and by mistake put a trace on a very actively used function, we had to kill the node! but being cleaver as we usually are the node was in such a way that it could safely be killed because we had other nodes that we could fail over on without loosing any data (hahaa!)… but in the end I still was lucky and it was a stupid thing to do :D.

If you have 1 case clause per function and you have small functions then you are much much more likely to know where an error will end up, it becomes clear as day… you know what you called, and with what argument so you should be able to narrow down the reasons for the error. Simply put; you can’t trace case clauses and that means a lot.

I think I shall call this one the “single clause” philosophy (since it probably applies to if clauses as well). The fact is that it has helped me shorten my functions a lot. Probably the number one mistake that people do in functional programming do is to treat functions other then functions; using if statements instead of pattern matching etc. Functions should be small (maybe 5-10 lines, very rarely more) and do only 1 thing (unless it is a glue function which its purpose is to call other functions).

Pattern matching is the power of all righteous.

Advertisements
Categories: Erlang, Philosophy Tags: ,

Let it crash (the right way…)

I do a lot of useless stuff on the interwebz; Like facebooking, twittering, blogging, redditing etc… but once in a while do some really useful stuff as well E.g. trapexit.org, stackoverflow.com, /r/programming/ + many other things… and among all this stuff I read a lot of programming articles and blogs. The latest one I’ve come across which is pretty interesting (even though it is basically the same record spinning) is this podcast interview with Joe Armstrong by SE Radio.

I have at several occasions spoken to Joe, even if he probably doesn’t remember, hehe. As they say in Sweden; “Alla känner apan men apan känner ingen” which means “Everyone knows the monkey but the monkey doesn’t know anyone” and I know his speeches/rhetoric and this one was not very different. However, this time he mentioned something that made me remember a very common misunderstanding that I’ve see in the years I have been doing Erlang consulting, namely “Letting everything crash” in the name of “Let it crash”-philosophy. Allow me to elaborate:

Joe mentions the following things which I have learnt and is constantly advising others to follow: “only program the happy case, what the specification says the task is supposed to do”, when talking about “happy case programming”. He goes on saying “when writing code from a specification, the specification says what the code is supposed to do, it does not tell you what you’re supposed to do if the real world situation deviates from the specification” and finally “so what do the programmers do.. they take ad hoc decisions” when talking about what the programmers do when they program defensively in order to solve this problem. And even though Joe sometimes have some strange ideas mostly he makes a lot of sense and the topic of defensive programming and “Let it crash” philosophy did hit the spot.

Personally, when ever I make a branch in my code (not like Git branches, but more like ifs and cases) to handle an “error” or “fault” then I always ask myself one single question, which is what this whole thing boils down to;

Do I know how I’m supposed to handle an error?

The answer is simple; Yes -> Implement, No -> Don’t implement, Let it crash. Well, let me take some of that back… the world is rarely that naive, there are a few things you need to consider but in the end it is all dependent on your spefication. Like Joe said, people take ad hoc decisions and I keep asking myself why? Why do people insist on wrapping many of their function calls in case-clauses, match on error and then return the same error?! I understand it if it says in the specification that this is what’s expected but not otherwise. If the answer to the above question is No then the first follow up question should logically be; “Well, should I know?” and then define it in the specification. The “defensive” part of the code is not that you are matching on the error (because your specification might say that you must!), the “defensive” part is that you are matching on the error because you don’t know what to do.

Here are a few other questions I ask myself when facing a case or if or similar (like a function clause to “handle” all):

  • Do I know how I’m supposed to handle an error here?
  • If not, then should I handle it? (Thus going back to specification)
  • If something goes wrong in this function call, can I continue? E.g. Line 2 depends on a file descriptor from line 1
  • When I restart, can I recover from the crash here? If not then it is a good indication that you need a specification for how to handle the error. Can I reset my state? Do I need to clean up? etc are also good questions to ask yourself
  • Am I crashing in a valid place? E.g. you should never crash (intentionally) inside a gen_server unless it is some kind of a worker process. A “main” process shouldn’t really be allowed to crash in the same sense as a worker process can. A worker process, say for an HTTP request, shouldn’t really be very defensive (perhaps a top-level try-catch to return some useful error)

I’m far from writing truly “happy case programming” but I’m going there, it is unbelievable how many LOC you save just on seeing things more postive (Note to self: Perhaps this applies to real life; More positive attitude == Less unnecessary brain activity :)). An important note to all these fancy philosophies is that you need to design your software to be ready to let stuff crash, if anyone says to you that it is easy then they are lying… the only difference is that Erlang makes it possible which is a huge advantage.

Still today I give myself a virtual slap on the wrist inside my mind when ever I type something like:

handle_call(action, _From, State) ->
    %% ... do something
    {reply, ok, State};
hande_call(Cmd, From, State) ->
    io:format("Unhandled: ~p From: ~p ~n",[Cmd, From]),
    {reply, ok, State}.

Why do I put the “catch-all-calls” clause there? Where would this call come from anyway? And if I get one, why am I not aware of it?!?

Categories: Erlang Tags: ,