Closed eksperimental closed 6 years ago
I got stuck trying to create a macro that will solve this limitation..
defmodule MacroExperiment do
defmacro apply_macro(module, macro_name, args) do
quote do
unquote(module).unquote(macro_name)(unquote_splicing(args))
end
end
end
this works when passing the values directly, but when calling storing them in a variable, it won't.
defmodule MacroExperimentTest do
use ExUnit.Case
require Integer
import MacroExperiment
test "apply_macro" do
# it works
assert apply_macro(Integer, :is_even, [2]) == true
refute apply_macro(Integer, :is_even, [3])
module = Integer
macro_name = :is_even
args = [2]
# it does not work
assert apply_macro(module, macro_name, [2]) == true
# raises: ** (UndefinedFunctionError) function Integer.macro_name/1 is undefined or private
assert apply_macro(module, macro_name, args) == true
# ** (ArgumentError) expected a list with quoted expressions in unquote_splicing/1, got: {:args, [line: 19], nil}
# (elixir) src/elixir_quote.erl:114: :elixir_quote.argument_error/1
# (elixir) src/elixir_quote.erl:95: :elixir_quote.list/2
# (macro_experiment) expanding macro: MacroExperiment.apply_macro/3
# test/macro_experiment_test.exs:19: MacroExperimentTest."test apply_macro"/1
# (ex_unit) expanding macro: ExUnit.Assertions.assert/1
# test/macro_experiment_test.exs:19: MacroExperimentTest."test apply_macro"/1
# (elixir) lib/code.ex:677: Code.require_file/2
# (elixir) lib/kernel/parallel_compiler.ex:201: anonymous fn/4 in Kernel.ParallelCompiler.spawn_workers/6
end
end
any hints how to solve this?
You can’t. A macro needs to be invoked at compile time. You can’t invoke it at runtime, so you can’t put the values you are going to invoke in variables. --
José Valimwww.plataformatec.com.br http://www.plataformatec.com.br/Founder and Director of R&D
Well, you 'could' write something that could invoke it at run-time (macro's are just specially named functions after all), but you'd get AST back, which is not as useful at runtime unless you want to run it through the interpreter or something (and you'd probably need to fake a chunk of the __CALLER__
environment too), which is doable though slower (and you'd need to build up the interpreter environment).
Honestly though, instead of asking how to apply a macro directly via run-time variables, you should post what you are actually trying to accomplish (not 'how') on the forums. :-)
https://en.wikipedia.org/wiki/XY_problem
The XY problem: You want to do X, but don't know how. You think you can solve it using Y, but don't know how to do that either. You ask about Y, which is a strange thing to want to do. Just ask about X.
Environment
Been banging my head for a while trying to figure this one out. It's either a bug or a limitation that is not properly documented.
Currently it is impossible to call a macro from
apply/3
the error is misleading since it has been required. The only (partial) workaround I was able to come up with was to run:
Expected behaviour: It should be either documented or fixed