如何在Elixir的use宏中实现支持任意元数的默认behavior逻辑
问题根因
defdelegate 语法要求必须指定与函数元数匹配的参数列表,你当前的实现仅传递了函数名,没有生成对应元数的参数占位符,因此触发编译语法错误。
修正后的实现
你可以动态生成对应元数的参数列表传入defdelegate,完整修改后的代码如下:
defmodule BehaviorApi do @callback foo(integer()) :: {:ok, String.t()} end defmodule MyDefaultApiImplementation do @behaviour BehaviorApi def foo(num), do: {:ok, "foo-#{num}"} # 新增任意元数的函数都可以自动适配,无需修改宏逻辑 def bar(a, b), do: a + b end defmodule Api do defmacro __using__(_opts) do defaults = Enum.map(MyDefaultApiImplementation.__info__(:functions), fn {func_atom, func_arity} -> # 动态生成对应元数的参数占位符,比如arity=1生成[arg1],arity=2生成[arg1, arg2] args = Macro.generate_arguments(func_arity, __MODULE__) quote do defdelegate unquote(func_atom)(unquote_splicing(args)), to: MyDefaultApiImplementation defoverridable([{unquote(func_atom), unquote(func_arity)}]) end end) beh = quote do @behaviour BehaviorApi end [beh | defaults] end end
使用验证
你可以正常重写需要覆盖的函数,不会再触发编译错误:
defmodule MyOverrideApi do use Api # 仅重写需要mock的foo函数,其余未重写的函数默认走生产实现 def foo(num), do: {:ok, "stub-foo-#{num}"} end # 运行效果验证 MyOverrideApi.foo(123) # 返回 {:ok, "stub-foo-123"},走自定义重写逻辑 MyOverrideApi.bar(1,2) # 返回 3,走默认生产实现
这里用了Elixir标准库提供的Macro.generate_arguments/2 生成指定元数的唯一参数名,无需手动拼接参数,天然适配所有元数的函数。
内容的提问来源于stack exchange,提问作者Clay
相关产品推荐
相关产品推荐

