简体   繁体   English

Erlang vs Elixir Macros

[英]Erlang vs Elixir Macros

I have came across some Erlang code which I am trying to convert to Elixir to help me learn both of the languages and understand the differences. 我遇到了一些Erlang代码,我试图将其转换为Elixir,以帮助我学习这两种语言并理解其中的差异。 Macros and metaprogramming in general is a topic I am still trying to get my head around, so hopefully you will understand my confusion. 宏观和元编程一般是我仍然试图解决的话题,所以希望你能理解我的困惑。

The Erlang code Erlang代码

-define(p2(MAT, REP), 
        p2(W = MAT ++ STM) -> m_rep(0, W, STM, REP))

% where m_rep is a function already defined.

To me, it seems that in the above code, there is two separate definitions of the p2 macro that map to a private function called m_rep . 对我来说,似乎在上面的代码中,有两个单独的p2宏定义映射到一个名为m_rep的私有函数。 In Elixir though, it seems that it is only possible to have one pattern matching definition. 但是在Elixir中,似乎只能有一个模式匹配定义。 Is it possible to have different ones in Elixir too? Elixir也有可能有不同的吗?

These are not two definitions. 这些不是两个定义。 The first line is the macro, the second line is the replacement. 第一行是宏,第二行是替换。 The confusing bit is that the macro has the same name as the function for which it is generating clauses. 令人困惑的是,宏与它生成子句的函数同名。 For example when using your macro like this: 例如,当像这样使用宏时:

?p2("a", "b");
?p2("c", "d").

the above will be expanded to: 以上内容将扩展至:

p2(w = "a" ++ stm) -> m_rep(0, w, stm, "b");
p2(w = "c" ++ stm) -> m_rep(0, w, stm, "d").

You can use erlc -P to produce a .P file that will show you the effects of macro expansion on your code. 您可以使用erlc -P生成.P文件,该文件将向您显示宏扩展对代码的影响。 Check out this slightly simpler, compilable example: 看看这个稍微简单,可编辑的例子:

-module(macro).
-export([foo/1]).

-define(foo(X),
        foo(X) -> X).

?foo("bar");
?foo("baz");
?foo("qux").

Using erlc -P macro.erl you will get the following output to macro.P : 使用erlc -P macro.erl您将获得以下输出到macro.P

-file("macro.erl", 1).

-module(macro).

-export([foo/1]).

foo("bar") ->
    "bar";
foo("baz") ->
    "baz";
foo("qux") ->
    "qux".

In Elixir you can define multiple function clauses using macros as well. 在Elixir中,您也可以使用宏定义多个函数子句。 It is more verbose, but I think it is also much clearer. 它更冗长,但我认为它也更清晰。 The Elixir equivalent would be: Elixir等价物将是:

defmodule MyMacros do
  defmacro p2(mat, rep) do
    quote do
      def p2(w = unquote(mat) ++ stm) do
        m_rep(0, w, stm, unquote(rep))
      end
    end
  end
end

which you can use to define multiple function clauses, just like the erlang counterpart: 您可以使用它来定义多个函数子句,就像erlang对应的一样:

defmodule MyModule do
  require MyMacros

  MyMacros.p2('a', 'b')
  MyMacros.p2('c', 'd')
end

I can't help myself here. 我在这里无法自拔。 :-) If it's the macros you are after then using LFE (Lisp Flavoured Erlang) gives you much better macro handling than either erlang or elixir. :-)如果它是你的宏然后使用后LFE(Lisp的香料二郎),为您提供更好的宏观操控比任何二郎或酏剂。 It also is compatible with both. 它也兼容两者。

-define(p2(MAT, REP), 
        p2(w = MAT ++ stm) -> m_rep(0, w, stm, REP))

% where m_rep is a function already defined.

The code above has a number of issues. 上面的代码有很多问题。

There's no such thing as a macro with multiple clauses in Erlang. 在Erlang中没有带有多个子句的宏。 The above code doesn't define two separate definitions of the p2 macro that map to a private function called m_rep . 上面的代码没有定义映射到名为m_rep的私有函数的p2宏的两个单独定义 What it does is it defines a 2-argument macro, which defines a p2 function taking some parameters and calling m_rep . 它的作用是定义一个2参数宏,它定义了一个带有一些参数并调用m_repp2函数。 However, the parameter definition of the internal p2 function is incorrect: 但是,内部p2函数的参数定义不正确:

  • it tries to use ++ with the second argument not being a list 它尝试使用++ ,第二个参数不是列表
  • it tries to assign a value to an atom (did you mean a capital W , a variable, instead of a small w , an atom?) 它试图给一个原子赋值(你的意思是一个大写的W ,一个变量,而不是一个小的w ,一个原子吗?)
  • it tries the assignment in a place where an assignment is not allowed - in a function head. 它在一个不允许赋值的地方尝试赋值 - 在函数头中。

Did you try to test for equality ( == instead of = ), not to do an assignment? 您是否尝试测试相等性( ==而不是= ),而不是进行分配? If so, you have to use a guard. 如果是这样,你必须使用警卫。

Moreover, it seems to me you're trying to use w and stm as though they were variables and pass them to m_rep , but they're not! 而且,在我看来,你试图使用wstm ,好像它们是变量并将它们传递给m_rep ,但它们不是! Variables in Erlang have to start with a capital letter. Erlang中的变量必须以大写字母开头。 Variables in Elixir, on the other hand, do not. 另一方面,Elixir中的变量则没有。 It might be you're confusing concepts from the two similar but still different languages. 你可能会混淆两种相似但仍然不同的语言的概念。

My general advice would be to pick one language and learn it well and only later with that knowledge under your belt try a different language. 我的一般建议是选择一种语言并对其进行良好的学习,然后才能用这种知识尝试不同的语言。 Pick Erlang if you're completely new to programming - it's simpler, there are less things to learn upfront. 如果您对编程完全陌生,请选择Erlang - 它更简单,预先学习的东西较少。 Pick Elixir if you already know Ruby or are more into immediate marketability of your skills. 如果您已经了解Ruby,请选择Elixir,或者更多地了解您的技能。

Please say more about your intention and I might be able to come up with code expressing it. 请详细说明您的意图,我也许能够提出表达它的代码。 The above snippet is too ambiguous. 上面的代码片段太模糊了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM