I’ve been heavily working with macros for more than 10 years already and I always used kinda ad-hoc code to expand/debug/test those when in trouble.
So yeah, it’s better to get a reputation of a slowpoke rather than to spend another 10 years fighting the ghosts.
Welcome ExPanda library that expands macros all the turtles down (short of defmodule and def/defp/defmacro/defmacrop structural forms.)
It’s time to take a look at what use GenServer does actually become in your code:
iex|🌢|1 ▶ "defmodule M, do: use GenServer"
|> ExPanda.expand_to_string()
|> elem(1)
|> IO.puts()
defmodule M do
require GenServer
opts = []
@behaviour GenServer
case :erlang.not(Module.has_attribute?(M, :doc)) do
false ->
nil
true ->
Module.__put_attribute__(
M,
:doc,
{0,
"Returns a specification to start this module under a supervisor.\n\nSee `Supervisor`.\n"},
nil,
[]
)
end
def child_spec(init_arg) do
default = %{id: M, start: {M, :start_link, [init_arg]}}
Supervisor.child_spec(default, unquote(Macro.escape(opts)))
end
defoverridable child_spec: 1
@before_compile GenServer
@doc false
def handle_call(msg, _from, state) do
proc =
case Process.info(:erlang.self(), :registered_name) do
{_, []} -> :erlang.self()
{_, name} -> name
end
case :erlang.phash2(1, 1) do
0 ->
:erlang.error(
RuntimeError.exception(
<<"attempted to call GenServer ", String.Chars.to_string(inspect(proc))::binary,
" but no handle_call/3 clause was provided">>
),
:none,
error_info: %{module: Exception}
)
1 ->
{:stop, {:bad_call, msg}, state}
end
end
@doc false
def handle_info(msg, state) do
proc =
case Process.info(:erlang.self(), :registered_name) do
{_, []} -> :erlang.self()
{_, name} -> name
end
:logger.error(
%{label: {GenServer, :no_handle_info}, report: %{module: M, message: msg, name: proc}},
%{
domain: [:otp, :elixir],
error_logger: %{tag: :error_msg},
report_cb: &:erlang./(GenServer.format_report(), 1)
}
)
{:noreply, state}
end
@doc false
def handle_cast(msg, state) do
proc =
case Process.info(:erlang.self(), :registered_name) do
{_, []} -> :erlang.self()
{_, name} -> name
end
case :erlang.phash2(1, 1) do
0 ->
:erlang.error(
RuntimeError.exception(
<<"attempted to cast GenServer ", String.Chars.to_string(inspect(proc))::binary,
" but no handle_cast/2 clause was provided">>
),
:none,
error_info: %{module: Exception}
)
1 ->
{:stop, {:bad_cast, msg}, state}
end
end
@doc false
def terminate(_reason, _state) do
:ok
end
@doc false
def code_change(_old, state, _extra) do
{:ok, state}
end
defoverridable code_change: 3, terminate: 2, handle_info: 2, handle_cast: 2, handle_call: 3
end
Enjoy!






















