Input and output in the Erlang VM are performed using I/O servers. These are simply Erlang processes that implement a low-level message interface. You never have to deal with this interface directly (which is a good thing, as it is complex). Instead, you use the various Elixir and Erlang I/O libraries and let them do the heavy lifting.
In Elixir you identify an open file or device by the PID of its I/O server. And these PIDs behave like all other PIDs—you can, for example, send them between nodes. If you look at the implementation of Elixir’s IO.puts function, you’ll see
| def puts(device \\ group_leader(), item) do |
| erl_dev = map_dev(device) |
| :io.put_chars erl_dev, [to_iodata(item), ?\n] |
| end |
(To see the source of an Elixir library module, view the online documentation at http://elixir-lang.org/docs/, navigate to the function in question, and click the Source link.)
The default device it uses is returned by the function :erlang.group_leader. (The group_leader function is imported from the :erlang module at the top of the IO module.) This will be the PID of an I/O server.
So, bring up two terminal windows and start a different named node in each. Connect to node one from node two, and register the PID returned by group_leader under a global name (we use :two).
Window #1 | ||||||||||
| ||||||||||
Window #2 | ||||||||||
|
Note that once we’ve registered the PID, we can access it from the other node. And once we’ve done that, we can pass it to IO.puts; the output appears in the other terminal window.
Window #1 | ||||||||||||
| ||||||||||||
Window #2 | ||||||||||||
|