Sometimes you have a process whose job is to do things with messages whose format you don’t know ahead of time or need to understand. Those processes need to handle any message you send them but at the same time be able to interpret control messages without catching anything that was actually intended to be data.
For example, it’s common to send a stop
message when you want the process to end itself by not recursing. If the process is forwarding messages to other processes, the stop
atom is something you’d very likely encounter. Stopping on that instead of forwarding it would be a failure.
The solution is to send and receive a tuple that’s going to be interpreted very narrowly by the pattern matcher, containing the following items:
?MODULE
– The preprocessor macro that expands to the name of the module containing the process’ codePID
– The ID of the process to which the message is addressedstop
– Whatever else the message needs to contain. In this case, it’s a single atom.
Erlang’s guard clauses can be used to make sure that if everything else looks correct, only tuples containing the ID of this specific process will be treated as a control message:
run() -> receive { ?MODULE, PID, stop } when PID == self() -> ok; _AnythingElse -> process(_AnythingElse), run() end
This means a stop
message bound for processes running other instances of this function can be processed by the process itself without causing undesired behavior.
Here’s a complete example:
-module(blogpost). -export([start/0, run/0]). run() -> receive { ?MODULE, PID, stop } when PID == self() -> io:fwrite("All done.~n"); _AnyOtherInput -> io:fwrite("Got input ~w~n", [_AnyOtherInput]), run() end. create() -> spawn(?MODULE, run, []). input(Object, Input) -> Object ! Input. destroy(Object) -> Object ! { ?MODULE, Object, stop }. start() -> Object = create(), [ input(Object, Input) || Input <- [foo, bar, baz] ], destroy(Object).
Recent Comments