This would not work for any type of value though: only the ones that are equal to their AST (numbers, atoms, strings…).
unquote(Bar.const()) might have to be replaced by unquote(Macro.escape(Bar.const())) if we need to support these other cases.
This works nicely but it is worth mentioning there is a small difference with when val == @const if @const is or contains a map (also true for the unquote version above):
defmodule Foo do
@x %{a: 1}
@doc """
iex> Foo.quux(%{a: 1, b: 2})
:partial_match
iex> Foo.quux(%{a: 1})
:exact_match
"""
def quux(x) when x == @x, do: :exact_match
def quux(@x), do: :partial_match
end
Depends what you’re comparing. As @sabiwara pointed out, maps can catch you off-guard.
I would just use a macro-ish code generator and call it a day:
defmodule Bar do
@constants %{
42 => "const",
220 => "MEGA const!"
}
def constants(), do: @constants
end
defmodule Foo do
for {key, val} <- Bar.constants() do
# This works fine but will be imprecise if you have constants that are maps
def quux(unquote(key)), do: IO.puts("hi #{unquote(val)}!")
# ...so do literal comparison if you're paranoid about constants being maps
def quux(key) when key == unquote(key), do: IO.puts("hi #{unquote(val)}!")
end
def quux(_), do: IO.puts("hi <unknown>!")
end
I am in half-agreement with @hauleth here: sure you don’t have all the code at a glance (because the functions in the module are being generated at compile time) and that might be confusing. At the same time, basic stuff like iterating over a collection and using unquote on its elements to do basic code generation is not hard to do and comprehend.
Most Elixir devs I’ve met had no problem with basic macro usage. And many teams use the above technique to generate code and save themselves from error-prone boilerplate copy-pasting.