Hi
I am generating code for modules from some data read in from file. The module names are formed from strings, and obviously I need to create module atoms. At first glance, prefixing the module name (string) with “Elixir.” and then using String.to_atom() seems to be the way to do it, but the atom produced that way is not working in code like the following:
quote do [{ unquote(module_atom), 123}] end
I have got around this by hand-crafting the AST for a module name, the following explains, but I am hoping there is a more elegant solution?
iex(1)> :"Elixir.A.B" == A.B
true
But:
iex(2)> quote do unquote(String.to_atom("Elixir.A.B")) end
A.B
iex(3)> quote do :"Elixir.A.B" end
A.B
iex(4)> quote do A.B end
{:__aliases__, [alias: false], [:A, :B]}
Hand-crafted AST:
iex(5)> {:__aliases__, [alias: false], String.split("A.B", ".") |> Enum.map(&String.to_atom/1)} == quote do A.B end
true
And:
iex(6)> module_name = "A.B"
"A.B"
iex(7)> module_atom = {:__aliases__, [alias: false], module_name |> String.split(".") |> Enum.map(&String.to_atom/1)}
{:__aliases__, [alias: false], [:A, :B]}
iex(8)> ast = quote do defmodule unquote(module_atom) do end; end
{:defmodule, [context: Elixir, import: Kernel], [{:__aliases__, [alias: false], [:A, :B]}, [do: nil]]}
iex(9)> Macro.to_string(ast)
"defmodule(A.B) do\n nil\nend"
iex(10)> ast2 = quote do [{ unquote(module_atom), 123}] end
[{{:__aliases__, [alias: false], [:A, :B]}, 123}]
iex(11)> Macro.to_string(ast2)
"[{A.B, 123}]"
As compared to:
iex(12)> module_atom_2 = String.to_atom("Elixir."<>module_name)
A.B
iex(13)> ast3 = quote do [{ unquote(module_atom_2), 123}] end
[{A.B, 123}]
iex(14)> Macro.to_string(ast3)
"[A.B:, 123]"
which is obviously wrong