macros - 为什么动态调用会导致“未定义函数”?
问题描述
我有一个模块可以通过以下方式动态地将外部调用路由到它自己的函数:
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
在当前范围内按字面命名的函数,而不是test/1
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
输出:
!!!: :foo
要使其调用test/1
模块的功能A
,您可以执行以下操作& 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
推荐阅读
- python-3.x - 是否可以在另一个数据框的列中搜索两个数据框列的值并返回匹配的 ID?
- node.js - 我有 Express + Mongo + Mongoose 后端应用程序。如果您没有通过 Modal 创建文档,如何从 DB 获取或更新文档?
- dart - 什么可能导致所附图片中显示的错误?
- c++ - 如何计算此循环的所有数字的总和
- php - 无法让 Laravel Passport 与 Vapor 一起使用
- solr - 带有空格的 Solr 查询以及查询和操作
- amazon-web-services - 如何使用 terraform 连接不同 VPC 中的副本 Postgres RDS 及其源?
- laravel - 为什么我在进行 Laravel 项目时需要公开宣传
- amazon-web-services - 每个 VPC 端口警报的 aws 网关负载均衡器终端节点
- c# - 创建一个接受类型并返回该类型实例的泛型方法