首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Elixir宏中使用助手函数

在Elixir宏中使用助手函数
EN

Stack Overflow用户
提问于 2016-01-20 05:38:18
回答 2查看 1.7K关注 0票数 2

继续使用药剂文档中的绑定和引号片段示例..。

我们有一个宏,它基于关键字列表定义函数。

代码语言:javascript
复制
defmodule MacroFun do

  defmacro defkv(kv) do
    quote bind_quoted: [kv: kv] do
      Enum.each kv, fn {k, v} ->
        def unquote(k)(), do: unquote(v)
      end
    end
  end

end

defmodule Runner do
  require MacroFun

  kv = [foo: 1, bar: 2]
  MacroFun.defkv(kv)

end

Runner.foo

现在,让我们将宏的主体移到助手函数中。

代码语言:javascript
复制
  defmacro defkv(kv) do
    _defkv(kv)
  end

  defp _defkv(kv) do
    quote bind_quoted: [kv: kv] do
      Enum.each kv, fn {k, v} ->
        def unquote(k)(), do: unquote(v)
      end
    end
  end

太好了,一切都还能用。但是,如果我们想在将kv传递给私有助手函数之前,再创建另一个修改它的宏,该怎么办:

代码语言:javascript
复制
  defmacro def_modified_kv(kv) do
    quote bind_quoted: [kv: kv] do
      modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
      _defkv(modified_kv)
    end
  end

那不管用。灵丹妙药说,_devkv没有定义。我们可以通过使用完全限定的函数名来修复:

代码语言:javascript
复制
  defmacro def_modified_kv(kv) do
    quote bind_quoted: [kv: kv] do
      modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
      MacroFun._defkv(modified_kv)
    end
  end

但随后,她抱怨说,MacroFun._defkv是私有的。因此,我们将其更改为public,但它仍然不起作用,因为helper方法_devkv将引用的代码返回给我们的宏def_modified_kv,这个宏本身就是引用的!

因此,我们可以通过对助手函数返回的代码(最终代码)来修复这个问题:

代码语言:javascript
复制
defmodule MacroFun do

  defmacro defkv(kv) do
    _defkv(kv)
  end

  defmacro def_modified_kv(kv) do
    quote bind_quoted: [kv: kv] do
      modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
      MacroFun._defkv(modified_kv) |> Code.eval_quoted([], __ENV__) |> elem(0)
    end
  end

  def _defkv(kv) do
    quote bind_quoted: [kv: kv] do
      Enum.each kv, fn {k, v} ->
        def unquote(k)(), do: unquote(v)
      end
    end
  end

end
  1. 为什么我必须更改为调用助手函数的完全限定名?
  2. 为什么我必须将助手函数更改为公共的(从私有的)?
  3. 除了调用Code.eval_quoted之外,还有更好的方法来做到这一点吗?

我觉得我做错了什么。

谢谢你的帮助。

EN

回答 2

Stack Overflow用户

发布于 2016-01-24 05:37:17

  1. 您需要导入模块。
  2. 导入模块时,只能导入公共方法。
  3. 宏是一个编译时特性,它们实际上在模块中编写代码(或AST)。因此,宏展开时的上下文就是您调用/使用它的位置。因此,如果使用宏(未在SomeOtherModule中定义),则是宏展开的上下文。在SomeOtherModule中,您需要import MacroFun在本地调用它的函数。

下面的代码在Elixir >= 1.2.0中工作

例如。

代码语言:javascript
复制
defmodule MacroFun do

  defmacro defkv(kv) do
    _defkv(kv)
  end

  defp _defkv(kv) do
    quote bind_quoted: [kv: kv] do
      Enum.each kv, fn {k, v} ->
        def unquote(k)(), do: unquote(v)
      end
    end
  end

  defmacro def_modified_kv(kv) do
    quote bind_quoted: [kv: kv] do
      modified_kv = Enum.map kv, fn {k, v} -> {k, v + 1} end
      defkv(modified_kv)
    end
  end
end

defmodule Runner do
  import MacroFun

  kv = [foo: 1, bar: 2]
  def_modified_kv(kv)

end

希望这能回答你的问题!祝好运。

更新了几次,因为宏有点混乱!

票数 0
EN

Stack Overflow用户

发布于 2016-01-25 10:37:52

1)通过使用require,您已经告诉编译器编译并提供MacroFun公共宏和函数。请注意,函数无论如何都是可用的:

注意,通常在使用之前不应该需要模块,唯一的例外是如果您想使用模块中的宏。

2)它们可通过其FQN获得

3)以下是我对此的看法:

代码语言:javascript
复制
defmodule MicroFun do
  defmacro defkv(kv) do
    _defkv(kv)
  end

  defp _defkv(kv) do
    quote bind_quoted: [kv: kv] do
      Enum.each kv, fn {k,v} ->
        def unquote(k)(), do: unquote(v)
      end
    end
  end

  defmacro modifier(kv) do
    quote bind_quoted: [kv: kv] do
      Enum.map kv, fn {k,v} -> {k, v+1} end
    end
  end
end

defmodule Runner do
  require MicroFun

  [foo: 1, bar: 2]
  |> MicroFun.modifier
  |> MicroFun.defkv
end

这样做的想法是,您不需要嵌套宏/函数时,您可以只是管道。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34892313

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档