【问题标题】:Elixir - How can I unquote an array of functions in my macro?Elixir - 如何在我的宏中取消引用一组函数?
【发布时间】:2016-09-05 06:14:25
【问题描述】:

免责声明:我知道可以以更简单的方式编写代码,但您应该明白我将简化的代码发布到 SO。

我有一个模块Simple,它使用Included:

defmodule Simple do
  use Included,
    events: [
      [ 
        name: :start,
        callback: fn(x) -> x * 2 end
      ], [
        name: :finish,,
        callback: fn(x) -> x * 3 ; x end
      ]
    ]
end

我希望Included 模块定义一个函数,该函数为上面列表中的每个项目获取一个参数并返回一个值。所以,我正在这样做:

defmodule Included do
  defmacro __using__(opts)
    events = Keyword.get(opts, :events)

    quote bind_quoted: [events: events] do
      events
      |> Enum.each(fn(event) ->
        def unquote(event[:name])(x) do
          x
          |> unquote(event[:callback]).()
          |> IO.puts
      end)
    end    
  end
end

这里的问题是我收到了invalid quoted expression: #Function<0.105634730。我试图以另一种方式实现它:

defmodule Included do
  defmacro __using__(opts)
    events = Keyword.get(opts, :events)

    events
    |> Enum.each(fn(event) ->
      quote bind_quoted: [event: event] do
        def unquote(event[:name])(x) do
          x
          |> event[:callback].()
          |> IO.puts
         end
      end
    end)
  end
end

但在这种情况下,我还没有看到定义的函数。 (没有错误,这里没有函数Simple.start/1和Simple.finish/1)。

我的问题是:

  1. 如何定义所需的功能?
  2. 为什么没有在第二种方法中定义函数?

【问题讨论】:

  • 一种有效的解决方法是在quote 之前转义所有事件的:callback 键:events = for event <- opts[:events] do; Keyword.update!(event, :callback, &Macro.escape/1); end。
  • @Dogbert 不幸的是我得到了cannot escape #Function<2.45959719/1 in Included.MACRO>. The supported values are: lists, tuples, maps, atoms, numbers, bitstrings, pids and remote functions in the format &Mod.fun/arity 我试图在 iex 中实现类似的东西,结果相同 =(

标签: metaprogramming elixir


【解决方案1】:

我不是 100% 确定为什么,但是在 Included.__using__/1 中的 quote 内部,函数的 AST 正在被转换为实际函数。如果您在quote 的开头添加IO.inspect(events),您将获得:

[[name: :start, callback: #Function<0.18558591 in file:c.exs>],
 [name: :finish, callback: #Function<1.18558591 in file:c.exs>]]

我为此找到的解决方法是转义事件中的:callback。

defmacro __using__(opts) do
  events = for event <- opts[:events] do
    Keyword.update!(event, :callback, &Macro.escape/1)
  end
  quote bind_quoted: [events: events] do
  ...
end

最终代码:

defmodule Included do
  defmacro __using__(opts) do
    events = for event <- opts[:events] do
      Keyword.update!(event, :callback, &Macro.escape/1)
    end
    quote bind_quoted: [events: events] do
      events
      |> Enum.each(fn(event) ->
        def unquote(event[:name])(x) do
          x
          |> unquote(event[:callback]).()
          |> IO.puts
        end
      end)
    end    
  end
end

defmodule Simple do
  use Included,
    events: [
      [ 
        name: :start,
        callback: fn(x) -> x * 2 end
      ], [
        name: :finish,
        callback: fn(x) -> x * 3 ; x end
      ]
    ]
end

Simple.start 10
Simple.finish 10

输出:

20
10

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-29
    • 2015-07-12
    • 2016-04-25
    • 2016-09-03
    • 1970-01-01
    相关资源
    最近更新 更多