是否可以通过“use/2”宏(用于 DRY)扩展 Elixir 协议?
Is it possible to extend an Elixir protocol via `use/2` macro (for DRY)?
我有一个项目,我需要定义一些名称不同但定义完全相同的协议。
我试过用 use/2
来做,但没有成功:它不起作用,来自 __using__/1
的定义没有出现在生成的协议中:
defmodule Action do
defmacro __using__(_) do
quote do
def run(tool)
end
end
end
defprotocol Actions.ShowId do
use Action
end
defmodule Tools.SimpleRelay do
defstruct [:id]
defimpl Actions.ShowId do
def run(%Tools.SimpleRelay{id: id}), do: IO.puts(inspect(id))
end
end
在具有协议实现定义的结构上,我得到:
warning: module Actions.ShowId is not a behaviour (in module Actions.ShowId.Tools.SimpleRelay)
iex:8: Actions.ShowId.Tools.SimpleRelay (module)
如果我尝试使用它:
iex(6)> relay = struct(Tools.SimpleRelay, id: "ALongStringId")
%Tools.SimpleRelay{id: "ALongStringId"}
iex(7)> Actions.ShowId.run(relay)
** (UndefinedFunctionError) function Actions.ShowId.run/1 is undefined or private
Actions.ShowId.run(%Tools.SimpleRelay{id: "ALongStringId"})
这是错误还是目的?还是我的定义不正确?
我试图在 elixirforum 上问同样的问题(并在那里发布了这个任务背景),到目前为止我得到的唯一建议是这是一个错误。在开issue之前,我想澄清一下这确实是一个bug。
谢谢!
Kernel.defprotocol/2
delegates to Protocol.__protocol__/2
which in turn redefines def/2
通过取消导出 Kernel.def/2
并改为导入 Protocol.def/1
。在您的宏的上下文中,def
宏指的是导入的 ATM Kernel.def/2
,这会破坏所需的行为。
我不确定这是否是一个错误(尽管错误消息可能更具描述性),但您可以通过显式调用 Protocol.def/1
[=33= 轻松克服它], 使用 FQN.
defmodule Action do
defmacro __using__(_) do
quote do
# ⇓⇓⇓⇓⇓⇓⇓⇓ HERE
Protocol.def(run(tool))
end
end
end
defprotocol Actions.ShowId do
use Action
end
defmodule Tools.SimpleRelay do
defstruct [:id]
defimpl Actions.ShowId do
def run(%Tools.SimpleRelay{id: id}), do: IO.puts(inspect(id))
end
end
如果您不喜欢格式化程序如何处理 Protocol.def/1
周围的括号,请随时将其委托给某些本地 defp/1
或 和 修改您的 [=22] =] 和
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
locals_without_parens: [:defp]
]
我有一个项目,我需要定义一些名称不同但定义完全相同的协议。
我试过用 use/2
来做,但没有成功:它不起作用,来自 __using__/1
的定义没有出现在生成的协议中:
defmodule Action do
defmacro __using__(_) do
quote do
def run(tool)
end
end
end
defprotocol Actions.ShowId do
use Action
end
defmodule Tools.SimpleRelay do
defstruct [:id]
defimpl Actions.ShowId do
def run(%Tools.SimpleRelay{id: id}), do: IO.puts(inspect(id))
end
end
在具有协议实现定义的结构上,我得到:
warning: module Actions.ShowId is not a behaviour (in module Actions.ShowId.Tools.SimpleRelay)
iex:8: Actions.ShowId.Tools.SimpleRelay (module)
如果我尝试使用它:
iex(6)> relay = struct(Tools.SimpleRelay, id: "ALongStringId")
%Tools.SimpleRelay{id: "ALongStringId"}
iex(7)> Actions.ShowId.run(relay)
** (UndefinedFunctionError) function Actions.ShowId.run/1 is undefined or private
Actions.ShowId.run(%Tools.SimpleRelay{id: "ALongStringId"})
这是错误还是目的?还是我的定义不正确?
我试图在 elixirforum 上问同样的问题(并在那里发布了这个任务背景),到目前为止我得到的唯一建议是这是一个错误。在开issue之前,我想澄清一下这确实是一个bug。
谢谢!
Kernel.defprotocol/2
delegates to Protocol.__protocol__/2
which in turn redefines def/2
通过取消导出 Kernel.def/2
并改为导入 Protocol.def/1
。在您的宏的上下文中,def
宏指的是导入的 ATM Kernel.def/2
,这会破坏所需的行为。
我不确定这是否是一个错误(尽管错误消息可能更具描述性),但您可以通过显式调用 Protocol.def/1
[=33= 轻松克服它], 使用 FQN.
defmodule Action do
defmacro __using__(_) do
quote do
# ⇓⇓⇓⇓⇓⇓⇓⇓ HERE
Protocol.def(run(tool))
end
end
end
defprotocol Actions.ShowId do
use Action
end
defmodule Tools.SimpleRelay do
defstruct [:id]
defimpl Actions.ShowId do
def run(%Tools.SimpleRelay{id: id}), do: IO.puts(inspect(id))
end
end
如果您不喜欢格式化程序如何处理 Protocol.def/1
周围的括号,请随时将其委托给某些本地 defp/1
或 和 修改您的 [=22] =] 和
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
locals_without_parens: [:defp]
]