如何编写类似于Ecto的field / 2的Elixir宏?

我正在学习Elixir,遇到了这种情况:
我有一个Ecto模式,我想做一个像“ get_by”这样的函数,它带有一个列名,并且它的值是这样的一个参数:get_by(:id,7) 因此,该功能的工作版本如下:

def get_by(column,value) do
  Repo.all(
    from(
      r in __MODULE__,where: field(r,^column) == ^value,)
  )
end

我知道它可以完全正常工作,但是我想知道field宏的工作原理。
原始代码对我来说太难读了。我试图在宏中使用AST,但似乎没有任何效果。我最好的是:

defmacro magic(var,{:^,_,[{column,_}]}) do
  dot = {:.,[],[var,column]}
  {dot,[]}
end

但这将返回r.column而不是绑定到column变量的原子。
应当如何编写宏以返回r.id

xiaoyuwuxin22 回答:如何编写类似于Ecto的field / 2的Elixir宏?

如果您查看Ecto.Query.API.field/2的源代码,则会看到对该函数的显式调用(不是宏),会引发

这是因为只有在Ecto.Query.from/2宏中才有意义。

您想要的东西在某种程度上仍然可以实现;不是带点符号(AFAICT,而是带有Access

defmodule M do
  defmacro magic(a1,{:^,_,[a2]}) do
    quote do: unquote(a1)[unquote(a2)]
  end
end
import M
{r,column} = {%{id: 42},:id}
magic(r,^column)
#⇒ 42

如果没有就地评估等绝对讨厌的技巧,我将无法quote do: unquote(a1).unquote(a2)工作。

为了更好地理解宏,您可能应该自己弄清楚哪里可以使用AST。


我强烈推荐Chris McCord的Metaprogramming Elixir

本文链接:https://www.f2er.com/3093841.html

大家都在问