灵丹妙药中的语言构造与宏

时间:2019-03-19 12:30:36

标签: macros elixir

我正在从https://elixirschool.com/en/lessons/basics/control-structures/学习控制结构 我注意到它提到了

  

如果以前有if / 2,您会遇到的机会;如果您使用过Ruby,则除非熟悉/ 2。在Elixir中,它们的工作方式大致相同,但它们被定义为宏,而不是语言构造。您可以在内核模块中找到它们的实现。

那么Elixir中的语言构造和宏之间有什么区别?什么时候需要编写宏?

1 个答案:

答案 0 :(得分:3)

宏是一种对编程语言进行编程的方法。简而言之,宏是一种生成程序代码的方法,而不是始终自己编写代码。

另一方面,一种语言构造(有时称为“特殊形式”)是长生不老药本身的核心。过于简单的做法是if的实现不是在Elixir中完成的,而是在实现Elixir的语言中完成的。

假设您要使用提到的unless

编辑:unless 在Elixir中可用。但是让我们假设其余的不是。

在Elixir中,没有unless的语言版本。 Jose Valim没有实现它。但是,您始终可以编写具有相同语义的内容:否定的if

我们想 拥有这个,但我们

unless sun_shines() do 
  open_umbrella()
end

但是我们只有一个if和一个not,所以我们可以这样写:

if not sun_shines() do 
  open_umbrella()
end

第二,宏是一种特殊的函数,但是它的参数是代码,执行宏的结果也是代码。假设我们有unless宏,它接受一个条件(即sun_shines())和一个主体(即open_umbrella()),并返回if not sun_shines(), do: open_umbrella()。因此,宏是可以在“无效代码”级别工作并生成“无效代码”的功能。

您可能会认为这太愚蠢了,无法为其编写宏。确实如此。但是这些类型的问题比您想象的要经常发生,因此宏是解决该问题的好方法。这只是编程您的编程语言的一种方法。

Aleksei Matiushkin提供了unless宏的示例实现:

defmodule MyMacros do
  defmacro unless(ast, do: block) do
    quote do
      if not unquote(ast) do
        unquote(block)
      end
    end
  end
end

在这里您可以清楚地看到给它一个AST(抽象语法树),它将把它转换为另一个AST(quote),并将其注入到调用宏的位置。请注意,这都是在编译时发生的。您的程序目前尚未执行!

例如,假设您具有上述模块,这是您的程序:

defmodule MyProgram do 
  def my_function(x) do 
    unless sun_shining() do 
      open_umbrella()
    end
  end
end

在编译之后和执行之前,您的程序将如下所示:

defmodule MyProgram do 
  def my_function(x) do 
    if not sun_shining() do 
      open_umbrella()
    end
  end
end

此阶段称为宏扩展阶段。

另外,您可以在这里找到分别在Elixir和ExUnit中使用的两个实际宏。

https://github.com/elixir-lang/elixir/blob/d48b16cf549eca0629449a47cc5574a7170706c3/lib/ex_unit/lib/ex_unit/assertions.ex#L104

https://github.com/elixir-lang/elixir/blob/13ced80fcda1bea69037aacd4b052a0c44b4be61/lib/elixir/lib/stream/reducers.ex#L58

注意:我一直在为该答案添加越来越多的信息。答案实际上值得一整本书,而 Chris McCord Metaprogramming Elixir是最好的一本书。