我正在学习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
?
答案 0 :(得分:1)
如果您查看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。