elixir - 在 Elixir 中用宏中的行为实现协议(protocol)

标签 elixir metaprogramming

我有一个协议(protocol),它具有单一功能和相对复杂的 API:

defprotocol Foo do
  def complex(foo, x, y)
end

我想提供一种方法来为常见且更简单的用例实现此协议(protocol)。经过一些实验,我得出以下结论:

defmodule Bar do
  @callback simple(any, any) :: boolean

  defmacro __using__(_) do
    quote do
      @behaviour Bar

      defimpl Foo do
        # TODO this is really hacky. What is a better way to reference parent module?
        # Note that the 1 in drop(1) is from Foo's module name
        @parent_module __MODULE__ |> Module.split |> Enum.drop(1) |> Module.concat
        def complex(bar, x, _y) do
          matches = @parent_module.simple(bar, x)
          if matches, do: [x], else: []
        end
      end
    end
  end
end

现在我可以通过 use Bar 实现 Foo,但是 @parent_module 的确定和使用方式似乎是错误的。有没有比使用上面 TODO 中的 hack 更好的方法?执行此操作的惯用方法是什么?我宁愿不将 Foo 变成一种行为,因为协议(protocol)的调度和整合符合使用模式。

最佳答案

您可以获取父模块的名称并将其存储在局部变量中,然后将其放入实现中的属性中,然后使用它:

defmodule Bar do
  @callback simple(any, any) :: boolean

  defmacro __using__(_) do
    quote do
      @behaviour Bar

      parent_module = __MODULE__

      defimpl Foo do
        @parent_module parent_module

        def complex(bar, x, _y) do
          matches = @parent_module.simple(bar, x)
          if matches, do: [x], else: []
        end
      end
    end
  end
end

关于elixir - 在 Elixir 中用宏中的行为实现协议(protocol),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43788959/

相关文章:

elixir - 如何从任何链接进程获得任何崩溃通知?

mysql - 在 Ecto/Phoenix 中用微秒断言时间戳等于 mysql 数据库值

c++ - 根据参数返回类型

ruby - 给定一个 Ruby 元类,我如何获取它所附加的实例?

elixir - 你如何扩展/继承一个 Elixir 模块?

elixir - 如何在我的 ExUnit 测试中 stub (或阻止运行)对工作人员的调用?

elixir - 惯用的 Elixir 获取嵌套列表项?

c++ - 当只有少数成员存在差异时,是否有必要对整个类(class)进行特化?

c++ - 一个类可以通过什么方式访问另一个类的成员?

Java动态方法创建