Using such macros has still the same issue - you have opaque integers passing around at runtime - this makes it really hard to debug issues, especially if dealing with large numbers.
As to when do conversions - I spoke about boundaries of the system. By that I mean every time we receive data from outside or send it out - this means params from requests, database responses, etc. For example, when using ecto, you can provide a custom type to do the conversions for you (assuming encode and decode functions as before):
defmodule Constant do
@behaviour Ecto.Type
def type, do: :integer
def cast(int) when is_integer(int), do: {:ok, decode(value)}
def cast(atom) when is_atom(atom), do: {:ok, atom)
def cast(_), do: :error
def load(int) when is_integer(int), do: {:ok, decode(value)}
def load(_), do: :error
def dump(atom) when is_atom(atom), do: {:ok, encode(value)}
def dump(_), do: :error
end


















