Every process can send another process a message if it knows how to reference it. Obviously that’s not doing being done willy-nilly but it’s a feature that is widely used and is a core tenet of the OTP.
You can send a message to process_a that tells it to send a message to process_b.
Here’s a quick example where the iex process sends a message to process_a which then sends that message to process_b along with the iex process pid so that process_b can notify the iex process that the message was received.
defmodule ProcessA do
def start() do
spawn fn -> run() end
end
def run do
receive do
{:send, recipient_pid, {origin_pid, message}} -> send(recipient_pid, {origin_pid, message})
end
run()
end
end
defmodule ProcessB do
def start do
spawn fn -> run() end
end
def run do
receive do
{origin_pid, message} -> send(origin_pid, {self(), message})
end
run()
end
end
iex(4)> iex_pid = self()
#PID<0.113.0>
iex(5)> process_a = ProcessA.start
#PID<0.134.0>
iex(6)> process_b = ProcessB.start
#PID<0.135.0>
iex(7)> send(process_a, {:send, process_b, {iex_pid, "hello world"}})
{:send, #PID<0.135.0>, {#PID<0.113.0>, "hello world"}}
iex(8)> flush
{#PID<0.135.0>, "hello world"}
:ok
I’m not sure but based on your naming scheme I think you may be conflating modules and processes. This is a fairly common mistake. A process is a distinct, self-contained concept that runs code and stores state whereas modules are merely static bags of functions that can be call from any process.
Every process has a pid, if you know this pid you can send a message to it.
That is what you are doing when you write process_b = ProcessB.start.
You are saving the pid in the variable process_b.
There are several methods that allow you to make the pid of one process available to another, for example using the Registry.