Hello, I want to load a alias
under my __using__
, but with opts
help like this:
defmacro __using__(opts) do
quote(bind_quoted: [opts: opts]) do
behaviour = Keyword.get(opts, :behaviour)
alias behaviour
end
end
It shows me invalid argument for alias, expected a compile time atom or alias, got: behaviour
error, if I use alias unquote(behaviour)
, it shows me unquote called outside quote
.
It should be noted, for @behaviour
it works if I do like this, @behaviour behaviour
but for alias
doesnât work.
How can I fix this? Thanks
1 Like
Youâll need to pull out the module in the macro body and not in the quoted return. It needs to look like this:
alias unquote(module)
Anything where the alias macro doesnât receive the module name directly, but e.g. the ast of a variable wonât work.
2 Likes
To be honest, I did not understand, I know it doesnât work even with unquote
this is whole my code:
defmacro __using__(opts) do
quote(bind_quoted: [opts: opts]) do
use GenServer, restart: :transient
require Logger
alias MishkaInstaller.{PluginState, Hook}
module_selected = Keyword.get(opts, :module)
initial_entry = Keyword.get(opts, :initial)
behaviour = Keyword.get(opts, :behaviour)
event = Keyword.get(opts, :event)
@ref event
alias unquote(behaviour) # doesn't work
@behaviour behaviour
# Start registering with Genserver and set this in application file of MishkaInstaller
def start_link(_args) do
GenServer.start_link(unquote(module_selected), %{id: "#{unquote(module_selected)}"}, name: unquote(module_selected))
end
def init(state) do
{:ok, state, 300}
end
# This part helps us to wait for database and completing PubSub either
def handle_info(:timeout, state) do
if is_nil(Process.whereis(MishkaHtml.PubSub)) do
{:noreply, state, 100}
else
unquote(module_selected).initial(unquote(initial_entry))
{:noreply, state}
end
end
end
end
If I understand a little about what you said, I can not get module name from a list in using
and pass it in alias
! Yes!?, so what is your suggestion in this code!!
One thing people usually have a hard time to grok is that macros only deal with AST â or âcodeâ. Even if things look like a variable they donât really represent the value assigned to the variable.
defmacro __using__(opts) do
quote(bind_quoted: [opts: opts]) do
behaviour = Keyword.get(opts, :behaviour)
alias behaviour
end
end
Doing this means alias
(also a macro) will receive the AST for the behaviour
variable (something like this: {:behaviour, [], Elixir}
). So it has no idea what module itâs supposed to alias.
defmacro __using__(opts_ast) do
behaviour = select_module_from_opts(opts_ast)
quote do
alias unquote(behaviour)
end
end
This on the other hand pulls out the module out of the AST your macro received and creates AST (again a representation of code), where alias
receives the module as an argument instead of a variable.
The tricky part here is select_module_from_opts
, because again opts
is AST not necessarily a plain value to consume. Some values represent themselves in AST â no difference between the AST and the value represented â so theyâre rather easy to deal with (e.g. many keyword lists with simple values) , but others are not as simple, e.g. maps or anything containing other variables.
6 Likes
Thank you for your complete explanation. The subject is beyond my comprehension, and Iâve refrained from using alias. 
1 Like