2017-05-20 32 views
1

我試圖定義裏面defmacro動態功能,但不明白爲什麼函數值是不可用的內部函數本身仙丹定義內宏觀

defmacro __using__(_) do 
    Enum.each ~w(public private), fn value -> 
    def unquote(:"make_#{value}")(user = %User{}) do 
     %{user | privacy: value} 
    end 
    end 
end 

藥劑默認value擴展到value(),然後說,動態功能有是不是這樣的功能

回答

0

你錯過了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"} 
+0

但是如果我需要添加更多的功能到動態生成的'def'的同一級別呢? – Moldaone

+0

你可以在'quote'裏放置任意數量的'def'。 – Dogbert

+0

問題是'__using__'函數需要AST定義列表,所以如果我聲明多個'quote',那麼只有最後一個被採用。 – Moldaone