Reputation: 121010
I have a module that dynamically routes external calls to it’s own functions in the following way:
defmodule A do
defmacro call(name) do
quote do
fun = & unquote(:"A.#{name}")(&1)
fun.(:foo)
end
end
def test(param), do: IO.inspect(param, label: "test")
end
#⇒ {:module, A, ..., {:test, 1}}
The module was successfully compiled and A.test/1
is there.
A.test :foo
#⇒ test: :foo
Now I try to call it as:
defmodule B do
require A
def test, do: A.call(:test)
end
#⇒ ** (CompileError) iex:21: undefined function A.test/1
# (stdlib) lists.erl:1338: :lists.foreach/2
# (stdlib) erl_eval.erl:677: :erl_eval.do_apply/6
# (iex) lib/iex/evaluator.ex:249: IEx.Evaluator.handle_eval/5
What is wrong with this dynamic call dispatch and why the error message contradicts the reality?
Upvotes: 1
Views: 96
Reputation: 222388
The error message is misleading. & unquote(:"A.#{name}")(&1)
will call a function literally named A.test
in the current scope, not the test/1
function of module A
:
defmodule A do
defmacro call(name) do
quote do
fun = & unquote(:"A.#{name}")(&1)
fun.(:foo)
end
end
def unquote(:"A.test")(param), do: IO.inspect(param, label: "!!!")
end
defmodule B do
require A
import A
def test, do: A.call(:test)
end
B.test
Output:
!!!: :foo
To make it call the test/1
function of module A
, you can do & A.unquote(:"#{name}")(&1)
:
defmodule A do
defmacro call(name) do
quote do
fun = & A.unquote(:"#{name}")(&1)
fun.(:foo)
end
end
def test(param), do: IO.inspect(param, label: "test")
end
defmodule B do
require A
def test, do: A.call(:test)
end
B.test
Output:
test: :foo
Upvotes: 2