списки: карта с побочными эффектами в Erlang - PullRequest
0 голосов
/ 04 ноября 2018

У меня есть список пакетов (подсписков) идентификаторов, и я хочу перебрать этот список и запустить рабочий процесс для каждого идентификатора в пакете идентификаторов. Каждый из этих работников запросит какую-либо службу, получит результат и отправит его обратно вызывающей стороне. Проще говоря, я хочу отобразить список id s в список данных, которые я получаю благодаря этим id s. Мне удалось этого добиться, но, как я полагаю, однотипным путем:

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)))

В этом случае, как вы видите, я неправильно использую функцию map, так как она разработана без побочных эффектов. Но я не вижу другого варианта. Существует foreach(), но он используется только для запуска побочных эффектов и просто возвращает ok, тогда как в моем случае я хочу сохранить форму списка тоже. В Haskell есть удобный класс типов Traversable с функцией traverse, который точно делает это: запускает fmap и в то же время позволяет выполнять действие (эффект) над каждым элементом. Есть ли что-то похожее в Эрланге? (вроде smap возможно?).

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Мне нравится ответ @Oleksandr, но использование блока begin..end в пределах понимания списка немного грязно . Я бы использовал для этого функции.

Также важно отметить, что вторая часть его ответа не гарантирует соблюдение порядка первоначального списка (т.е. он будет просто иметь такое же количество элементов, но они будут отсортированы в соответствии с порядком их поступления). ). Это может быть хорошо для вас, но если вы хотите иметь возможность сопоставлять входы (идентификаторы) и выходы (результаты), вы должны использовать выборочные приемы, как я покажу вам ниже.

Итак, вот как я бы реализовал это без OTP (поскольку вы также не используете OTP):

your_function() ->
    [process_chunk(Ids) || Ids <- JobChunks].

process_chunk(Ids) ->
    Pids = [spawn_side_effect_fun(Id) || Id <- Ids],
    [get_result_for(Pid) || _Pid <- Pids].

spawn_side_effect_fun(Id) ->
    Self = self(),
    spawn_link(fun() ->
        Self ! {received_data, self(), your_side_effect_operation()}
    end).

get_result_for(Pid) ->
    receive
        %% Here we're pattern-matching on Pid
        %% so that we get the result for this particular Pid
        %% therefore the order is preserved in the final list.
        {received_data, Pid, Data} -> Data
    end.

Также важно отметить, что вы здесь не обрабатываете никаких ошибок. Поскольку вы не перехватываете выходы, ошибка в порожденном процессе просто уничтожит основной.

0 голосов
/ 04 ноября 2018

Erlang в отличие от Haskell не является pure функциональным языком программирования. Как следствие, он не накладывает ограничений на функции с точки зрения того, могут ли они иметь побочные эффекты или нет. В Haskell даже подсистема ввода-вывода не может нарушить свою чистоту, и поэтому существует различие на уровне типов между Traversable и Functor (traverse и fmap), где первое может воздействовать на каждого элемент контейнера, а последний не может. В Erlang нет такого четкого различия, и, как следствие, у вас может быть функция execute(Container) ->, и вы не знаете, будет ли она запускать эффекты, просто глядя на свою подпись. Вот почему наличие в Эрланге map и smap (или traverse, или как бы вы это ни называли) не имеет смысла и не приносит никакой ценности вообще. Но это правда, что использование lists:map для такого рода операций нарушает контракт map, который должен быть чистой функцией. В такой ситуации я могу порекомендовать вам использовать понимание списка, которое, на мой взгляд, является более идиоматическим:

[begin
    Pids = [spawn_link(fun() ->
        % Side-effect operation which worker performs
    end) || Id <- Ids],
   [receive {received_data, Data} -> Data end || _Pid <- Pids]
end || Ids <- JobChunks].

Опять же , с моей точки зрения, . Побочные эффекты - это существенная разница между пониманием списка и lists:map(). Когда они используются вышеупомянутым способом, я обычно думаю о них как о монадских пониманиях Хаскелла.

...