Elixir:使用语句中的动态参数

Elixir: dynamic parameter in use statement

如何将 adapter 参数动态传递给 Nebulex.Cache 模块初始化?

defmodule MyApp.RedisCache do    
  use Nebulex.Cache,
      otp_app: :nebulex,
      adapter: if true do NebulexRedisAdapter else Nebulex.Adapters.Nil end
end

最简单的方法是将有条件的上移一级。

defmodule MyApp.RedisCache do    
  if true do
    use Nebulex.Cache,
      otp_app: :nebulex,
      adapter: NebulexRedisAdapter
  else
    use Nebulex.Cache,
      otp_app: :nebulex,
      adapter: Nebulex.Adapters.Nil
  end
  ...
end

另一种方法是创建一个带有参数的私有宏,或者,创建一个存储 adapter 的模块属性并将其传递给 use(假设 Nebulex.Cache Macro.expand/2 个参数在 __CALLER__ 上下文中。)


原因是宏接收 AST,所以 __using__/1 接收的是 quote do: if ....,在第一个编译阶段甚至不会尝试编译。


下面的代码可能会阐明 AST 在调用之间的传递方式以及 Macro.expand/2 的作用。

defmodule M do
  @param if(true, do: M1, else: M2)

  defmacrop t1(arg: args) do
    IO.inspect(args, label: "#1")
  end
  defmacrop t2(arg: args) do
    IO.inspect(Macro.expand(args, __CALLER__), label: "#2")
  end

  def test do
    t1(arg: if(true, do: M1, else: M2))
    t2(arg: if(true, do: M1, else: M2))
    t1(arg: @param)
    t2(arg: @param)
  end
end

结果是:

#1: {:if, [line: 19],
 ...
#2: {:case, [optimize_boolean: true],
 ...
#1: {:@, [line: 21], [{:param, [line: 21], nil}]}
#2: M1

请注意,if 已扩展为 case,并且模块属性不会扩展,除非对其调用显式 Macro.expand/2