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