I have a list of batches(sublists) of ids and I want to iterate over this list and spawn a worker process for each id in the batch of ids. Each of these workers will query some service, get the result and send it back to the caller. In simple words I want to map a list of id
s to a list of data which I get by virtue of those id
s. I managed to achieve this but in, what I believe is, an unidiomatic way:
lists:map(fun(Ids) ->
Pids = [spawn_link(fun() ->
Result = [...] % Here goes a side-effect operation (http request)
Self ! {received_data, process(Result)}
end) || Id <- Ids],
[receive {received_data, Data} -> Data end || _Pid <- Pids],
end, JobChunks)))
In this case as you see I misuse map
function as it's designed to be side-effect free. But i don't see another option. There is foreach()
but it is used only to run side-effects and just returns ok
whereas in my case I want to preserve shape of a list too. In Haskell there is a handy type-class Traversable
with traverse
function which exactly does this: runs fmap
and in the same time allows you to perform action (effect) on each item. Is there something similar in Erlang? (like smap
perhaps?).
Erlang unlike Haskell is not a pure functional programming language. As a corollary it does not put restrictions on functions in terms of whether they can or can't have side effects. In Haskell even I/O subsystem can't break its purity and that's why there exists a distinction on a type-level between
Traversable
andFunctor
(traverse
andfmap
) where the former can run effects upon each element of the container and the latter can't. In Erlang there is no such clear distinction and, as a result, you may have a functionexecute(Container) ->
and you don't know whether it will or won't run effects by just gazing at its signature. That's why havingmap
andsmap
(ortraverse
, or whatever you call it) in Erlang does not make sense and does not bring any value whatsoever. But it is true that usinglists:map
for this sort of operation breaks a contract ofmap
which is supposed to be a pure function. In this kind of situation I may recommend you to use a list comprehension which in my opinion is a more idiomatic way:Again in my own viewpoint side effects thing is a major difference between list comprehensions and
lists:map()
. When they are used in the aforementioned way I ordinarily think of them as of Haskell's monad comprehensions.