Ayende @ Rahien

Refunds available at head office

Sometimes imperative is so much easier

Take a look at the following Erlang function:

count_promises(Id, N, {_, PromisesQueue}) ->
    rec_count_promises(0, Id, N, PromisesQueue).

rec_count_promises(Count, _, _, []) ->
    Count;
rec_count_promises(Count, Id, N, [{Id, N, _, _} | RestQueue]) ->
    rec_count_promises((Count + 1), Id, N, RestQueue);
rec_count_promises(Count, Id, N, [_ | RestQueue]) ->
    rec_count_promises(Count, Id, N, RestQueue).

I am reading a codebase full of this sort of things, and it is really painful. I keep thinking back to how I would do it in C#:

public int CountPromises(int id, int n, Tuple<List<Record>, List<Record>> phase1)
{
     int count=0;
     foreach(var record in phase1.Item2)
       {
            if(record.Id == id && record.N == n)
                   count ++;
     }
     return count ;
}

Yet that is imperative, and involve mutating state. I agree, it is a huge improvement, but it can be made both functionally safe and easier to read:

phase1.Items2.Count(record => record.Id == id && record.N == n);

As I said, I am currently going through a codebase full of these sort of functions, and it is painful, annoying and irritating. I am not experienced enough with Erlang to be able to tell conclusively if this is idiomatic Erlang code, but I think so.

Comments

Paul Cowan
05/11/2010 09:21 AM by
Paul Cowan

Did the Erlang code come from couchdb?

If so, I would be curious to know what benefit there is in rewriting couchdb on another platform?

Andrew Davey
05/11/2010 09:35 AM by
Andrew Davey

Writing out the recursion and pattern matching for this function sure feels like a waste of time.

It seems to me that the Erlang code should be re-factored to make use of higher level functions, like "filter".

Demis Bellot
05/11/2010 10:18 AM by
Demis Bellot

Yeah it's hard to see how this can can be more readable in anyone's mind. I'm aware the mathematical mind works differently and prefers the purity of functional programming without side-effects but does it work this different?

IMHO readability of code is just as important as succinctness, i.e. there's no point having really tight code with low signal/noise ratio if it takes others longer to scan and understand a page of code.

Max
05/11/2010 11:25 AM by
Max

This is horrible functional programming style. I'm not a Erlang wizard, so this is how I would write the function in Haskell:

countPromises id n = length . filter ((id', n') -> id' == id && n' == n) . snd

Much better! If you want it to look like C# you can also define:

count p = length . filter p

countPromises id n = count ((id', n') -> id' == id && n' == n) . snd

If you use GHC, all the intermediate lists wil be fused away, so this will actually compile into the tight imperative loop you wrote above.

tobi
05/11/2010 12:07 PM by
tobi

This cannot be idiomatic erlang code because it would mean that most erlang programmers are not very smart. That is hard to believe.

configurator
05/11/2010 01:29 PM by
configurator

Oh. I thought you said "idiotic" Erlang code.

I think yes, it is.

Bobo
05/11/2010 02:05 PM by
Bobo

countpromises(Id, N, {, PromisesQueue}) ->

lists:foldr(fun(X,ACC) -> {I,NN,,} = X, if I==Id , NN==N -> ACC+1; true -> ACC end end , 0, PromisesQueue).

NoComments
05/11/2010 09:02 PM by
NoComments

Probably no comments in the mess either.

Igor K.
05/11/2010 09:15 PM by
Igor K.

countpromises(Id, N, {, PromisesQueue}) ->

length(lists:filter(fun(X, Y, _, _}) -> X =:= Id andalso Y =:= N end, PromisesQueue)).

Apalmer
05/12/2010 02:50 PM by
Apalmer

Doesnt look idiomatic functional programming at all, it looks like a mechanical conversion of an imperative loop with mutating state to a recursive function call with accumulators. Which is basic functional programming 101, but in general once the user learns that they can do this relieably, they then move on to composing simple functions over lists to accomplish the same goal. Not sure its possible that maybe there is some performance gain in the erlang implementation if you feel this was programmed by advanced functional programmers. A lot of times when performance is taken into account, an advanced programmer has to strip out all the nice functional patterns and put in some worst of both worlds imperative/functional chimera

Comments have been closed on this topic.