Erlang: pmap with max number of processes I'm working through Joe Armstrong's Programming Erlang 2nd E. The book has exercises at the end of each chapter. Chapter 26, Question 5 is:


  Write a function called pmap(F, L, Max) that computes the list [F(I) || I &lt;- L] in parallel but is subject to the restriction that no more than Max parallel processes run simultaneously.


My solution is:

-module(pmap_cap).
-export([pmap/3]).

pmap(F, L, Max) -&gt;
    S = self(),
    Ref = make_ref(),
    Pids = lists:map(fun(SubL) -&gt;
                             spawn(fun() -&gt; do_f(S, Ref, F, SubL) end)
                     end,
                     partition(L, min(length(L), Max))),
    gather(Pids, Ref).

partition(L, N) -&gt;
    M = length(L),
    if
        M =&lt; N -&gt; lists:map(fun(X) -&gt; [X] end, L);
        true -&gt; partition(L, M div N, M rem N)
    end.

partition([], _Q, _R) -&gt; [];
partition(L, Q, R) -&gt;
    Extra = if R &gt; 0 -&gt; 1; true -&gt; 0 end,
    [lists:sublist(L, Q + Extra)|
     partition(lists:sublist(L, Q + Extra + 1, length(L)), Q, R-1)].

do_f(Parent, Ref, F, SubL) -&gt;
    Parent ! {self(), Ref, lists:map(fun(X) -&gt; catch F(X) end, SubL)}.

gather([Pid|T], Ref) -&gt;
    receive
        {Pid, Ref, Ret} -&gt;
            lists:append(Ret, gather(T, Ref))
    end;
gather([], _) -&gt; [].


What could be improved? Are there performance issues? Can the code be written more idiomatically?
General


Use standard library as many as possible, usually it given better result therefore function partition can be easily rewritting with wrapper on lists:split/2 and it will be consider as perfomance improvement

split(List,Max)-&gt;
    L = length(List),
    split_by(List,L rem Max + L div Max,[]).

split_by([],_N,R)-&gt;lists:reverse(R);
split_by(List,N,R)-&gt;
    {Part,NewList} = lists:split(N,List),
    split_by(NewList,N,[Part|R]).



Idiomaticness


Add to head of list and reverse instead append. For explanation - SO question.
Tail recursion better for readability and may be faster, so function gather become:

gather([Pid|T], Ref,R) -&gt;
    receive
        {Pid, Ref, Ret} -&gt; gather(T, Ref,[Ret|R])
    end;
gather([], _,R) -&gt; lists:flatten(lists:reverse(R)).

List comprehensions instead lists:map/2:

Min = min(length(L), Max),
Pids = [spawn(
    fun() -&gt; do_f(S, Ref, F, SubL) end) || SubL &lt;- split(L, Min)],
...