elixir定义宏内的动态函数

时间:2017-05-20 11:17:52

标签: elixir

我试图在defmacro内定义动态函数,但无法理解函数本身内部函数值不可用的原因

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

Elixir默认情况下会value扩展为value(),然后说没有这样的功能

1 个答案:

答案 0 :(得分: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"}