Sometimes an argument to a macro is to be used as the name of a function being created with def
. That’s straightforward[*].
But suppose the name of the function is also to be used as an atom within the body of a function. (Think of using the name to look up a value in the application environment.) Is there a better way to do that than what I show below?
Example 1: config2 from_config2
The config2
macro is to define a zero-argument function from_config2
that returns :from_config2
. The following implementation works:
defmacro config2(quoted_name) do
# quoted_name is this tuple: `{:from_config_2, [line: 75], nil}`
name_atom = elem(quoted_name, 0)
quote do
def unquote(quoted_name), do: unquote(name_atom)
end
end
Plucking a value from a tuple (line 3) seems rather slimy. Is there a better way?
Example 2: config3 :from_config3
This is the same as the above, except that the macro argument is an explicit atom (and thus not wrapped in a tuple). So I can do the wrapping, as in line 3 below:
defmacro config3(name_atom) do
# name_atom is just `:from_config_3`
quoted_name = {name_atom, [], nil}
quote do
def unquote(quoted_name), do: unquote(name_atom)
end
end
Is there a better way?
[*] If you haven’t done it before, defining a function in a macro looks something like this:
defmacro defchain(head, do: body) do
quote do
def unquote(head) do
_called_for_side_effect = unquote(body)
unquote(value_arg(head))
end
end
end
One note: guards make a function definition’s syntax tree a little weird. So if you want to write macros that handle this:
defchain assert_field(kvs, list) when is_list(list) do
assert_fields(kvs, list)
end
… you might have to do some processing like this. That’s the definition of value_arg
as used above.