为什么动态调用会导致“undefined function”?
Why the dynamic call results in “undefined function”?
我有一个模块可以通过以下方式将外部调用动态路由到它自己的函数:
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}}
模块编译成功,A.test/1
存在。
A.test :foo
#⇒ test: :foo
现在我尝试将其命名为:
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
这个动态调用调度有什么问题,为什么错误消息与实际情况相矛盾?
错误消息具有误导性。 & unquote(:"A.#{name}")(&1)
将在当前范围内调用字面上名为 A.test
的函数,而不是模块 A
:
的 test/1
函数
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
输出:
!!!: :foo
要让它调用模块A
的test/1
函数,你可以& 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
输出:
test: :foo
我有一个模块可以通过以下方式将外部调用动态路由到它自己的函数:
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}}
模块编译成功,A.test/1
存在。
A.test :foo
#⇒ test: :foo
现在我尝试将其命名为:
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
这个动态调用调度有什么问题,为什么错误消息与实际情况相矛盾?
错误消息具有误导性。 & unquote(:"A.#{name}")(&1)
将在当前范围内调用字面上名为 A.test
的函数,而不是模块 A
:
test/1
函数
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
输出:
!!!: :foo
要让它调用模块A
的test/1
函数,你可以& 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
输出:
test: :foo