You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 02:48:01