I am trying to define a macro like this:
defmodule MyModule do
defmacrop def_sort_order(name, order) do
quote do
Module.put_attribute(
__MODULE__,
unquote(name),
Enum.with_index(unquote(order)) |> Map.new()
)
end
end
def_sort_order(
:test_order,
[
:a,
:b,
:c
]
)
end
And then later in the module I would like to access the attribute in some function and it should look like this:
IO.inspect(@test_order)
%{a: 0, b: 1, c: 2}
But I keep getting undefined function def_sort_order/2 (there is no such import)
What I am doing wrong here?
You can’t call macros defined in a module in the same module’s body, it has to be within a def:
defmodule Foo do
defmacrop foo, do: (quote(do: "foo"))
def do_foo, do: foo()
end
To do what you’re looking to do, you would have to define your macro in another module (as defmacro) and require or import it in the modules you want to use it in. This is because you’re trying to call def_sort_order on compilation, but the module hasn’t been compiled yet.
3 Likes
A simple anonymous function declared and used at compile time would also work:
defmodule MyModule do
def_sort_order = fn name, order ->
Module.put_attribute(
__MODULE__,
name,
Enum.with_index(order) |> Map.new()
)
end
def_sort_order.(:test_order, [:a, :b, :c])
end
Although in that case, you might not even need any macro or function especially to call Module.put_attribute/3, this could just be simple module attributes:
defmodule MyModule do
@test_order Enum.with_index([:a, :b, :c]) |> Map.new()
end
or just a fn to generate the attribute:
defmodule MyModule do
sort_order = fn order -> Enum.with_index(order) |> Map.new() end
@test_order sort_order.([:a, :b, :c])
end
4 Likes