我想在Elixir中定义一个协议,然后有几个模块实现它。我的问题是那些模块只是Agent包装器,所以:
** (Protocol.UndefinedError) protocol Proto not implemented for #PID<0.88.0>
test.exs:1: Proto.impl_for!/1
test.exs:2: Proto.foo/1
(elixir) lib/code.ex:363: Code.require_file/2
然后导致,
{{1}}
有没有办法直接处理?
答案 0 :(得分:2)
要使用此类协议,您必须制作A
和B
结构,并从相应的start_link
函数返回它们。您还需要Agent
周围的包装器才能调用Agent
个函数。我为此创建了一个单独的模块,因为如果你创建更多具有相同结构的结构,可以重复使用它:
defprotocol Proto do
def foo(proto)
end
defmodule A do
defstruct [:pid]
def start_link() do
WrappedAgent.start_link(A, fn -> :a end)
end
end
defimpl Proto, for: A do
def foo(proto) do
WrappedAgent.get(proto, fn a -> {:a, a} end)
end
end
defmodule B do
defstruct [:pid]
def start_link() do
WrappedAgent.start_link(B, fn -> :b end)
end
end
defimpl Proto, for: B do
def foo(proto) do
WrappedAgent.get(proto, fn b -> {:b, b} end)
end
end
defmodule WrappedAgent do
def start_link(module, f) do
with {:ok, pid} <- Agent.start_link(f),
do: {:ok, %{__struct__: module, pid: pid}}
end
def get(%{pid: pid}, f), do: Agent.get(pid, f)
end
{:ok, a} = A.start_link()
IO.inspect Proto.foo(a)
{:ok, b} = B.start_link()
IO.inspect Proto.foo(b)
输出:
{:a, :a}
{:b, :b}
答案 1 :(得分:2)
为了澄清一下,为数据类型实现了协议。您可以为所有标准数据类型和您在模块中定义的任何结构实现协议。
[Atom, Integer, Float, BitString, Regexp, PID, Function, Reference, Port, Tuple, List, Map]
想想“相同的功能,不同的数据类型”。与行为形成鲜明对比,这与您最初的想法更为接近。在那里,您定义了一组函数,这些函数具有相同的arity(并且按照惯例,相同的输入),您在模块中实现这些函数以允许其他模块 以标准方式使用您的模块。