1
votes

I am new to Erlang and I am trying to understand how to send a message from one process to a list of processes.

Supposedly we have a data structure that holds a list with Pids. How can I make a Pid send a message "M" to a list of Pids, where each element of the list has 2 elements: a string (representing a name) and the Pid? What I have come up with is:

broadcast(P, M, R) ->
  P ! {self(), friends},
  receive
    {P, Friends} ->
  P ! {self(), {send_message, {M, R, P, Friends}}}
  end.

looper({Name, Friends, Messages}) ->
{From, {send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}} ->
  if R =< 0 ->
        From ! {From, {self(), {ID, M}}},
        looper({Name, [{FriendPid, FriendName} | FriendTale], [{ID, M} | Messages]});
     R > 0  andalso FriendTale =/= []->
       FriendPid ! {From, {send_message, {M, R-1, ID, FriendTale}}},
       looper({Name, FriendTale, [{ID, M} | Messages]})
  end;
 terminate ->
    ok
end.

But from what I understand is that I don't pattern match correctly the list of Pids so that I can "extract" the Pid from an element of the list of Pids.

Basically, I have a function called "looper" that is constantly waiting for new messages to arrive. When it receives a message of type

{send_message, {M, R, ID, [{FriendPid, FriendName} | FriendTale]}}

where "M" is the message that I want to broadcast to the list of Pids called "Friends" and R is just an integer.

The R is basically an integer saying how far the message should go.

e.g. 0 = broadcast the message to self,
     1 = broadcast the message to the friends of the pid,
     2 = broadcast the message to the friends of the friends of the pid and so on...

What I get from the terminal after I setup the Pid and set the "friendships" between the Pids is:

1> f().
ok
2> c(facein).
facein.erl:72: Warning: variable 'From' is unused
{ok,facein}
3> {Message, Pid} = facein:start({"Bjarki", [], []}).
{ok,<0.186.0>}
4> {Message, Pid2} = facein:start({"Bjarki2", [], []}).
{ok,<0.188.0>}
5> facein:add_friend(Pid,Pid2).
ok
6> facein:broadcast(Pid,"hello",1).

=ERROR REPORT==== 5-Oct-2014::12:12:58 ===
Error in process <0.186.0> with exit value: {if_clause,[{facein,looper,1,[{file,"facein.erl"},{line,74}]}]}

{<0.177.0>,{send_message,{"hello",1,#Ref<0.0.0.914>}}}

Any help would be greatly appreciated. Thanks

1
could you add loop definition (how are your arguments named to be exact).mpm
@mpm ok added the looper definitionsokras
You can consider to use gproc, it has a handy Pub/Sub module that let you to subscribe processes to an event and then publish a message to all the subscribers of the event.Eric

1 Answers

2
votes

EDIT

After you added brodcast function. What you are receiving sending to looper funciton is friends atom. You can not do list comprehension on atom, only on list. That's why you get bedarg when you try to use <- operator.

To break out your logic: You send your pid and atom to yourself, just to received one line later. Not sure why would you need to do that? You could just go straight with basically the same:

broadcast(P, M, R) ->
  P ! {self(), {send_message, {M, R, P, friends}}}.

And now you can clearly see, that it is atom, not a list of pids, that you are sending to looper.


Error you are getting suggest that you are calling some build-in Erlang function ( + ! ) with bad type. So I would guess that one of Friends is not a process, or R is not something you can - 1 on. Maybe try to print them out before list comprehension to debug.

You also could use guards like

receive
  {From, {send_message, {M, R, ID, Friends}}} when is_integer(R) ->
     %%  ...

but you will just ignore messages that do not pattern match.

minor notes

I'm not sure if that's what you are trying to do but those might also help.

One thing I can notice is fact that you are sending tuple {send_message, {M, R-1, ID, Friends}}. That's your whole message, and only this will be received. Erlang will not add anything magically, so if you are counting on receiving {From, {send_message, {M, R, ID, Friends}}} , you need to send this From by yourself. Like this F ! {self(), {send_message, {M, R-1, ID, Friends}}}

Other thing you might look out for is pattern matching in "longer" function. Friends variable is getting bound (assigned to a value) as function argument. So when you are doing your receive {From, {send_message, {M, R, ID, Friends}}} what you are doing, is pattern matching on message type (two-tuple, with two-tuple with four-tuple), atom send_message and Friends list. Meaning that you will execute you "send logic" only when you receive list of friends exactly the same that loop function initially was called with. And all the other messages (except terminate of course) will be ignored (will just linger in your message box). If you are counting on receiving some new friends, pattern match on unbound variables (keeping function short helps with this Erlang-gotcha).