The warning you see is an Erlang bug. If Erlang sees you are invoking a function in a literal tuple, it shows the warning. I have seen this while working with Elixir, I have silenced it in Elixir's compiler but forgot to report it to the Erlang team as a bug. Sorry.
The stateful module thing is actually avoided in Erlang by the majority of developers. They were added to support a feature called "parameterized modules", which has then since been removed, but the underlying dispatching mechanism still exists. If you search the Erlang Questions mailing list you can find plenty of discussion on the topic. Note that protocols in Elixir are not implemented like that though.
In fact, it seems your implementation does not seem to add anything compared to a regular function. For example, you could have simply written:
to_string(V) when is_list(V); is_atom(V) ->
Buffer = io_lib:format("~p",[V]),
lists:flatten(Buffer);
to_string(V) ->
not_implemented.
and called the function directly. Your implementation is simply using the classic ad-hoc polymorphism provided by Erlang at the end of the day. The limitation of this approach is that, since dispatch is hardcoded to ?stringer, the only way to extend the to_string/1
behaviour to work with a new data type is to reimplement and replace the whole stringer
module.
Here is an example of an issue that helps you ponder about this: if application A defines a "protocol" named stringer
, how can applications B and C extend this protocol to their own data types and all be used by application D without loss of functionality?
In very simple words, the way protocols work in Elixir is by making the stringer module an intermediate dispatch module. So the stringer module actually works like this:
to_string(V) when is_list(V) ->
string_list:to_string(V);
to_string(A) when is_atom(A) ->
string_atom:to_string(A);
%% ...
to_string(A) when is_tuple(A) ->
string_tuple:to_string(A).
and imagine that code is wrapped around something that checks if the module exists and fails accordingly if not. Of course, all of that is defined automatically for you by simply defining the protocol. There is also a mechanism (called consolidation) to compile protocols down to a fast dispatch mechanism on releases.