Is node/0 a pure function?
Thinking about Erlang functions in Haskell types
2018-03-21
I had a very interesting debate with a co-worker today.
I’d pointed out that the following filter expression felt like it was doing
unnecessary IO
, in the Haskell sense:
Enum.filter(fn {pid, _} -> :erlang.node(pid) == node() end)
The argument was simply that node/0
was not a pure function, and using such a
function as a predicate to Enum.filter/2
felt somewhat inelegant. It was an
off-hand comment, and I didn’t think much about it, until the author of the
code in question challenged me on what it means for a function to be pure.
The gist of the argument was: since node()
always returns the same thing
throughout the run of a program on a given instance of the BEAM, is that not
the same as purity, for all practical purposes?
All my theoretical arguments (like how running the code on different machines
yields different results) seemed unconvincing to the colleague. His argument
about node()
being effectively a global constant in the context of the VM
seemed to suffice for him to treat it like a pure function.
When theory fails (or the theorizer is not convincing enough), practice comes
to the rescue. Here is a code snippet which proves that node()
is not pure,
because it implicity depends on the state of the net_kernel
application:
erl -eval "
{ok, _} = net_kernel:start(['hello@kitty', shortnames]),
erlang:display(node()),
net_kernel:stop(),
{ok, _} = net_kernel:start(['bye@kitty', shortnames]),
erlang:display(node()).
" -s erlang halt
This will output two different values for node()
:
Eshell V8.3.5.3 (abort with ^G)
1> 'hello@kitty'
'bye@kitty'
This demonstrates that node/0
is not a pure function, even in the
loose-but-practical sense of always returning the same value during one run of
a program.
For comparison, the somewhat-analogous
function
in Haskell does indeed have the type IO Hostname
.