【问题标题】:Language constructs vs Macros in elixir长生不老药中的语言结构与宏
【发布时间】:2019-03-20 05:32:51
【问题描述】:

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

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

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

【问题讨论】:

    标签: macros elixir


    【解决方案1】:

    宏是一种对编程语言进行编程的方法。简单地说,宏是一种生成程序代码的方式,而不是一直自己编写。

    另一方面,语言结构(有时称为“特殊形式”)是长生不老药本身的核心。过度简化可能是 if 的实现不是在 Elixir 中完成的,而是在 Elixir 实现的语言中完成的。

    假设您想使用提到的unless

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

    在 Elixir 中,该语言中没有 unless。 José 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 McCordMetaprogramming Elixir 是最好的。

    【讨论】:

    • 丹吉特。我会澄清的。
    • 另外,宏绝不是常规函数。宏接收 AST 并返回 AST,并在编译阶段进行扩展。宏返回的内容按原样注入,而不是宏调用。编译后的代码中没有宏的痕迹。
    • 另外,你可以提供一个朴素的unless实现的例子(不支持else子句):defmodule U, do: defmacro unless(ast, do: block), do: quote do: if not unquote(ast), do: unquote(block),并将其用作require U; U.unless false, do: 42
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多