你錯過了quote
圍繞def
。您還需要取消在def
內的地圖更新表達式中的值。最後一點,你需要在這裏使用Enum.map
代替Enum.each
,使__using__/1
返回構造的AST:
defmacro __using__(_) do
Enum.map ~w(public private), fn value ->
quote do
def unquote(:"make_#{value}")(user = %User{}) do
%{user | privacy: unquote(value)}
end
end
end
end
測試:
defmodule User do
defstruct [:privacy]
end
defmodule A do
defmacro __using__(_) do
Enum.map ~w(public private), fn value ->
quote do
def unquote(:"make_#{value}")(user = %User{}) do
%{user | privacy: unquote(value)}
end
end
end
end
end
defmodule B do
use A
end
iex(1)> %User{} |> B.make_public
%User{privacy: "public"}
編輯:在意見中的要求變化:
defmacro __using__(_) do
Enum.map ~w(public private), fn value ->
quote do
def unquote(:"make_#{value}")(user = %User{}) do
%{user | privacy: unquote(value)}
end
def unquote(:"make_#{String.upcase(value)}")(user = %User{}) do
%{user | privacy: unquote(String.upcase(value))}
end
end
end
end
iex(1)> %User{} |> B.make_public
%User{privacy: "public"}
iex(2)> %User{} |> B.make_PUBLIC
%User{privacy: "PUBLIC"}
iex(3)> %User{} |> B.make_private
%User{privacy: "private"}
iex(4)> %User{} |> B.make_PRIVATE
%User{privacy: "PRIVATE"}
但是如果我需要添加更多的功能到動態生成的'def'的同一級別呢? – Moldaone
你可以在'quote'裏放置任意數量的'def'。 – Dogbert
問題是'__using__'函數需要AST定義列表,所以如果我聲明多個'quote',那麼只有最後一個被採用。 – Moldaone